示例#1
0
        public bool TryLookup(NamespacedName key, out ReconcileData data)
        {
            var endspointsList = new List <Endpoints>();

            lock (_sync)
            {
                if (!_ingressData.TryGetValue(key.Name, out var ingress))
                {
                    ingress = default;
                }

                if (_ingressToServiceNames.TryGetValue(key.Name, out var serviceNames))
                {
                    foreach (var serviceName in serviceNames)
                    {
                        if (_endpointsData.TryGetValue(serviceName, out var serviceData))
                        {
                            endspointsList.Add(serviceData);
                        }
                    }
                }

                data = new ReconcileData(ingress, endspointsList);
                return(true);
            }
        }
示例#2
0
 /// <inheritdoc/>
 public bool TryGetWorkItem(NamespacedName namespacedName, out OperatorCacheWorkItem <TResource> workItem)
 {
     lock (_workItemsSync)
     {
         return(_workItems.TryGetValue(namespacedName, out workItem));
     }
 }
示例#3
0
    /// <inheritdoc/>
    public void UpdateWorkItem(NamespacedName namespacedName, UpdateWorkItem <TResource> update)
    {
        if (update is null)
        {
            throw new ArgumentNullException(nameof(update));
        }

        lock (_workItemsSync)
        {
            if (_workItems.TryGetValue(namespacedName, out var workItem))
            {
                // alter an existing entry
                workItem = update(workItem);
                if (workItem.IsEmpty)
                {
                    // remove if result has no information
                    _workItems.Remove(namespacedName);
                }
                else
                {
                    // otherwise update struct in dictionary
                    _workItems[namespacedName] = workItem;
                }
            }
            else
            {
                workItem = update(OperatorCacheWorkItem <TResource> .Empty);
                if (workItem.IsEmpty == false)
                {
                    // add if result has information
                    _workItems.Add(namespacedName, workItem);
                }
            }
        }
    }
    public void EqualityAndInequality(
        string namespace1,
        string name1,
        string namespace2,
        string name2,
        bool shouldBeEqual)
    {
        var namespacedName1 = new NamespacedName(namespace1, name1);
        var namespacedName2 = new NamespacedName(namespace2, name2);

        var areEqual    = namespacedName1 == namespacedName2;
        var areNotEqual = namespacedName1 != namespacedName2;

#pragma warning disable CS1718 // Comparison made to same variable
        var sameEqual1    = namespacedName1 == namespacedName1;
        var sameNotEqual1 = namespacedName1 != namespacedName1;
        var sameEqual2    = namespacedName2 == namespacedName2;
        var sameNotEqual2 = namespacedName2 != namespacedName2;
#pragma warning restore CS1718 // Comparison made to same variable

        Assert.NotEqual(areNotEqual, areEqual);
        Assert.Equal(shouldBeEqual, areEqual);
        Assert.True(sameEqual1);
        Assert.False(sameNotEqual1);
        Assert.True(sameEqual2);
        Assert.False(sameNotEqual2);
    }
示例#5
0
        public async Task ProcessAsync(IDispatchTarget target, NamespacedName key, ReconcileData data, CancellationToken cancellationToken)
        {
            try
            {
                var message = new Message
                {
                    MessageType = MessageType.Update,
                    Key         = $"{key.Namespace}:{key.Name}"
                };
                var context = new YarpIngressContext(data.Ingress, data.ServiceList, data.EndpointsList);
                YarpParser.CovertFromKubernetesIngress(context);

                message.Cluster = context.Clusters;
                message.Routes  = context.Routes;

                var bytes = JsonSerializer.SerializeToUtf8Bytes(message);

                _logger.LogInformation(JsonSerializer.Serialize(message));

                await _dispatcher.SendAsync(target, bytes, cancellationToken).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex.Message);
                throw;
            }
        }
    public void JustNameFromClusterResource()
    {
        var resource = new V1ClusterRole(
            apiVersion: V1ClusterRole.KubeApiVersion,
            kind: V1ClusterRole.KubeKind,
            metadata: new V1ObjectMeta(
                name: "the-name"));

        var nn = NamespacedName.From(resource);

        Assert.Equal("the-name", nn.Name);
        Assert.Null(nn.Namespace);
    }
示例#7
0
        private void OnPrimaryResourceWatchEvent(WatchEventType watchEventType, TResource resource)
        {
            var key = NamespacedName.From(resource);

            _cache.UpdateWorkItem(key, workItem =>
            {
                if (watchEventType == WatchEventType.Added || watchEventType == WatchEventType.Modified)
                {
                    workItem = workItem.SetResource(resource);
                }
                else if (watchEventType == WatchEventType.Deleted)
                {
                    workItem = workItem.SetResource(default);
                }
    public void NamespaceAndNameFromResource()
    {
        var resource = new V1ConfigMap(
            apiVersion: V1ConfigMap.KubeApiVersion,
            kind: V1ConfigMap.KubeKind,
            metadata: new V1ObjectMeta(
                name: "the-name",
                namespaceProperty: "the-namespace"));

        var nn = NamespacedName.From(resource);

        Assert.Equal("the-name", nn.Name);
        Assert.Equal("the-namespace", nn.Namespace);
    }
    public void WorksAsDictionaryKey()
    {
        var dictionary = new Dictionary <NamespacedName, string>();
        var name1      = new NamespacedName("ns", "n1");
        var name2      = new NamespacedName("ns", "n2");
        var name3      = new NamespacedName("ns", "n3");

        dictionary[name1] = "one";
        dictionary[name1] = "one again";
        dictionary[name2] = "two";

        Assert.Contains(new KeyValuePair <NamespacedName, string>(name1, "one again"), dictionary);
        Assert.Contains(new KeyValuePair <NamespacedName, string>(name2, "two"), dictionary);
        Assert.DoesNotContain(name3, dictionary.Keys);
    }
示例#10
0
        private void OnEvent(WatchEventType watchEventType, TResource item)
        {
            if (watchEventType != WatchEventType.Modified || item.Kind != "ConfigMap")
            {
                Logger.LogDebug(
                    EventId(EventType.InformerWatchEvent),
                    "Informer {ResourceType} received {WatchEventType} notification for {ItemKind}/{ItemName}.{ItemNamespace} at resource version {ResourceVersion}",
                    typeof(TResource).Name,
                    watchEventType,
                    item.Kind,
                    item.Name(),
                    item.Namespace(),
                    item.ResourceVersion());
            }

            if (watchEventType == WatchEventType.Added ||
                watchEventType == WatchEventType.Modified)
            {
                // BUGBUG: log warning if cache was not in expected state
                _cache[NamespacedName.From(item)] = item.Metadata?.OwnerReferences;
            }

            if (watchEventType == WatchEventType.Deleted)
            {
                _cache.Remove(NamespacedName.From(item));
            }

            if (watchEventType == WatchEventType.Added ||
                watchEventType == WatchEventType.Modified ||
                watchEventType == WatchEventType.Deleted ||
                watchEventType == WatchEventType.Bookmark)
            {
                _lastResourceVersion = item.ResourceVersion();
            }

            if (watchEventType == WatchEventType.Added ||
                watchEventType == WatchEventType.Modified ||
                watchEventType == WatchEventType.Deleted)
            {
                InvokeRegistrationCallbacks(watchEventType, item);
            }
        }
示例#11
0
    public void NotifyWithPrimaryResourceCausesCacheEntryAndQueueItem()
    {
        var generator       = Mock.Of <IOperatorGenerator <TypicalResource> >();
        var typicalInformer = new FakeResourceInformer <TypicalResource>();
        var podInformer     = new FakeResourceInformer <V1Pod>();
        var addCalls        = new List <NamespacedName>();
        var queue           = new FakeQueue <NamespacedName>
        {
            OnAdd = addCalls.Add,
        };
        var cache = new OperatorCache <TypicalResource>();

        using var host = new HostBuilder()
                         .ConfigureServices(services =>
        {
            services
            .AddLogging()
            .AddKubernetesOperatorRuntime()
            .AddOperator <TypicalResource>(op =>
            {
                op.WithRelatedResource <V1Pod>();
                op.Configure(options => options.NewRateLimitingQueue = _ => queue);
            })
            .AddSingleton(generator)
            .AddSingleton <IResourceInformer <TypicalResource> >(typicalInformer)
            .AddSingleton <IResourceInformer <V1Pod> >(podInformer)
            .AddSingleton <IOperatorCache <TypicalResource> >(cache);
        })
                         .Build();

        var handler = host.Services.GetRequiredService <IOperatorHandler <TypicalResource> >();

        var typical = new TypicalResource
        {
            ApiVersion = $"{TypicalResource.KubeGroup}/{TypicalResource.KubeApiVersion}",
            Kind       = TypicalResource.KubeKind,
            Metadata   = new V1ObjectMeta(
                name: "test-name",
                namespaceProperty: "test-namespace")
        };

        var unrelatedPod = new V1Pod
        {
            ApiVersion = TypicalResource.KubeApiVersion,
            Kind       = TypicalResource.KubeKind,
            Metadata   = new V1ObjectMeta(
                name: "test-unrelated",
                namespaceProperty: "test-namespace")
        };

        var relatedPod = new V1Pod(

            apiVersion: TypicalResource.KubeApiVersion,
            kind: TypicalResource.KubeKind,
            metadata: new V1ObjectMeta(
                name: "test-related",
                namespaceProperty: "test-namespace",
                ownerReferences: new[]
        {
            new V1OwnerReference(
                uid: typical.Uid(),
                apiVersion: typical.ApiVersion,
                kind: typical.Kind,
                name: typical.Name())
        }));

        typicalInformer.Callback(WatchEventType.Added, typical);
        podInformer.Callback(WatchEventType.Added, unrelatedPod);
        podInformer.Callback(WatchEventType.Added, relatedPod);

        var expectedName = new NamespacedName("test-namespace", "test-name");

        Assert.Equal(new[] { expectedName, expectedName }, addCalls);

        Assert.True(cache.TryGetWorkItem(expectedName, out var cacheItem));

        Assert.Equal(typical, cacheItem.Resource);

        var related = Assert.Single(cacheItem.Related);

        Assert.Equal(GroupKindNamespacedName.From(relatedPod), related.Key);
        Assert.Equal(relatedPod, related.Value);
    }
 /// <summary>
 /// Initializes a new instance of the <see cref="GroupKindNamespacedName"/> struct.
 /// </summary>
 /// <param name="group">The group.</param>
 /// <param name="kind">The kind.</param>
 /// <param name="namespacedName">Name of the namespaced.</param>
 public GroupKindNamespacedName(string group, string kind, NamespacedName namespacedName)
 {
     Group          = group;
     Kind           = kind;
     NamespacedName = namespacedName;
 }
示例#13
0
 public bool Equals(QueueItem other)
 {
     return(NamespacedName.Equals(other.NamespacedName) &&
            EqualityComparer <IDispatchTarget> .Default.Equals(DispatchTarget, other.DispatchTarget));
 }
示例#14
0
 public QueueItem(NamespacedName namespacedName, IDispatchTarget dispatchTarget)
 {
     NamespacedName = namespacedName;
     DispatchTarget = dispatchTarget;
 }
示例#15
0
        private async Task ListAsync(CancellationToken cancellationToken)
        {
            var previousCache = _cache;

            _cache = new Dictionary <NamespacedName, IList <V1OwnerReference> >();

            Logger.LogInformation(
                EventId(EventType.SynchronizeStarted),
                "Started synchronizing {ResourceType} resources from API server.",
                typeof(TResource).Name);

            string continueParameter = null;

            do
            {
                cancellationToken.ThrowIfCancellationRequested();

                // request next page of items
                using var listWithHttpMessage = await _client.ListClusterAnyResourceKindWithHttpMessagesAsync <TResource>(
                          _names.Group,
                          _names.ApiVersion,
                          _names.PluralName,
                          continueParameter : continueParameter,
                          cancellationToken : cancellationToken);

                var list = listWithHttpMessage.Body;
                foreach (var item in list.Items)
                {
                    // These properties are not already set on items while listing
                    // assigned here for consistency
                    item.ApiVersion = _names.GroupApiVersion;
                    item.Kind       = _names.Kind;

                    var key = NamespacedName.From(item);
                    _cache[key] = item?.Metadata?.OwnerReferences;

                    var watchEventType = WatchEventType.Added;
                    if (previousCache.Remove(key))
                    {
                        // an already-known key is provided as a modification for re-sync purposes
                        watchEventType = WatchEventType.Modified;
                    }

                    InvokeRegistrationCallbacks(watchEventType, item);
                }

                foreach (var(key, value) in previousCache)
                {
                    // for anything which was previously known but not part of list
                    // send a deleted notification to clear any observer caches
                    var item = new TResource()
                    {
                        ApiVersion = _names.GroupApiVersion,
                        Kind       = _names.Kind,
                        Metadata   = new V1ObjectMeta(
                            name: key.Name,
                            namespaceProperty: key.Namespace,
                            ownerReferences: value),
                    };

                    InvokeRegistrationCallbacks(WatchEventType.Deleted, item);
                }

                // keep track of values needed for next page and to start watching
                _lastResourceVersion = list.ResourceVersion();
                continueParameter    = list.Continue();
            }while (!string.IsNullOrEmpty(continueParameter));

            Logger.LogInformation(
                EventId(EventType.SynchronizeComplete),
                "Completed synchronizing {ResourceType} resources from API server.",
                typeof(TResource).Name);
        }
示例#16
0
 public bool TryGetReconcileData(NamespacedName key, out ReconcileData data)
 {
     return(Namespace(key.Namespace).TryLookup(key, out data));
 }
 /// <summary>
 /// Called by the informer with real-time resource updates.
 /// </summary>
 /// <param name="eventType">Indicates if the resource new, updated, or deleted.</param>
 /// <param name="resource">The information as provided by the Kubernets API server.</param>
 private void Notification(WatchEventType eventType, V1Ingress resource)
 {
     _cache.Update(eventType, resource);
     _queue.Add(new QueueItem(NamespacedName.From(resource), null));
 }
示例#18
0
 public bool TryGetReconcileData(NamespacedName key, out ReconcileData data)
 {
     throw new NotImplementedException();
 }