public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) { var eventStoreSettings = new EventStoreSettings(); configuration.GetSection(nameof(EventStoreSettings)).Bind(eventStoreSettings); services.Configure <EventStoreSettings>(configuration.GetSection(nameof(EventStoreSettings))); var eventStoreConnection = EventStoreConnection.Create( connectionString: eventStoreSettings.ConnectionString, builder: ConnectionSettings.Create().KeepReconnecting(), connectionName: eventStoreSettings.ConnectionName); eventStoreConnection.ConnectAsync().GetAwaiter().GetResult(); services.AddSingleton(eventStoreConnection); var couchbaseSettings = new CouchbaseSettings(); configuration.GetSection(nameof(CouchbaseSettings)).Bind(couchbaseSettings); services.Configure <CouchbaseSettings>(configuration.GetSection(nameof(CouchbaseSettings))); services.AddCouchbase((opt) => { opt.ConnectionString = couchbaseSettings.ConnectionStrings; opt.Username = couchbaseSettings.UserName; opt.Password = couchbaseSettings.Password; }); services.AddTransient(typeof(IGenericRepository <>), typeof(GenericRepository <>)); return(services); }
/// <summary> /// Implements the service as a <see cref="Task"/>. /// </summary> /// <returns>The <see cref="Task"/>.</returns> private static async Task RunAsync() { try { var settings = new CouchbaseSettings() { Servers = new List <Uri>() { new Uri("couchbase://10.0.0.90"), new Uri("couchbase://10.0.0.91"), new Uri("couchbase://10.0.0.92") }, Bucket = "stoke" }; var credentials = new Credentials() { Username = Environment.GetEnvironmentVariable("TS_COUCHBASE_USERNAME"), Password = Environment.GetEnvironmentVariable("TS_COUCHBASE_PASSWORD") }; using (var bucket = settings.OpenBucket(credentials)) { var retry = new ExponentialRetryPolicy(CouchbaseTransientDetector.IsTransient); for (int i = 0; i < 500000; i++) { var key = bucket.GenKey(); await retry.InvokeAsync(async() => await bucket.InsertSafeAsync(key, new Document <Item>() { Id = key, Content = new Item() { Name = "Jeff", Age = 56 } })); var exists = await bucket.ExistsAsync(key); var result2 = await bucket.GetAsync <Document <Item> >(key); result2.EnsureSuccess(); } } } catch (OperationCanceledException) { return; } catch (Exception e) { log.LogError(e); } finally { terminator.ReadyToExit(); } }
/// <summary> /// Returns a Couchbase bucket connection using specified settings and a Docker secret. /// </summary> /// <param name="settings">The Couchbase settings.</param> /// <param name="secretName">The Docker secret name.</param> /// <returns>The connected <see cref="IBucket"/>.</returns> public static IBucket ConnectBucket(this CouchbaseSettings settings, string secretName) { Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(secretName)); var credentials = NeonHelper.JsonDeserialize <Credentials>(HiveHelper.GetSecret(secretName)); return(global::Couchbase.CouchbaseExtensions.OpenBucket(settings, credentials)); }
/// <summary> /// Returns a Couchbase bucket connection using specified settings and credentials. /// </summary> /// <param name="settings">The Couchbase settings.</param> /// <param name="credentials">The credentials.</param> /// <param name="timeout">The optional timeout (defaults to 30 seconds).</param> /// <param name="ignoreDurability">Optionally configure the bucket to ignore durability parameters.</param> /// <returns>The connected <see cref="NeonBucket"/>.</returns> /// <remarks> /// <para> /// You may explicitly pass <paramref name="ignoreDurability"/><c>=true</c> for /// development and test environments where there may not be enough cluster nodes to /// satisfy durability constraints. If this is <c>null</c> (the default) then the bucket /// will look for the presence of the <b>DEV_WORKSTATION</b> environment variable /// and ignore durability constraints if this variable exists, otherwise durability /// constraints will be honored. /// </para> /// </remarks> public static NeonBucket OpenBucket(this CouchbaseSettings settings, Credentials credentials, TimeSpan timeout = default, bool?ignoreDurability = null) { Covenant.Requires <ArgumentNullException>(credentials != null); return(settings.OpenBucket(credentials.Username, credentials.Password, timeout: timeout, ignoreDurability: ignoreDurability)); }
/// <summary> /// Actually starts Couchbase within the initialization <see cref="Action"/>. You'll /// generally want to use <see cref="Start(CouchbaseSettings, string, string, string[], string, string, bool)"/> /// but this method is used internally or for special situations. /// </summary> /// <param name="settings">Optional Couchbase settings.</param> /// <param name="image">Optionally specifies the Couchbase container image (defaults to <b>nhive/couchbase-test:latest</b>).</param> /// <param name="name">Optionally specifies the Couchbase container name (defaults to <c>cb-test</c>).</param> /// <param name="env">Optional environment variables to be passed to the Couchbase container, formatted as <b>NAME=VALUE</b> or just <b>NAME</b>.</param> /// <param name="username">Optional Couchbase username (defaults to <b>Administrator</b>).</param> /// <param name="password">Optional Couchbase password (defaults to <b>password</b>).</param> /// <param name="noPrimary">Optionally disable creation of thea primary bucket index.</param> /// <returns> /// <c>true</c> if the fixture wasn't previously initialized and /// this method call initialized it or <c>false</c> if the fixture /// was already initialized. /// </returns> public void StartInAction( CouchbaseSettings settings = null, string image = "nhive/couchbase-test:latest", string name = "cb-test", string[] env = null, string username = "******", string password = "******", bool noPrimary = false) { Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(image)); base.CheckWithinAction(); if (IsInitialized) { return; } RunContainer(name, image, new string[] { "--detach", "--mount", "type=volume,target=/opt/couchbase/var", "-p", "4369:4369", "-p", "8091-8096:8091-8096", "-p", "9100-9105:9100-9105", "-p", "9110-9118:9110-9118", "-p", "9120-9122:9120-9122", "-p", "9999:9999", "-p", "11207:11207", "-p", "11209-11211:11209-11211", "-p", "18091-18096:18091-18096", "-p", "21100-21299:21100-21299" }, env: env); Thread.Sleep(warmupDelay); settings = settings ?? new CouchbaseSettings(); settings.Servers.Clear(); settings.Servers.Add(new Uri("http://localhost:8091")); if (settings.Bucket == null) { settings.Bucket = "test"; } Bucket = null; Settings = settings; Username = username; Password = password; ConnectBucket(); }
/// <summary> /// Returns a Couchbase cluster connection using specified settings and <see cref="Credentials"/>. /// </summary> /// <param name="settings">The Couchbase settings.</param> /// <param name="credentials">Cluster credentials.</param> /// <returns>The connected <see cref="Cluster"/>.</returns> public static Cluster OpenCluster(this CouchbaseSettings settings, Credentials credentials) { Covenant.Requires <ArgumentNullException>(settings.Servers != null && settings.Servers.Count > 0); Covenant.Requires <ArgumentNullException>(credentials != null); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(credentials.Username)); Covenant.Requires <ArgumentNullException>(credentials.Password != null); var config = settings.ToClientConfig(); var cluster = new Cluster(config); cluster.Authenticate(credentials.Username, credentials.Password); return(cluster); }
public CouchbaseRepositoryBase() { _couchbaseSettings = ServiceTool.ServiceProvider.GetService <IConfiguration>().GetSection("CouchbaseSettings").Get <CouchbaseSettings>(); _cluster = new Cluster(new ClientConfiguration { Servers = new List <Uri> { new Uri(_couchbaseSettings.Host) } }); _cluster.Authenticate(_couchbaseSettings.Username, _couchbaseSettings.Password); _bucket = _cluster.OpenBucket("todo"); _context = new BucketContext(_bucket); }
/// <summary> /// <para> /// Starts a Couchbase container if it's not already running. You'll generally want /// to call this in your test class constructor instead of <see cref="ITestFixture.Start(Action)"/>. /// </para> /// <note> /// You'll need to call <see cref="StartAsComposed(CouchbaseSettings, string, string, string[], string, string, bool)"/> /// instead when this fixture is being added to a <see cref="ComposedFixture"/>. /// </note> /// </summary> /// <param name="settings">Optional Couchbase settings.</param> /// <param name="image"> /// Optionally specifies the Couchbase container image. This defaults to /// <b>nkubeio/couchbase-test:latest</b> or <b>nkubedev/couchbase-test:latest</b> /// depending on whether the assembly was built from a git release branch or not. /// </param> /// <param name="name">Optionally specifies the Couchbase container name (defaults to <c>cb-test</c>).</param> /// <param name="env">Optional environment variables to be passed to the Couchbase container, formatted as <b>NAME=VALUE</b> or just <b>NAME</b>.</param> /// <param name="username">Optional Couchbase username (defaults to <b>Administrator</b>).</param> /// <param name="password">Optional Couchbase password (defaults to <b>password</b>).</param> /// <param name="noPrimary">Optionally disable creation of the primary bucket index.</param> /// <returns> /// <see cref="TestFixtureStatus.Started"/> if the fixture wasn't previously started and /// this method call started it or <see cref="TestFixtureStatus.AlreadyRunning"/> if the /// fixture was already running. /// </returns> /// <remarks> /// <note> /// Some of the <paramref name="settings"/> properties will be ignored including /// <see cref="CouchbaseSettings.Servers"/>. This will be replaced by the local /// endpoint for the Couchbase container. Also, the fixture will connect to the /// <b>test</b> bucket by default (unless another is specified). /// </note> /// <para> /// This method creates a primary index by default because it's very common for /// unit tests to require a primary index. You can avoid creating a primary index /// by passing <paramref name="noPrimary"/><c>=true</c>. /// </para> /// <para> /// There are three basic patterns for using this fixture. /// </para> /// <list type="table"> /// <item> /// <term><b>initialize once</b></term> /// <description> /// <para> /// The basic idea here is to have your test class initialize Couchbase /// once within the test class constructor inside of the initialize action /// with common state that all of the tests can access. /// </para> /// <para> /// This will be quite a bit faster than reconfiguring Couchbase at the /// beginning of every test and can work well for many situations. /// </para> /// </description> /// </item> /// <item> /// <term><b>initialize every test</b></term> /// <description> /// For scenarios where Couchbase must be cleared before every test, /// you can use the <see cref="Clear()"/> method to reset its /// state within each test method, populate the database as necessary, /// and then perform your tests. /// </description> /// </item> /// <item> /// <term><b>docker integrated</b></term> /// <description> /// The <see cref="CouchbaseFixture"/> can also be added to the <see cref="DockerFixture"/> /// and used within a swarm. This is useful for testing multiple services and /// also has the advantage of ensure that swarm/node state is fully reset /// before the database container is started. /// </description> /// </item> /// </list> /// </remarks> public TestFixtureStatus Start( CouchbaseSettings settings = null, string image = null, string name = "cb-test", string[] env = null, string username = "******", string password = "******", bool noPrimary = false) { return(base.Start( () => { StartAsComposed(settings, image, name, env, username, password, noPrimary); })); }
/// <summary> /// Returns a Couchbase cluster connection using specified settings and the username and password. /// </summary> /// <param name="settings">The Couchbase settings.</param> /// <param name="username">The cluster username.</param> /// <param name="password">The cluster password.</param> /// <returns>The connected <see cref="Cluster"/>.</returns> public static Cluster OpenCluster(this CouchbaseSettings settings, string username, string password) { Covenant.Requires <ArgumentNullException>(settings.Servers != null && settings.Servers.Count > 0); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(username)); Covenant.Requires <ArgumentNullException>(password != null); var config = settings.ToClientConfig(); var cluster = new Cluster(config); if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) { cluster.Authenticate(new Authentication.PasswordAuthenticator(username, password)); } return(cluster); }
/// <summary> /// <para> /// Starts a Couchbase container if it's not already running. You'll generally want /// to call this in your test class constructor instead of <see cref="ITestFixture.Start(Action)"/>. /// </para> /// <note> /// You'll need to call <see cref="StartAsComposed(CouchbaseSettings, string, string, string[], string, string, bool, string, ContainerLimits)"/> /// instead when this fixture is being added to a <see cref="ComposedFixture"/>. /// </note> /// </summary> /// <param name="settings">Optional Couchbase settings.</param> /// <param name="image"> /// Optionally specifies the Couchbase container image. This defaults to /// <b>ghcr.io/neonrelease/couchbase-dev:latest</b> or <b>ghcr.io/neonrelease-dev/couchbase-dev:latest</b> /// depending on whether the assembly was built from a git release branch or not. /// </param> /// <param name="name">Optionally specifies the Couchbase container name (defaults to <c>cb-test</c>).</param> /// <param name="env">Optional environment variables to be passed to the Couchbase container, formatted as <b>NAME=VALUE</b> or just <b>NAME</b>.</param> /// <param name="username">Optional Couchbase username (defaults to <b>Administrator</b>).</param> /// <param name="password">Optional Couchbase password (defaults to <b>password</b>).</param> /// <param name="noPrimary">Optionally disable creation of the primary bucket index.</param> /// <param name="hostInterface"> /// Optionally specifies the host interface where the container public ports will be /// published. This defaults to <see cref="ContainerFixture.DefaultHostInterface"/> /// but may be customized. This needs to be an IPv4 address. /// </param> /// <param name="limits"> /// Optionally specifies the Docker container limits to use for hosting Couchbase. Note that /// this method will use reasonable default limits when this is <c>null</c>. /// </param> /// <returns> /// <see cref="TestFixtureStatus.Started"/> if the fixture wasn't previously started and /// this method call started it or <see cref="TestFixtureStatus.AlreadyRunning"/> if the /// fixture was already running. /// </returns> /// <remarks> /// <note> /// Some of the <paramref name="settings"/> properties will be ignored including /// <see cref="CouchbaseSettings.Servers"/>. This will be replaced by the local /// endpoint for the Couchbase container. Also, the fixture will connect to the /// <b>test</b> bucket by default (unless another is specified). /// </note> /// <para> /// This method creates a primary index by default because it's very common for /// unit tests to require a primary index. You can avoid creating a primary index /// by passing <paramref name="noPrimary"/><c>=true</c>. /// </para> /// <para> /// There are three basic patterns for using this fixture. /// </para> /// <list type="table"> /// <item> /// <term><b>initialize once</b></term> /// <description> /// <para> /// The basic idea here is to have your test class initialize Couchbase /// once within the test class constructor inside of the initialize action /// with common state that all of the tests can access. /// </para> /// <para> /// This will be quite a bit faster than reconfiguring Couchbase at the /// beginning of every test and can work well for many situations. /// </para> /// </description> /// </item> /// <item> /// <term><b>initialize every test</b></term> /// <description> /// For scenarios where Couchbase must be cleared before every test, /// you can use the <see cref="Clear()"/> method to reset its /// state within each test method, populate the database as necessary, /// and then perform your tests. /// </description> /// </item> /// <item> /// <term><b>docker integrated</b></term> /// <description> /// The <see cref="CouchbaseFixture"/> can also be added to the <see cref="DockerFixture"/> /// and used within a swarm. This is useful for testing multiple services and /// also has the advantage of ensure that swarm/node state is fully reset /// before the database container is started. /// </description> /// </item> /// </list> /// </remarks> public TestFixtureStatus Start( CouchbaseSettings settings = null, string image = null, string name = "cb-test", string[] env = null, string username = "******", string password = "******", bool noPrimary = false, string hostInterface = null, ContainerLimits limits = null) { return(base.Start( () => { StartAsComposed(settings, image, name, env, username, password, noPrimary, hostInterface, limits); })); }
/// <summary> /// Converts a <see cref="CouchbaseSettings"/> into a <see cref="ClientConfiguration"/>. /// </summary> /// <param name="settings">The simplified Couchbase settings instance.</param> /// <returns>A Couchbase <see cref="ClientConfiguration"/>.</returns> public static ClientConfiguration ToClientConfig(this CouchbaseSettings settings) { var config = new ClientConfiguration(); config.Servers.Clear(); foreach (var uri in settings.Servers) { config.Servers.Add(uri); } config.UseSsl = false; config.Serializer = () => new DefaultSerializer(NeonHelper.JsonRelaxedSerializerSettings.Value, NeonHelper.JsonRelaxedSerializerSettings.Value); config.DefaultOperationLifespan = (uint)settings.OperationTimeout; config.DefaultConnectionLimit = settings.MaxPoolConnections; return(config); }
/// <summary> /// Starts a Couchbase container if it's not already running. You'll generally want /// to call this in your test class constructor instead of <see cref="ITestFixture.Initialize(Action)"/>. /// </summary> /// <param name="settings">Optional Couchbase settings.</param> /// <param name="image">Optionally specifies the Couchbase container image (defaults to <b>nhive/couchbase-test:latest</b>).</param> /// <param name="name">Optionally specifies the Couchbase container name (defaults to <c>cb-test</c>).</param> /// <param name="env">Optional environment variables to be passed to the Couchbase container, formatted as <b>NAME=VALUE</b> or just <b>NAME</b>.</param> /// <param name="username">Optional Couchbase username (defaults to <b>Administrator</b>).</param> /// <param name="password">Optional Couchbase password (defaults to <b>password</b>).</param> /// <param name="noPrimary">Optionally disable creation of the primary bucket index.</param> /// <returns> /// <c>true</c> if the fixture wasn't previously initialized and /// this method call initialized it or <c>false</c> if the fixture /// was already initialized. /// </returns> /// <remarks> /// <note> /// Some of the <paramref name="settings"/> properties will be ignored including /// <see cref="CouchbaseSettings.Servers"/>. This will be replaced by the local /// endpoint for the Couchbase container. Also, the fixture will connect to the /// <b>test</b> bucket by default (unless another is specified). /// </note> /// <para> /// This method creates a primary index by default because it's very common for /// unit tests to require a primary index. You can avoid creating a primary index /// by passing <paramref name="noPrimary"/><c>=true</c>. /// </para> /// <para> /// There are three basic patterns for using this fixture. /// </para> /// <list type="table"> /// <item> /// <term><b>initialize once</b></term> /// <description> /// <para> /// The basic idea here is to have your test class initialize Couchbase /// once within the test class constructor inside of the initialize action /// with common state that all of the tests can access. /// </para> /// <para> /// This will be quite a bit faster than reconfiguring Couchbase at the /// beginning of every test and can work well for many situations. /// </para> /// </description> /// </item> /// <item> /// <term><b>initialize every test</b></term> /// <description> /// For scenarios where Couchbase must be cleared before every test, /// you can use the <see cref="Clear()"/> method to reset its /// state within each test method, populate the database as necessary, /// and then perform your tests. /// </description> /// </item> /// <item> /// <term><b>docker integrated</b></term> /// <description> /// The <see cref="CouchbaseFixture"/> can also be added to the <see cref="DockerFixture"/> /// and used within a swarm. This is useful for testing multiple services and /// also has the advantage of ensure that swarm/node state is fully reset /// before the database container is started. /// </description> /// </item> /// </list> /// </remarks> public bool Start( CouchbaseSettings settings = null, string image = "nhive/couchbase-test:latest", string name = "cb-test", string[] env = null, string username = "******", string password = "******", bool noPrimary = false) { Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(image)); createPrimaryIndex = !noPrimary; return(base.Initialize( () => { StartInAction(settings, image, name, env, username, password, noPrimary); })); }
//--------------------------------------------------------------------- // Instance members /// <summary> /// Constructor. /// </summary> public CouchbaseArgs() { Settings = new CouchbaseSettings(); Credentials = new Credentials(); }
/// <inheritdoc/> public override void Run(CommandLine commandLine) { if (commandLine.HasHelpOption || commandLine.Arguments.Length == 0) { Console.WriteLine(usage); Program.Exit(0); } if (commandLine.Arguments.Length == 0) { Console.Error.WriteLine("*** ERROR: Expected a TARGET argument like: [couchbase://HOST@USER:PASSWORD:BUCKET] or [http(s)://HOST:PORT@USER:PASSWORD:BUCKET]"); Program.Exit(1); } // Open a Couchbase bucket for the TARGET. // // http(s)://HOST:PORT@USER:PASSWORD:BUCKET // couchbase://HOST@USER:PASSWORD:BUCKET var target = commandLine.Arguments[0]; var error = $"*** ERROR: [{target}] is not a valid Couchbase target. Expected: [couchbase://HOST@USER:PASSWORD:BUCKET] or [http(s)://HOST:PORT@USER:PASSWORD:BUCKET]"; var fields = target.Split(new char[] { '@' }, 2); if (fields.Length != 2) { Console.WriteLine(error); Program.Exit(1); } var uri = fields[0]; fields = fields[1].Split(':'); if (fields.Length != 3) { Console.WriteLine(error); Program.Exit(1); } var username = fields[0]; var password = fields[1]; var bucketName = fields[2]; var config = new CouchbaseSettings(); config.Servers.Clear(); config.Servers.Add(new Uri(uri)); config.Bucket = bucketName; using (var bucket = config.OpenBucket(username, password)) { commandLine = commandLine.Shift(1); // Get the N1QL query. if (commandLine.Arguments.Length != 1) { Console.Error.WriteLine("*** ERROR: QUERY argument expected."); Program.Exit(1); } var query = commandLine.Arguments[0]; if (query == "-") { // Read the query from STDIN. query = NeonHelper.ReadStandardInputText(); } else if (query.StartsWith("@")) { // Read the query from the file. query = File.ReadAllText(query.Substring(1)); } var queryResults = bucket.Query <JToken>(query); Console.WriteLine(JsonConvert.SerializeObject(queryResults, Formatting.Indented)); } Program.Exit(0); }
/// <summary> /// Returns a Couchbase bucket connection using specified settings and the cluster credentials. /// </summary> /// <param name="settings">The Couchbase settings.</param> /// <param name="username">The username.</param> /// <param name="password">The password.</param> /// <param name="timeout">The optional timeout (defaults to 60 seconds).</param> /// <param name="ignoreDurability">Optionally configure the bucket to ignore durability parameters.</param> /// <returns>The connected <see cref="NeonBucket"/>.</returns> /// <exception cref="TimeoutException">Thrown if the bucket is not ready after waiting <paramref name="timeout"/>.</exception> /// <remarks> /// <para> /// You may explicitly pass <paramref name="ignoreDurability"/><c>=true</c> for /// development and test environments where there may not be enough cluster nodes to /// satisfy durability constraints. If this is <c>null</c> (the default) then the bucket /// will look for the presence of the <b>DEV_WORKSTATION</b> environment variable /// and ignore durability constraints if this variable exists, otherwise durability /// constraints will be honored. /// </para> /// </remarks> public static NeonBucket OpenBucket(this CouchbaseSettings settings, string username, string password, TimeSpan timeout = default(TimeSpan), bool?ignoreDurability = null) { var bucketConfig = new BucketConfiguration() { BucketName = settings.Bucket, UseEnhancedDurability = settings.UseEnhancedDurability, PoolConfiguration = new PoolConfiguration() { ConnectTimeout = settings.ConnectTimeout, SendTimeout = settings.SendTimeout, MaxSize = settings.MaxPoolConnections, MinSize = settings.MinPoolConnections } }; bucketConfig.PoolConfiguration.ClientConfiguration = new ClientConfiguration() { QueryRequestTimeout = (uint)settings.QueryRequestTimeout, ViewRequestTimeout = settings.ViewRequestTimeout, Serializer = () => new EntitySerializer() }; var config = settings.ToClientConfig(); config.BucketConfigs.Clear(); config.BucketConfigs.Add(settings.Bucket, bucketConfig); var cluster = new Cluster(config); // We have to wait for three Couchbase operations to complete // successfully: // // 1. Authenticate // 2. Open Bucket // 3. List Indexes // // Each of these can fail if Couchbase isn't ready. The Open Bucket // can fail after the Authenticate succeeded because the bucket is // still warming up in the cluster. if (timeout <= TimeSpan.Zero) { timeout = NeonBucket.ReadyTimeout; } //----------------------------------------------------------------- // Authenticate against the Couchbase cluster. var stopwatch = new Stopwatch(); stopwatch.Start(); NeonHelper.WaitFor( () => { try { cluster.Authenticate(username, password); return(true); } catch { return(false); } }, timeout: timeout, pollTime: TimeSpan.FromSeconds(0.5)); //----------------------------------------------------------------- // Open the bucket. Note that we're going to recreate the bucket after // each failed attempt because it doesn't seem to recover from connection // failures. // // I believe this may be due to the index and or query services not being // ready yet after Couchbase has just been spun up and the bucket isn't // notified when these become ready, even after some time passes. timeout = timeout - stopwatch.Elapsed; // Adjust the timeout downward by the time taken to authenticate. stopwatch.Restart(); var bucket = (NeonBucket)null; NeonHelper.WaitFor( () => { try { bucket = new NeonBucket(cluster.OpenBucket(settings.Bucket), settings, ignoreDurability); return(true); } catch { return(false); } }, timeout: timeout, pollTime: TimeSpan.FromSeconds(0.5)); //----------------------------------------------------------------- // Wait until the bucket can perform a simple read operation without // failing. It appears that we can see early failures for Couchbase // clusters that have just been created (e.g. during unit tests). timeout = timeout - stopwatch.Elapsed; // Adjust the timeout downward by the time taken to connect. stopwatch.Restart(); bucket.WaitUntilReadyAsync(timeout).Wait(); return(bucket); }
/// <summary> /// Used to start the fixture within a <see cref="ComposedFixture"/>. /// </summary> /// <param name="settings">Optional Couchbase settings.</param> /// <param name="image"> /// Optionally specifies the Couchbase container image. This defaults to /// <b>nkubeio/couchbase-dev:latest</b> or <b>nkubedev/couchbase-dev:latest</b> /// depending on whether the assembly was built from a git release branch or not. /// </param> /// <param name="name">Optionally specifies the Couchbase container name (defaults to <c>cb-test</c>).</param> /// <param name="env">Optional environment variables to be passed to the Couchbase container, formatted as <b>NAME=VALUE</b> or just <b>NAME</b>.</param> /// <param name="username">Optional Couchbase username (defaults to <b>Administrator</b>).</param> /// <param name="password">Optional Couchbase password (defaults to <b>password</b>).</param> /// <param name="noPrimary">Optionally disable creation of thea primary bucket index.</param> /// <param name="hostInterface"> /// Optionally specifies the host interface where the container public ports will be /// published. This defaults to <see cref="ContainerFixture.DefaultHostInterface"/> /// but may be customized. This needs to be an IPv4 address. /// </param> public void StartAsComposed( CouchbaseSettings settings = null, string image = null, string name = "cb-test", string[] env = null, string username = "******", string password = "******", bool noPrimary = false, string hostInterface = null) { image = image ?? $"{KubeConst.NeonBranchRegistry}/couchbase-dev:latest"; base.CheckWithinAction(); createPrimaryIndex = !noPrimary; if (!IsRunning) { StartAsComposed(name, image, new string[] { "--detach", "-p", $"{GetHostInterface(hostInterface)}:4369:4369", "-p", $"{GetHostInterface(hostInterface)}:8091-8096:8091-8096", "-p", $"{GetHostInterface(hostInterface)}:9100-9105:9100-9105", "-p", $"{GetHostInterface(hostInterface)}:9110-9118:9110-9118", "-p", $"{GetHostInterface(hostInterface)}:9120-9122:9120-9122", "-p", $"{GetHostInterface(hostInterface)}:9999:9999", "-p", $"{GetHostInterface(hostInterface)}:11207:11207", "-p", $"{GetHostInterface(hostInterface)}:11209-11211:11209-11211", "-p", $"{GetHostInterface(hostInterface)}:18091-18096:18091-18096", "-p", $"{GetHostInterface(hostInterface)}:21100-21299:21100-21299" }, env: env); Thread.Sleep(warmupDelay); settings = settings ?? new CouchbaseSettings(); settings.Servers.Clear(); settings.Servers.Add(new Uri($"http://{GetHostInterface(hostInterface, forConnection: true)}:8091")); if (settings.Bucket == null) { settings.Bucket = "test"; } Bucket = null; Settings = settings; Username = username; Password = password; jsonClient = new JsonClient(); jsonClient.BaseAddress = new Uri($"http://{GetHostInterface(hostInterface, forConnection: true)}:8094"); jsonClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Basic", Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{username}:{password}"))); } ConnectBucket(); }
/// <inheritdoc/> public override void Run(CommandLine commandLine) { if (commandLine.HasHelpOption || commandLine.Arguments.Length == 0) { Console.WriteLine(usage); Program.Exit(0); } Program.ConnectHive(); // Process the command arguments. var command = commandLine.Arguments[0]; commandLine = commandLine.Shift(1); if (commandLine.Arguments.Length == 0) { Console.Error.WriteLine("*** ERROR: Expected a TARGET argument like: [couchbase://HOST@USER:PASSWORD:BUCKET] or [http(s)://HOST:PORT@USER:PASSWORD:BUCKET]"); Program.Exit(1); } // Open a Couchbase bucket for the TARGET. // // http(s)://HOST:PORT@USER:PASSWORD:BUCKET // couchbase://HOST@USER:PASSWORD:BUCKET var target = commandLine.Arguments[0]; var error = $"*** ERROR: [{target}] is not a valid Couchbase target. Expected: [couchbase://HOST@USER:PASSWORD:BUCKET] or [http(s)://HOST:PORT@USER:PASSWORD:BUCKET]"; var fields = target.Split(new char[] { '@' }, 2); if (fields.Length != 2) { Console.WriteLine(error); Program.Exit(1); } var uri = fields[0]; fields = fields[1].Split(':'); if (fields.Length != 3) { Console.WriteLine(error); Program.Exit(1); } var username = fields[0]; var password = fields[1]; var bucketName = fields[2]; var config = new CouchbaseSettings(); config.Servers.Clear(); config.Servers.Add(new Uri(uri)); config.Bucket = bucketName; using (var bucket = config.OpenBucket(username, password)) { commandLine = commandLine.Shift(1); switch (command) { case "query": // Get the N1QL query. if (commandLine.Arguments.Length != 1) { Console.Error.WriteLine("*** ERROR: QUERY argument expected."); Program.Exit(1); } var query = commandLine.Arguments[0]; if (query == "-") { // Read the query from STDIN. query = NeonHelper.ReadStandardInputText(); } else if (query.StartsWith("@")) { // Read the query from the file. query = File.ReadAllText(query.Substring(1)); } var queryResults = bucket.Query <JToken>(query); Console.WriteLine(JsonConvert.SerializeObject(queryResults, Formatting.Indented)); break; case "upsert": if (commandLine.Arguments.Length != 1) { Console.Error.WriteLine("*** ERROR: JSON object argument expected."); Program.Exit(1); } var fileArg = commandLine.Arguments[0]; var input = (Stream)null; if (fileArg == "-") { // Read the object from STDIN. input = Console.OpenStandardInput(); } else { input = new FileStream(fileArg, FileMode.Open, FileAccess.ReadWrite); } var keyPattern = commandLine.GetOption("--key"); var firstKeyValue = commandLine.GetOption("--first-key", "1"); if (!long.TryParse(firstKeyValue, out var firstKey)) { Console.Error.WriteLine($"*** ERROR: [--firstkey={firstKeyValue}] is not a valid integer."); Program.Exit(1); } var upsertError = false; using (var reader = new StreamReader(input, Encoding.UTF8)) { var importer = new CouchbaseImporter( message => { upsertError = true; Console.Error.WriteLine($"*** ERROR: {message}"); }, bucket, keyPattern, firstKey); foreach (var line in reader.Lines()) { if (line.Trim() == string.Empty) { continue; // Ignore blank lines } var item = JToken.Parse(line); if (item.Type != JTokenType.Object) { upsertError = true; Console.Error.WriteLine($"*** ERROR: [{fileArg}] includes one or more lines with non-document objects."); break; } importer.WriteDocument((JObject)item); } } if (upsertError) { Program.Exit(1); } break; default: Console.Error.WriteLine($"*** ERROR: Unknown command: [{command}]"); Program.Exit(1); break; } } }
/// <inheritdoc/> public override void Run(CommandLine commandLine) { if (commandLine.HasHelpOption || commandLine.Arguments.Length == 0) { Console.WriteLine(usage); Program.Exit(0); } if (commandLine.Arguments.Length == 0) { Console.Error.WriteLine("*** ERROR: Expected a TARGET argument like: [couchbase://HOST@USER:PASSWORD:BUCKET] or [http(s)://HOST:PORT@USER:PASSWORD:BUCKET]"); Program.Exit(1); } // Open a Couchbase bucket for the TARGET. // // http(s)://HOST:PORT@USER:PASSWORD:BUCKET // couchbase://HOST@USER:PASSWORD:BUCKET var target = commandLine.Arguments[0]; var error = $"*** ERROR: [{target}] is not a valid Couchbase target. Expected: [couchbase://HOST@USER:PASSWORD:BUCKET] or [http(s)://HOST:PORT@USER:PASSWORD:BUCKET]"; var fields = target.Split(new char[] { '@' }, 2); if (fields.Length != 2) { Console.WriteLine(error); Program.Exit(1); } var uri = fields[0]; fields = fields[1].Split(':'); if (fields.Length != 3) { Console.WriteLine(error); Program.Exit(1); } var username = fields[0]; var password = fields[1]; var bucketName = fields[2]; var config = new CouchbaseSettings(); config.Servers.Clear(); config.Servers.Add(new Uri(uri)); config.Bucket = bucketName; using (var bucket = config.OpenBucket(username, password)) { commandLine = commandLine.Shift(1); if (commandLine.Arguments.Length != 1) { Console.Error.WriteLine("*** ERROR: JSON-FILE argument is required."); Program.Exit(1); } var fileArg = commandLine.Arguments[0]; var input = (Stream)null; if (fileArg == "-") { // Read from STDIN. input = NeonHelper.OpenStandardInput(); } else { input = new FileStream(fileArg, FileMode.Open, FileAccess.ReadWrite); } var keyPattern = commandLine.GetOption("--key"); var firstKeyValue = commandLine.GetOption("--first-key", "1"); if (!long.TryParse(firstKeyValue, out var firstKey)) { Console.Error.WriteLine($"*** ERROR: [--firstkey={firstKeyValue}] is not a valid integer."); Program.Exit(1); } var upsertError = false; using (var reader = new StreamReader(input, Encoding.UTF8)) { var importer = new CouchbaseImporter( message => { upsertError = true; Console.Error.WriteLine($"*** ERROR: {message}"); }, bucket, keyPattern, firstKey); foreach (var line in reader.Lines()) { if (line.Trim() == string.Empty) { continue; // Ignore blank lines } var item = JToken.Parse(line); if (item.Type != JTokenType.Object) { upsertError = true; Console.Error.WriteLine($"*** ERROR: [{fileArg}] includes one or more lines with non-JSON document objects."); break; } importer.WriteDocument((JObject)item); } } if (upsertError) { Program.Exit(1); } } Program.Exit(0); }