Exemple #1
0
        public void Literal_Name()
        {
            Assert.Throws <ArgumentNullException>(() => CbHelper.LiteralName(null));
            Assert.Throws <ArgumentNullException>(() => CbHelper.LiteralName(string.Empty));

            Assert.Equal("`test`", CbHelper.LiteralName("test"));
            Assert.Equal("`test\\\\foo`", CbHelper.LiteralName("test\\foo"));
        }
Exemple #2
0
 public void Literal_String()
 {
     Assert.Equal("NULL", CbHelper.Literal(null));
     Assert.Equal("\"Hello World!\"", CbHelper.Literal("Hello World!"));
     Assert.Equal("\"'\"", CbHelper.Literal("'"));
     Assert.Equal("\"\\\"\"", CbHelper.Literal("\""));
     Assert.Equal("\"\\\\\"", CbHelper.Literal("\\"));
     Assert.Equal("\"\\b\"", CbHelper.Literal("\b"));
     Assert.Equal("\"\\f\"", CbHelper.Literal("\f"));
     Assert.Equal("\"\\n\"", CbHelper.Literal("\n"));
     Assert.Equal("\"\\r\"", CbHelper.Literal("\r"));
     Assert.Equal("\"\\t\"", CbHelper.Literal("\t"));
 }
Exemple #3
0
        public async Task Flush()
        {
            var indexQuery = $"select * from system:indexes where keyspace_id={CbHelper.Literal(bucket.Name)}";

            // Flush and verify that the primary index was created by default.

            couchbase.Clear();

            var indexes = await bucket.QuerySafeAsync <JObject>(indexQuery);

            Assert.Single(indexes);

            var index = (JObject)indexes.First().GetValue("indexes");

            Assert.True((bool)index.GetValue("is_primary"));
            Assert.Equal("idx_primary", (string)index.GetValue("name"));

            // Write some data, verify that it was written then flush
            // the bucket and verify that the data is gone.

            await bucket.UpsertSafeAsync("hello", "world!");

            Assert.Equal("world!", await bucket.GetSafeAsync <string>("hello"));

            couchbase.Clear();
            Assert.Null(await bucket.FindSafeAsync <string>("hello"));

            // Create a secondary index and verify.

            await bucket.QuerySafeAsync <dynamic>($"create index idx_foo on {CbHelper.LiteralName(bucket.Name)} ( {CbHelper.LiteralName("Test")} )");

            indexes = await bucket.QuerySafeAsync <JObject>(indexQuery);

            Assert.Equal(2, indexes.Count);     // Expecting the primary and new secondary index

            // Clear the database and then verify that only the
            // recreated primary index exists.

            couchbase.Clear();

            indexes = await bucket.QuerySafeAsync <JObject>(indexQuery);

            Assert.Single(indexes);

            index = (JObject)indexes.First().GetValue("indexes");

            Assert.True((bool)index.GetValue("is_primary"));
            Assert.Equal("idx_primary", (string)index.GetValue("name"));
        }
Exemple #4
0
        /// <inheritdoc/>
        public void Run(ModuleContext context)
        {
            var hive       = HiveHelper.Hive;
            var nodeGroups = hive.Definition.GetHostGroups(excludeAllGroup: true);

            //-----------------------------------------------------------------
            // Parse the module arguments.

            if (!context.ValidateArguments(context.Arguments, validModuleArgs))
            {
                context.Failed = true;
                return;
            }

            var couchbaseArgs = CouchbaseArgs.Parse(context);

            if (couchbaseArgs == null)
            {
                return;
            }

            if (!context.Arguments.TryGetValue <string>("state", out var state))
            {
                state = "present";
            }

            state = state.ToLowerInvariant();

            if (!context.Arguments.TryGetValue <bool>("force", out var force))
            {
                force = false;
            }

            var buildNow = context.ParseBool("build_all");

            if (!buildNow.HasValue)
            {
                buildNow = false;
            }

            var primary = context.ParseBool("primary") ?? false;

            string name;

            if (primary)
            {
                name = "#primary";
            }
            else
            {
                name = context.ParseString("name", v => new Regex(@"[a-z][a-z0-0#_]*", RegexOptions.IgnoreCase).IsMatch(v));
            }

            if (string.IsNullOrEmpty(name) && state != "build")
            {
                context.WriteErrorLine("[name] argument is required.");
            }

            var type        = (context.ParseEnum <IndexType>("using") ?? default(IndexType)).ToString().ToUpperInvariant();
            var namespaceId = context.ParseString("namespace");

            if (string.IsNullOrEmpty(namespaceId))
            {
                namespaceId = "default";
            }

            var keys = context.ParseStringArray("keys");

            var where = context.ParseString("where");
            var nodes = context.ParseStringArray("nodes");
            var defer = context.ParseBool("build_defer") ?? false;
            var wait  = context.ParseBool("build_wait") ?? true;

            var replicas = context.ParseInt("replicas");

            if (state == "present")
            {
                if (primary)
                {
                    if (keys.Count > 0)
                    {
                        context.WriteErrorLine("PRIMARY indexes do not allow any [keys].");
                        return;
                    }

                    if (!string.IsNullOrEmpty(where))
                    {
                        context.WriteErrorLine("PRIMARY indexes do not support the [where] clause.");
                        return;
                    }
                }
                else
                {
                    if (keys.Count == 0)
                    {
                        context.WriteErrorLine("Non-PRIMARY indexes must specify at least one [key].");
                        return;
                    }
                }

                if (type == "GSI" && replicas.HasValue && nodes.Count > 0)
                {
                    context.WriteErrorLine("Only one of [nodes] or [replicas] may be specified for GSI indexes.");
                    return;
                }
            }

            string keyspace;

            if (!string.IsNullOrEmpty(namespaceId) && namespaceId != "default")
            {
                keyspace = $"{namespaceId}:{couchbaseArgs.Settings.Bucket}";
            }
            else
            {
                keyspace = couchbaseArgs.Settings.Bucket;
            }

            if (context.HasErrors)
            {
                return;
            }

            //-----------------------------------------------------------------
            // Perform the operation.

            Task.Run(
                async() =>
            {
                using (var bucket = couchbaseArgs.Settings.OpenBucket(couchbaseArgs.Credentials))
                {
                    var indexId = $"{bucket.Name}.{name}";

                    // Fetch the index if it already exists.

                    var existingIndex = await bucket.GetIndexAsync(name);

                    if (existingIndex == null)
                    {
                        context.WriteLine(AnsibleVerbosity.Trace, $"Index [{name}] does not exist.");
                    }
                    else
                    {
                        context.WriteLine(AnsibleVerbosity.Trace, $"Index [{name}] exists.");
                    }

                    var existingIsPrimary = existingIndex != null && existingIndex.IsPrimary;

                    switch (state.ToLowerInvariant())
                    {
                    case "present":

                        // Generate the index creation query.

                        var sbCreateIndexCommand = new StringBuilder();

                        if (primary)
                        {
                            sbCreateIndexCommand.Append($"create primary index {CbHelper.LiteralName(name)} on {CbHelper.LiteralName(bucket.Name)}");
                        }
                        else
                        {
                            var sbKeys = new StringBuilder();

                            foreach (var key in keys)
                            {
                                sbKeys.AppendWithSeparator(key, ", ");
                            }

                            sbCreateIndexCommand.Append($"create index {CbHelper.LiteralName(name)} on {CbHelper.LiteralName(bucket.Name)} ( {sbKeys} )");
                        }

                        // Append the WHERE clause for non-PRIMARY indexes.

                        if (!primary && !string.IsNullOrEmpty(where))
                        {
                            // Ensure that the WHERE clause is surrounded by "( ... )".

                            if (!where.StartsWith("(") && !where.EndsWith(")"))
                            {
                                where = $"({where})";
                            }

                            // Now strip the parens off the where clause to be added
                            // to the query.

                            var queryWhere = where.Substring(1, where.Length - 2);

                            // Append the clause.

                            sbCreateIndexCommand.AppendWithSeparator($"where {queryWhere}");
                        }

                        // Append the USING clause.

                        sbCreateIndexCommand.AppendWithSeparator($"using {type}");

                        // Append the WITH clause for GSI indexes.

                        if (type == "GSI")
                        {
                            var sbWithSettings = new StringBuilder();

                            if (defer)
                            {
                                sbWithSettings.AppendWithSeparator("\"defer_build\":true", ", ");
                            }

                            context.WriteLine(AnsibleVerbosity.Trace, "Query for the hive nodes.");

                            var clusterNodes = await bucket.QuerySafeAsync <dynamic>("select nodes.name from system:nodes");

                            context.WriteLine(AnsibleVerbosity.Trace, $"Hive has [{clusterNodes.Count}] nodes.");

                            if ((!replicas.HasValue || replicas.Value == 0) && nodes.Count == 0)
                            {
                                // We're going to default to hosting GSI indexes explicitly
                                // on all nodes unless directed otherwise.  We'll need query
                                // the database for the current nodes.

                                foreach (JObject node in clusterNodes)
                                {
                                    nodes.Add((string)node.GetValue("name"));
                                }
                            }
                            else if (replicas.HasValue && replicas.Value > 0)
                            {
                                if (clusterNodes.Count <= replicas.Value)
                                {
                                    context.WriteErrorLine($"[replicas={replicas.Value}] cannot equal or exceed the number of Couchbase nodes.  [replicas={clusterNodes.Count - 1}] is the maximum allowed value for this hive.");
                                    return;
                                }
                            }

                            if (nodes.Count > 0)
                            {
                                sbWithSettings.AppendWithSeparator("\"nodes\": [", ", ");

                                var first = true;

                                foreach (var server in nodes)
                                {
                                    if (first)
                                    {
                                        first = false;
                                    }
                                    else
                                    {
                                        sbCreateIndexCommand.Append(",");
                                    }

                                    sbWithSettings.Append(CbHelper.Literal(server));
                                }

                                sbWithSettings.Append("]");
                            }

                            if (replicas.HasValue && type == "GSI")
                            {
                                sbWithSettings.AppendWithSeparator($"\"num_replica\":{CbHelper.Literal(replicas.Value)}", ", ");
                            }

                            if (sbWithSettings.Length > 0)
                            {
                                sbCreateIndexCommand.AppendWithSeparator($"with {{ {sbWithSettings} }}");
                            }
                        }

                        // Add or update the index.

                        if (existingIndex != null)
                        {
                            context.WriteLine(AnsibleVerbosity.Info, $"Index [{indexId}] already exists.");

                            // An index with this name already exists, so we'll compare its
                            // properties with the module parameters to determine whether we
                            // need to remove and recreate it.

                            var changed = false;

                            if (force)
                            {
                                changed = true;
                                context.WriteLine(AnsibleVerbosity.Info, "Rebuilding index because [force=yes].");
                            }
                            // Compare the old/new index types.

                            var orgType = existingIndex.Type.ToUpperInvariant();

                            if (!string.Equals(orgType, type, StringComparison.InvariantCultureIgnoreCase))
                            {
                                changed = true;
                                context.WriteLine(AnsibleVerbosity.Info, $"Rebuilding index because using changed from [{orgType}] to [{type}].");
                            }

                            // Compare the old/new index keys.

                            var keysChanged = false;

                            if (!primary)
                            {
                                var orgKeys = existingIndex.Keys;

                                if (orgKeys.Length != keys.Count)
                                {
                                    keysChanged = true;
                                }
                                else
                                {
                                    // This assumes that the order of the indexed keys doesn't
                                    // matter.

                                    var keysSet = new Dictionary <string, bool>(StringComparer.InvariantCultureIgnoreCase);

                                    for (int i = 0; i < orgKeys.Length; i++)
                                    {
                                        keysSet[(string)orgKeys[i]] = false;
                                    }

                                    for (int i = 0; i < orgKeys.Length; i++)
                                    {
                                        keysSet[CbHelper.LiteralName(keys[i])] = true;
                                    }

                                    keysChanged = keysSet.Values.Count(k => !k) > 0;
                                }

                                if (keysChanged)
                                {
                                    changed = true;

                                    var sbOrgKeys = new StringBuilder();
                                    var sbNewKeys = new StringBuilder();

                                    foreach (string key in orgKeys)
                                    {
                                        sbOrgKeys.AppendWithSeparator(key, ", ");
                                    }

                                    foreach (string key in keys)
                                    {
                                        sbNewKeys.AppendWithSeparator(key, ", ");
                                    }

                                    context.WriteLine(AnsibleVerbosity.Info, $"Rebuilding index because keys changed from [{sbOrgKeys}] to [{sbNewKeys}].");
                                }
                            }

                            // Compare the filter condition.

                            var orgWhere = existingIndex.Where;

                            if (orgWhere != where)
                            {
                                changed = true;
                                context.WriteLine(AnsibleVerbosity.Info, $"Rebuilding index because where clause changed from [{orgWhere ?? string.Empty}] to [{where ?? string.Empty}].");
                            }

                            // We need to remove and recreate the index if it differs
                            // from what was requested.

                            if (changed)
                            {
                                if (context.CheckMode)
                                {
                                    context.WriteLine(AnsibleVerbosity.Important, $"Index [{indexId}] will be rebuilt when CHECK-MODE is disabled.");
                                }
                                else
                                {
                                    context.Changed = !context.CheckMode;

                                    context.WriteLine(AnsibleVerbosity.Trace, $"Removing existing index [{indexId}].");
                                    string dropCommand;

                                    if (existingIsPrimary)
                                    {
                                        dropCommand = $"drop primary index on {CbHelper.LiteralName(bucket.Name)} using {orgType.ToUpperInvariant()}";
                                    }
                                    else
                                    {
                                        dropCommand = $"drop index {CbHelper.LiteralName(bucket.Name)}.{CbHelper.LiteralName(name)} using {orgType.ToUpperInvariant()}";
                                    }

                                    context.WriteLine(AnsibleVerbosity.Trace, $"DROP COMMAND: {dropCommand}");
                                    await bucket.QuerySafeAsync <dynamic>(dropCommand);
                                    context.WriteLine(AnsibleVerbosity.Trace, $"Dropped index [{indexId}].");

                                    context.WriteLine(AnsibleVerbosity.Trace, $"CREATE COMMAND: {sbCreateIndexCommand}");
                                    await bucket.QuerySafeAsync <dynamic>(sbCreateIndexCommand.ToString());
                                    context.WriteLine(AnsibleVerbosity.Info, $"Created index [{indexId}].");

                                    if (!defer && wait)
                                    {
                                        // Wait for the index to come online.

                                        context.WriteLine(AnsibleVerbosity.Info, $"Waiting for index [{name}] to be built.");
                                        await bucket.WaitForIndexAsync(name, "online");
                                        context.WriteLine(AnsibleVerbosity.Info, $"Completed building index [{name}].");
                                    }
                                }
                            }
                            else
                            {
                                context.WriteLine(AnsibleVerbosity.Trace, $"No changes detected for index [{indexId}].");
                            }
                        }
                        else
                        {
                            if (context.CheckMode)
                            {
                                context.WriteLine(AnsibleVerbosity.Important, $"Index [{indexId}] will be created when CHECK-MODE is disabled.");
                                context.WriteLine(AnsibleVerbosity.Trace, $"{sbCreateIndexCommand}");
                            }
                            else
                            {
                                context.Changed = true;

                                context.WriteLine(AnsibleVerbosity.Trace, $"Creating index.");
                                context.WriteLine(AnsibleVerbosity.Trace, $"CREATE COMMAND: {sbCreateIndexCommand}");
                                await bucket.QuerySafeAsync <dynamic>(sbCreateIndexCommand.ToString());
                                context.WriteLine(AnsibleVerbosity.Info, $"Created index [{indexId}].");

                                if (!defer && wait)
                                {
                                    // Wait for the index to come online.

                                    context.WriteLine(AnsibleVerbosity.Info, $"Waiting for index [{name}] to be built.");
                                    await bucket.WaitForIndexAsync(name, "online");
                                    context.WriteLine(AnsibleVerbosity.Info, $"Completed building index [{name}].");
                                }
                            }
                        }
                        break;

                    case "absent":

                        if (existingIndex != null)
                        {
                            if (context.CheckMode)
                            {
                                context.WriteLine(AnsibleVerbosity.Important, $"Index [{indexId}] will be dropped when CHECK-MODE is disabled.");
                            }
                            else
                            {
                                context.Changed = true;
                                context.WriteLine(AnsibleVerbosity.Info, $"Dropping index [{indexId}].");

                                string orgType = existingIndex.Type;
                                string dropCommand;

                                if (existingIsPrimary)
                                {
                                    dropCommand = $"drop primary index on {CbHelper.LiteralName(bucket.Name)} using {orgType.ToUpperInvariant()}";
                                }
                                else
                                {
                                    dropCommand = $"drop index {CbHelper.LiteralName(bucket.Name)}.{CbHelper.LiteralName(name)} using {orgType.ToUpperInvariant()}";
                                }

                                context.WriteLine(AnsibleVerbosity.Trace, $"COMMAND: {dropCommand}");
                                await bucket.QuerySafeAsync <dynamic>(dropCommand);
                                context.WriteLine(AnsibleVerbosity.Trace, $"Index [{indexId}] was dropped.");
                            }
                        }
                        else
                        {
                            context.WriteLine(AnsibleVerbosity.Important, $"Index [{indexId}] does not exist so there's no need to drop it.");
                        }
                        break;

                    case "build":

                        // List the names of the deferred GSI indexes.

                        var deferredIndexes = ((await bucket.ListIndexesAsync()).Where(index => index.State == "deferred" && index.Type == "gsi")).ToList();

                        context.WriteLine(AnsibleVerbosity.Info, $"[{deferredIndexes.Count}] deferred GSI indexes exist.");

                        if (deferredIndexes.Count == 0)
                        {
                            context.WriteLine(AnsibleVerbosity.Important, $"All GSI indexes have already been built.");
                            context.Changed = false;
                            return;
                        }

                        // Build the indexes (unless we're in CHECK-MODE).

                        var sbIndexList = new StringBuilder();

                        foreach (var deferredIndex in deferredIndexes)
                        {
                            sbIndexList.AppendWithSeparator($"{CbHelper.LiteralName(deferredIndex.Name)}", ", ");
                        }

                        if (context.CheckMode)
                        {
                            context.WriteLine(AnsibleVerbosity.Important, $"These GSI indexes will be built when CHECK-MODE is disabled: {sbIndexList}.");
                            context.Changed = false;
                            return;
                        }

                        var buildCommand = $"BUILD INDEX ON {CbHelper.LiteralName(bucket.Name)} ({sbIndexList})";

                        context.WriteLine(AnsibleVerbosity.Trace, $"BUILD COMMAND: {buildCommand}");
                        context.WriteLine(AnsibleVerbosity.Info, $"Building indexes: {sbIndexList}");
                        await bucket.QuerySafeAsync <dynamic>(buildCommand);
                        context.WriteLine(AnsibleVerbosity.Info, $"Build command submitted.");
                        context.Changed = true;

                        // The Couchbase BUILD INDEX command doesn't wait for the index
                        // building to complete so, we'll just spin until all of the
                        // indexes we're building are online.

                        if (wait)
                        {
                            context.WriteLine(AnsibleVerbosity.Info, $"Waiting for the indexes to be built.");

                            foreach (var deferredIndex in deferredIndexes)
                            {
                                await bucket.WaitForIndexAsync(deferredIndex.Name, "online");
                            }

                            context.WriteLine(AnsibleVerbosity.Info, $"Completed building [{deferredIndexes.Count}] indexes.");
                        }
                        break;

                    default:

                        throw new ArgumentException($"[state={state}] is not one of the valid choices: [present], [absent], or [build].");
                    }
                }
            }).Wait();
        }
Exemple #5
0
        /// <summary>
        /// Removes all data and indexes from the database bucket and then recreates the
        /// primary index if an index was specified when the fixture was started.
        /// </summary>
        public void Clear()
        {
            CheckDisposed();

            // $todo(jeff.lill):
            //
            // The code below was originally intended to clear the Couchbase bucket
            // in place and this seemed to work for several months and then it just stopped
            // working in Nov 2018 after I upgraded the CouchbaseNetClient nuget package.
            // The weird thing is that is still didn't work after I reverted.
            //
            // The problem seems to be due to a timing or race condition because if I pause
            // execution after clearing, the subsequent unit test passes.  Unfortunately, I
            // haven't been able to figure out how to determine when everything is ready.
            // I even tried executing the unit test query that fails but it succeeded here
            // but still failed in the test.  I can't really explain that: perhaps
            // Couchbase restarted one or more services sometime after I cleared the
            // bucket but after I checked for health below.
            //
            // It seems like the first test ran against a clean container always works
            // so I'm going to revert to simply restarting the container and come back
            // someday and remove this section.

#if DIDNT_WORK
            // Drop all of the bucket indexes.

            var existingIndexes = Bucket.ListIndexesAsync().Result;

            if (existingIndexes.Count > 0)
            {
                foreach (var index in existingIndexes)
                {
                    Bucket.QuerySafeAsync <dynamic>($"drop index {CbHelper.LiteralName(Bucket.Name)}.{CbHelper.LiteralName(index.Name)} using {index.Type}").Wait();
                }
            }

            // Flush the bucket data.

            using (var bucketManager = Bucket.CreateManager())
            {
                NeonBucket.ReadyRetry.InvokeAsync(
                    async() =>
                {
                    bucketManager.Flush();
                    await Bucket.WaitUntilReadyAsync();
                }).Wait();
            }

            // Wait until all of the indexes are actually deleted.

            NeonHelper.WaitFor(
                () =>
            {
                var indexes = Bucket.ListIndexesAsync().Result;

                return(indexes.Count == 0);
            },
                timeout: NeonBucket.ReadyTimeout,
                pollTime: TimeSpan.FromMilliseconds(500));

            // Recreate the primary index if one was enabled when the fixture was started.

            if (createPrimaryIndex)
            {
                Bucket.QuerySafeAsync <dynamic>($"create primary index on {CbHelper.LiteralName(Bucket.Name)} using gsi").Wait();
                Bucket.WaitForIndexAsync("#primary").Wait();
            }
#endif
            base.Restart();
            Thread.Sleep(warmupDelay);
            ConnectBucket();
        }
Exemple #6
0
        /// <summary>
        /// Establishes the bucket connection and waits until the Couchbase container is ready
        /// to start handling requests.
        /// </summary>
        private void ConnectBucket()
        {
            // Give the Couchbase container a chance to spin up.

            Thread.Sleep(warmupDelay);

            // Dispose any existing underlying cluster and bucket.

            if (Bucket != null)
            {
                var existingBucket = Bucket.GetInternalBucket();

                if (existingBucket != null)
                {
                    existingBucket.Cluster.CloseBucket(existingBucket);
                    existingBucket.Cluster.Dispose();
                    Bucket.SetInternalBucket(null);
                }
            }

            // It appears that it may take a bit of time for the Couchbase query
            // service to start in new container we started above.  We're going to
            // retry creating the primary index (or a dummy index) until it works.

            var bucket       = (NeonBucket)null;
            var indexCreated = false;
            var indexReady   = false;
            var queryReady   = false;

            NeonBucket.ReadyRetry.InvokeAsync(
                async() =>
            {
                if (bucket == null)
                {
                    bucket = Settings.OpenBucket(Username, Password);
                }

                try
                {
                    if (createPrimaryIndex)
                    {
                        // Create the primary index if requested.

                        if (!indexCreated)
                        {
                            await bucket.QuerySafeAsync <dynamic>($"create primary index on {CbHelper.LiteralName(bucket.Name)} using gsi");
                            indexCreated = true;
                        }

                        if (!indexReady)
                        {
                            await bucket.WaitForIndexAsync("#primary");
                            indexReady = true;
                        }

                        // Ensure that the query service is running too.

                        if (!queryReady)
                        {
                            var query = new QueryRequest($"select meta({bucket.Name}).id, {bucket.Name}.* from {bucket.Name}")
                                        .ScanConsistency(ScanConsistency.RequestPlus);

                            await bucket.QuerySafeAsync <dynamic>(query);
                            queryReady = true;
                        }
                    }
                    else
                    {
                        // List the indexes to ensure the index service is ready when we didn't create a primary index.

                        if (!queryReady)
                        {
                            await bucket.ListIndexesAsync();
                            queryReady = true;
                        }
                    }
                }
                catch
                {
                    // $hack(jeff.lill):
                    //
                    // It looks like we need to create a new bucket if the query service
                    // wasn't ready.  I'm guessing that this is due to the Couchbase index
                    // service not being ready at the time the bucket was connected and
                    // the bucket isn't smart enough to retry looking for the index service
                    // afterwards.  This won't be a problem for most real-world scenarios
                    // because for those, Couchbase will have been started long ago and
                    // will continue running indefinitely.
                    //
                    // We'll dispose the old bucket and set it to NULL here and then
                    // open a fresh bucket above when the retry policy tries again.

                    bucket.Dispose();
                    bucket = null;

                    throw;
                }
            }).Wait();

            // Use the new bucket if this is the first Couchbase container created or
            // else substitute the new underlying bucket into the existing bucket so
            // that unit tests don't need to be aware of the change.

            if (this.Bucket == null)
            {
                this.Bucket = bucket;
            }
            else
            {
                this.Bucket.SetInternalBucket(bucket.GetInternalBucket());
            }
        }