예제 #1
0
        public V1Service GetServiceByNameAndNamespace(string ServiceName, string _Namespace)
        {
            try
            {
                V1ServiceList ServiceList = KClient.ListNamespacedService(_Namespace);

                if (ServiceList != null)
                {
                    for (int i = 0; i < ServiceList.Items.Count; ++i)
                    {
                        if (ServiceList.Items[i].Name() == ServiceName)
                        {
                            return(ServiceList.Items[i]);
                        }
                    }
                }

                return(null);
            }
            catch (Exception ex)
            {
                //Return null if pod could not be found and throw exception when connection error occurs
                throw ex;
            }
        }
예제 #2
0
        private static void AddServiceResult(Mock <IKubernetes> mock, V1ServiceList results, string serviceName = null)
        {
            // If we have a specific service name, this will restrict the method to that
            var expectedFilter = $"service-name={serviceName}";
            var result         = new HttpOperationResponse <V1ServiceList>()
            {
                Body = results
            };

            // Kubernetes client uses extension methods to find services, this is the acutal method that will finally be called
            mock.Setup(m =>
                       m.ListNamespacedServiceWithHttpMessagesAsync(It.IsAny <string>(),
                                                                    It.IsAny <bool?>(),
                                                                    It.IsAny <string>(),
                                                                    It.IsAny <string>(),
                                                                    It.Is <string>(s => string.IsNullOrEmpty(serviceName) || s == expectedFilter),
                                                                    It.IsAny <int?>(),
                                                                    It.IsAny <string>(),
                                                                    It.IsAny <int?>(),
                                                                    It.IsAny <bool?>(),
                                                                    It.IsAny <string>(),
                                                                    It.IsAny <Dictionary <string, List <string> > >(),
                                                                    It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(result));
        }
예제 #3
0
        private Dictionary <string, string> GetCurrentServiceConfig(V1ServiceList currentServices)
        {
            return(currentServices.Items.ToDictionary(
                       service =>
            {
                if (service?.Metadata?.Name != null)
                {
                    return service.Metadata.Name;
                }

                Events.InvalidCreationString("service", "null service");
                throw new NullReferenceException("null service in list");
            },
                       service =>
            {
                if (service == null)
                {
                    Events.InvalidCreationString("service", "null service");
                    throw new NullReferenceException("null service in list");
                }

                if (service.Metadata?.Annotations != null &&
                    service.Metadata.Annotations.TryGetValue(Constants.CreationString, out string creationString))
                {
                    return creationString;
                }

                Events.InvalidCreationString(service.Kind, service.Metadata?.Name);

                var serviceWithoutStatus = new V1Service(service.ApiVersion, service.Kind, service.Metadata, service.Spec);
                return JsonConvert.SerializeObject(serviceWithoutStatus);
            }));
        }
예제 #4
0
        public async Task PurgeModulesAsync()
        {
            // Delete all services for current edge deployment
            V1ServiceList services = await this.client.ListNamespacedServiceAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            var serviceTasks = services.Items
                               .Select(service => this.client.DeleteNamespacedServiceAsync(service.Metadata.Name, this.deviceNamespace, new V1DeleteOptions()));
            await Task.WhenAll(serviceTasks);

            // Delete all deployments for current edge deployment
            V1DeploymentList deployments = await this.client.ListNamespacedDeploymentAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            var deploymentTasks = deployments.Items
                                  .Select(
                deployment => this.client.DeleteNamespacedDeploymentAsync(
                    deployment.Metadata.Name,
                    this.deviceNamespace,
                    new V1DeleteOptions(propagationPolicy: KubernetesConstants.DefaultDeletePropagationPolicy),
                    propagationPolicy: KubernetesConstants.DefaultDeletePropagationPolicy));
            await Task.WhenAll(deploymentTasks);

            V1PersistentVolumeClaimList pvcs = await this.client.ListNamespacedPersistentVolumeClaimAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            var pvcTasks = pvcs.Items
                           .Select(pvc => this.client.DeleteNamespacedPersistentVolumeClaimAsync(pvc.Metadata.Name, this.deviceNamespace, new V1DeleteOptions()));
            await Task.WhenAll(pvcTasks);

            // Delete the service account for all deployments
            V1ServiceAccountList serviceAccounts = await this.client.ListNamespacedServiceAccountAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            var serviceAccountTasks = serviceAccounts.Items
                                      .Select(service => this.client.DeleteNamespacedServiceAsync(service.Metadata.Name, this.deviceNamespace, new V1DeleteOptions()));
            await Task.WhenAll(serviceAccountTasks);
        }
예제 #5
0
        public async Task PurgeModulesAsync()
        {
            // Delete all services for current edge deployment
            V1ServiceList services = await this.client.ListNamespacedServiceAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            var serviceTasks = services.Items
                               .Select(service => this.client.DeleteNamespacedServiceAsync(service.Metadata.Name, this.deviceNamespace, new V1DeleteOptions()));
            await Task.WhenAll(serviceTasks);

            // Delete all deployments for current edge deployment
            V1DeploymentList deployments = await this.client.ListNamespacedDeploymentAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            var deploymentTasks = deployments.Items
                                  .Select(
                deployment => this.client.DeleteNamespacedDeployment1Async(
                    deployment.Metadata.Name,
                    this.deviceNamespace,
                    new V1DeleteOptions(propagationPolicy: "Foreground"),
                    propagationPolicy: "Foreground"));
            await Task.WhenAll(deploymentTasks);

            // Delete the service account for all deployments
            V1ServiceAccountList serviceAccounts = await this.client.ListNamespacedServiceAccountAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            var serviceAccountTasks = serviceAccounts.Items
                                      .Select(service => this.client.DeleteNamespacedServiceAsync(service.Metadata.Name, this.deviceNamespace, new V1DeleteOptions()));
            await Task.WhenAll(serviceAccountTasks);
        }
예제 #6
0
        public async Task <ModuleSet> DeployModulesAsync(IReadOnlyList <KubernetesModule> modules, ModuleSet currentModules)
        {
            var desiredModules   = ModuleSet.Create(modules.ToArray());
            var moduleIdentities = await this.moduleIdentityLifecycleManager.GetModuleIdentitiesAsync(desiredModules, currentModules);

            var labels = modules
                         .ToDictionary(
                module => module.Name,
                module => new Dictionary <string, string>
            {
                [KubernetesConstants.K8sEdgeModuleLabel]  = moduleIdentities[module.Name].DeploymentName(),
                [KubernetesConstants.K8sEdgeDeviceLabel]  = KubeUtils.SanitizeLabelValue(this.resourceName.DeviceId),
                [KubernetesConstants.K8sEdgeHubNameLabel] = KubeUtils.SanitizeLabelValue(this.resourceName.Hostname)
            });
            var deviceOnlyLabels = new Dictionary <string, string>
            {
                [KubernetesConstants.K8sEdgeDeviceLabel]  = KubeUtils.SanitizeLabelValue(this.resourceName.DeviceId),
                [KubernetesConstants.K8sEdgeHubNameLabel] = KubeUtils.SanitizeLabelValue(this.resourceName.Hostname)
            };

            var desiredServices = modules
                                  .Select(module => this.serviceMapper.CreateService(moduleIdentities[module.Name], module, labels[module.Name]))
                                  .FilterMap()
                                  .ToList();

            V1ServiceList currentServices = await this.client.ListNamespacedServiceAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            await this.ManageServices(currentServices, desiredServices);

            var desiredDeployments = modules
                                     .Select(module => this.deploymentMapper.CreateDeployment(moduleIdentities[module.Name], module, labels[module.Name]))
                                     .ToList();

            V1DeploymentList currentDeployments = await this.client.ListNamespacedDeploymentAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            await this.ManageDeployments(currentDeployments, desiredDeployments);

            var desiredPvcs = modules
                              .Select(module => this.pvcMapper.CreatePersistentVolumeClaims(module, deviceOnlyLabels))
                              .FilterMap()
                              .SelectMany(x => x)
                              .Distinct(KubernetesPvcByValueEqualityComparer);

            // Modules may use PVCs created by the user, we get all PVCs and then work on ours.
            V1PersistentVolumeClaimList currentPvcList = await this.client.ListNamespacedPersistentVolumeClaimAsync(this.deviceNamespace);

            await this.ManagePvcs(currentPvcList, desiredPvcs);

            var desiredServiceAccounts = modules
                                         .Select(module => this.serviceAccountMapper.CreateServiceAccount(moduleIdentities[module.Name], labels[module.Name]))
                                         .ToList();

            V1ServiceAccountList currentServiceAccounts = await this.client.ListNamespacedServiceAccountAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            await this.ManageServiceAccounts(currentServiceAccounts, desiredServiceAccounts);

            return(desiredModules);
        }
예제 #7
0
        public async Task <IActionResult> GetNamespace([FromRoute] string namespaceName)
        {
            V1Namespace @namespace = await this.kubernetesClient.ReadNamespaceAsync(namespaceName);

            V1ServiceList services = await this.kubernetesClient.ListNamespacedServiceAsync(namespaceParameter : namespaceName, labelSelector : "application=openbrisk");

            return(this.Ok(new NamespaceInfo
            {
                Name = @namespace.Metadata.Name,
                FunctionCount = services.Items.Count,
            }));
        }
예제 #8
0
        public async Task <IActionResult> GetFunctions([FromRoute] string namespaceName)
        {
            V1ServiceList services = await this.kubernetesClient.ListNamespacedServiceAsync(namespaceName, labelSelector : "application=openbrisk");

            return(this.Ok(services.Items.Select(x => new FunctionInfo
            {
                Name = x.Metadata.Name,
                Namespace = x.Metadata.NamespaceProperty,
                Runtime = "",
                Version = 0,
                ReplicaCount = x.Spec.Ports.Count,
                InvocationCount = 0,
            })));
        }
예제 #9
0
        public void TestKubernetesReturnsService()
        {
            const string expectedUrl        = "http://127.0.0.1:80";
            const string serviceName        = "my-service-working";
            const string missingServiceName = "my-service-not-found";

            var clusterResult = new V1ServiceList()
            {
                Items = new List <V1Service>()
                {
                    new V1Service()
                    {
                        Spec = new V1ServiceSpec()
                        {
                            ClusterIP = "127.0.0.1",
                            Ports     = new List <V1ServicePort>()
                            {
                                new V1ServicePort(80, "http")
                            }
                        }
                    }
                }
            };
            var mockClient = new Mock <IKubernetes>();

            // and a service result for other service names
            AddServiceResult(mockClient, new V1ServiceList());
            // Add explicit service result
            AddServiceResult(mockClient, clusterResult, serviceName);


            CreateMockFactory(mockClient);

            var resolver = serviceCollection.BuildServiceProvider().GetService <IServiceResolver>() as KubernetesServiceResolver;

            Assert.NotNull(resolver);

            var result = resolver.ResolveService(serviceName).Result;

            Assert.Equal(expectedUrl, result);

            // Ensure that a service doesn't exist has no results
            var noResult = resolver.ResolveService(missingServiceName).Result;

            Assert.Null(noResult);

            VerifyKubernetesServiceSearch(mockClient, serviceName);
            VerifyKubernetesServiceSearch(mockClient, missingServiceName);
        }
예제 #10
0
        private async Task WatchDeploymentEventsAsync(WatchEventType type, object custom)
        {
            EdgeDeploymentDefinition <TConfig> edgeDeploymentDefinition;

            try
            {
                string customString = JsonConvert.SerializeObject(custom);
                edgeDeploymentDefinition = this.deploymentSerde.Deserialize(customString);
            }
            catch (Exception e)
            {
                Events.EdgeDeploymentDeserializeFail(e);
                return;
            }

            // only operate on the device that matches this operator.
            if (string.Equals(edgeDeploymentDefinition.Metadata.Name, this.resourceName, StringComparison.OrdinalIgnoreCase))
            {
                using (await this.watchLock.LockAsync())
                {
                    V1ServiceList currentServices = await this.client.ListNamespacedServiceAsync(this.k8sNamespace, labelSelector : this.deploymentSelector);

                    V1DeploymentList currentDeployments = await this.client.ListNamespacedDeploymentAsync(this.k8sNamespace, labelSelector : this.deploymentSelector);

                    Events.DeploymentStatus(type, this.resourceName);
                    switch (type)
                    {
                    case WatchEventType.Added:
                    case WatchEventType.Modified:
                        await this.ManageDeployments(currentServices, currentDeployments, edgeDeploymentDefinition);

                        break;

                    case WatchEventType.Deleted:
                        await this.DeleteDeployments(currentServices, currentDeployments);

                        break;

                    case WatchEventType.Error:
                        Events.DeploymentError();
                        break;
                    }
                }
            }
            else
            {
                Events.DeploymentNameMismatch(edgeDeploymentDefinition.Metadata.Name, this.resourceName);
            }
        }
예제 #11
0
        private async Task DeleteDeployments(V1ServiceList currentServices, V1DeploymentList currentDeployments)
        {
            // Delete the deployment.
            // Delete any services.
            IEnumerable <Task <V1Status> > removeServiceTasks = currentServices.Items.Select(i => this.client.DeleteNamespacedServiceAsync(i.Metadata.Name, this.k8sNamespace, new V1DeleteOptions()));
            await Task.WhenAll(removeServiceTasks);

            IEnumerable <Task <V1Status> > removeDeploymentTasks = currentDeployments.Items.Select(
                d => this.client.DeleteNamespacedDeployment1Async(
                    d.Metadata.Name,
                    this.k8sNamespace,
                    new V1DeleteOptions(propagationPolicy: "Foreground"),
                    propagationPolicy: "Foreground"));
            await Task.WhenAll(removeDeploymentTasks);

            this.currentModules = ModuleSet.Empty;
        }
예제 #12
0
        public async Task <ModuleSet> DeployModulesAsync(IList <KubernetesModule> modules, ModuleSet currentModules)
        {
            var desiredModules   = ModuleSet.Create(modules.ToArray());
            var moduleIdentities = await this.moduleIdentityLifecycleManager.GetModuleIdentitiesAsync(desiredModules, currentModules);

            var labels = modules
                         .ToDictionary(
                module => module.Name,
                module => new Dictionary <string, string>
            {
                [KubernetesConstants.K8sEdgeModuleLabel]  = moduleIdentities[module.Name].DeploymentName(),
                [KubernetesConstants.K8sEdgeDeviceLabel]  = KubeUtils.SanitizeLabelValue(this.resourceName.DeviceId),
                [KubernetesConstants.K8sEdgeHubNameLabel] = KubeUtils.SanitizeLabelValue(this.resourceName.Hostname)
            });

            var desiredServices = modules
                                  .Select(module => this.serviceMapper.CreateService(moduleIdentities[module.Name], module, labels[module.Name]))
                                  .Where(service => service.HasValue)
                                  .Select(service => service.OrDefault())
                                  .ToList();

            V1ServiceList currentServices = await this.client.ListNamespacedServiceAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            await this.ManageServices(currentServices, desiredServices);

            var desiredDeployments = modules
                                     .Select(module => this.deploymentMapper.CreateDeployment(moduleIdentities[module.Name], module, labels[module.Name]))
                                     .ToList();

            V1DeploymentList currentDeployments = await this.client.ListNamespacedDeploymentAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            await this.ManageDeployments(currentDeployments, desiredDeployments);

            var desiredServiceAccounts = modules
                                         .Select(module => this.serviceAccountMapper.CreateServiceAccount(moduleIdentities[module.Name], labels[module.Name]))
                                         .ToList();

            V1ServiceAccountList currentServiceAccounts = await this.client.ListNamespacedServiceAccountAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

            await this.ManageServiceAccounts(currentServiceAccounts, desiredServiceAccounts);

            return(desiredModules);
        }
예제 #13
0
        public void TestKubernetesNoServicesMatch()
        {
            const string serviceName = "my-service";

            var clusterResult = new V1ServiceList();
            var mockClient    = new Mock <IKubernetes>();

            AddServiceResult(mockClient, clusterResult);

            CreateMockFactory(mockClient);

            var resolver = serviceCollection.BuildServiceProvider().GetService <IServiceResolver>() as KubernetesServiceResolver;

            Assert.NotNull(resolver);

            var result = resolver.ResolveService(serviceName).Result;

            Assert.Null(result);

            // Verify that we searched for a service with the name expected
            VerifyKubernetesServiceSearch(mockClient, serviceName);
        }
예제 #14
0
        public void TestKubernetesReturnsNoServiceWithoutPort80()
        {
            const string serviceName = "my-service-but-no-http";

            var clusterResult = new V1ServiceList()
            {
                Items = new List <V1Service>()
                {
                    new V1Service()
                    {
                        Metadata = new V1ObjectMeta()
                        {
                            Name = "Service without http"
                        },
                        Spec = new V1ServiceSpec()
                        {
                            ClusterIP = "127.0.0.1",
                            Ports     = new List <V1ServicePort>()
                            {
                                new V1ServicePort(81, "tcp") // this isn't an http port, so it wont be returned
                            }
                        }
                    }
                }
            };
            var mockClient = new Mock <IKubernetes>();

            AddServiceResult(mockClient, clusterResult, serviceName);

            CreateMockFactory(mockClient);

            var resolver = serviceCollection.BuildServiceProvider().GetService <IServiceResolver>() as KubernetesServiceResolver;

            Assert.NotNull(resolver);

            var result = resolver.ResolveService(serviceName).Result;

            Assert.Null(result);
        }
예제 #15
0
        public void CleanLab(IKubernetes client, string userName)
        {
            var                          dList = client.ListNamespacedDeployment(userName);
            V1ServiceList                sList = null;
            V1NetworkPolicyList          nList = null;
            Networkingv1beta1IngressList iList = null;

            if (dList != null && dList.Items.Count > 0)
            {
                sList = client.ListNamespacedService(userName);
                nList = client.ListNamespacedNetworkPolicy(userName);
                iList = client.ListNamespacedIngress1(userName);
                foreach (var item in dList.Items)
                {
                    client.DeleteNamespacedDeployment(item.Metadata.Name, userName);
                }
            }
            if (sList != null && sList.Items.Count > 0)
            {
                foreach (var item in sList.Items)
                {
                    client.DeleteNamespacedService(item.Metadata.Name, userName);
                }
            }
            if (iList != null && iList.Items.Count > 0)
            {
                foreach (var item in iList.Items)
                {
                    client.DeleteNamespacedIngress1(item.Metadata.Name, userName);
                }
            }
            if (nList != null && nList.Items.Count > 0)
            {
                foreach (var item in nList.Items)
                {
                    client.DeleteNamespacedNetworkPolicy(item.Metadata.Name, userName);
                }
            }
        }
예제 #16
0
        async Task ManageServices(V1ServiceList existing, IEnumerable <V1Service> desired)
        {
            // find difference between desired and existing services
            var diff = FindServiceDiff(desired, existing.Items);

            // Update only those services if configurations have not matched
            var updatingTask = diff.Updated
                               .Select(
                update =>
            {
                Events.UpdateService(update.To);

                this.serviceMapper.UpdateService(update.To, update.From);
                return(this.client.ReplaceNamespacedServiceAsync(update.To, update.To.Metadata.Name, this.deviceNamespace));
            });
            await Task.WhenAll(updatingTask);

            // Delete all existing services that are not in desired list
            var removingTasks = diff.Removed
                                .Select(
                name =>
            {
                Events.DeleteService(name);
                return(this.client.DeleteNamespacedServiceAsync(name, this.deviceNamespace));
            });
            await Task.WhenAll(removingTasks);

            // Create new desired services
            var addingTasks = diff.Added
                              .Select(
                service =>
            {
                Events.CreateService(service);
                return(this.client.CreateNamespacedServiceAsync(service, this.deviceNamespace));
            });
            await Task.WhenAll(addingTasks);
        }
예제 #17
0
        private async void AssertNoMatchingService(string deviceSelector, string moduleName)
        {
            V1ServiceList currentServiceList = await this.client.ListServicesAsync(deviceSelector);

            Assert.Single(currentServiceList.Items, s => s.Metadata.Name == moduleName);
        }
예제 #18
0
        private async void AssertNoServicesExist(string deviceSelector)
        {
            V1ServiceList currentServices = await this.client.ListServicesAsync(deviceSelector);

            Assert.Empty(currentServices.Items);
        }
예제 #19
0
        public async Task <EdgeDeploymentStatus> DeployModulesAsync(ModuleSet desiredModules, ModuleSet currentModules)
        {
            try
            {
                var moduleIdentities = await this.moduleIdentityLifecycleManager.GetModuleIdentitiesAsync(desiredModules, currentModules);

                // having desired modules an no module identities means that we are unable to obtain a list of module identities
                if (desiredModules.Modules.Any() && !moduleIdentities.Any())
                {
                    Events.NoModuleIdentities();
                    return(EdgeDeploymentStatus.Failure("Unable to obtain identities for desired modules"));
                }

                var labels = desiredModules.Modules
                             .ToDictionary(
                    module => module.Key,
                    module => new Dictionary <string, string>
                {
                    [KubernetesConstants.K8sEdgeModuleLabel]  = moduleIdentities[module.Key].DeploymentName(),
                    [KubernetesConstants.K8sEdgeDeviceLabel]  = KubeUtils.SanitizeLabelValue(this.resourceName.DeviceId),
                    [KubernetesConstants.K8sEdgeHubNameLabel] = KubeUtils.SanitizeLabelValue(this.resourceName.Hostname)
                });
                var deviceOnlyLabels = new Dictionary <string, string>
                {
                    [KubernetesConstants.K8sEdgeDeviceLabel]  = KubeUtils.SanitizeLabelValue(this.resourceName.DeviceId),
                    [KubernetesConstants.K8sEdgeHubNameLabel] = KubeUtils.SanitizeLabelValue(this.resourceName.Hostname)
                };

                var desiredServices = desiredModules.Modules
                                      .Select(module => this.serviceMapper.CreateService(moduleIdentities[module.Key], (KubernetesModule)module.Value, labels[module.Key]))
                                      .FilterMap()
                                      .ToList();

                V1ServiceList currentServices = await this.client.ListNamespacedServiceAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

                await this.ManageServices(currentServices, desiredServices);

                var desiredDeployments = desiredModules.Modules
                                         .Select(module => this.deploymentMapper.CreateDeployment(moduleIdentities[module.Key], (KubernetesModule)module.Value, labels[module.Key]))
                                         .ToList();

                V1DeploymentList currentDeployments = await this.client.ListNamespacedDeploymentAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

                await this.ManageDeployments(currentDeployments, desiredDeployments);

                var desiredPvcs = desiredModules.Modules
                                  .Select(module => this.pvcMapper.CreatePersistentVolumeClaims((KubernetesModule)module.Value, deviceOnlyLabels))
                                  .FilterMap()
                                  .SelectMany(x => x)
                                  .Distinct(KubernetesPvcByValueEqualityComparer);

                // Modules may use PVCs created by the user, we get all PVCs and then work on ours.
                V1PersistentVolumeClaimList currentPvcList = await this.client.ListNamespacedPersistentVolumeClaimAsync(this.deviceNamespace);

                await this.ManagePvcs(currentPvcList, desiredPvcs);

                var desiredServiceAccounts = desiredModules.Modules
                                             .Select(module => this.serviceAccountMapper.CreateServiceAccount((KubernetesModule)module.Value, moduleIdentities[module.Key], labels[module.Key]))
                                             .ToList();

                V1ServiceAccountList currentServiceAccounts = await this.client.ListNamespacedServiceAccountAsync(this.deviceNamespace, labelSelector : this.deploymentSelector);

                await this.ManageServiceAccounts(currentServiceAccounts, desiredServiceAccounts);

                return(EdgeDeploymentStatus.Success("Successfully deployed"));
            }
            catch (HttpOperationException e)
            {
                Events.DeployModulesException(e);
                return(EdgeDeploymentStatus.Failure(e));
            }
        }
예제 #20
0
        public void TestKubernetesReturnsServiceWithPort80()
        {
            const string expectedUrl = "http://my-host:80";
            const string serviceName = "my-service-non-standard-port";
            // Define 3 services
            // one has the wrong port defined, needs 80
            // one has the right port, but no ClusterIP (we only support them at the moment)
            // the last one is correct
            var clusterResult = new V1ServiceList()
            {
                Items = new List <V1Service>()
                {
                    // Bad Service
                    new V1Service()
                    {
                        Metadata = new V1ObjectMeta()
                        {
                            Name = "Wrong Service",
                        },
                        Spec = new V1ServiceSpec()
                        {
                            ClusterIP = "127.0.0.1",
                            Ports     = new List <V1ServicePort>()
                            {
                                new V1ServicePort(242, "ssh")
                            }
                        }
                    },
                    new V1Service()
                    {
                        Metadata = new V1ObjectMeta()
                        {
                            Name = "Right Service but no cluster IP",
                        },
                        Spec = new V1ServiceSpec()
                        {
                            LoadBalancerIP = "my-host",
                            Ports          = new List <V1ServicePort>()
                            {
                                new V1ServicePort(80, "tcp"),
                            }
                        }
                    },
                    new V1Service()
                    {
                        Metadata = new V1ObjectMeta()
                        {
                            Name = "Right Service",
                        },
                        Spec = new V1ServiceSpec()
                        {
                            ClusterIP = "my-host",
                            Ports     = new List <V1ServicePort>()
                            {
                                new V1ServicePort(22, "ssh"), // we shouldn't use this port
                                new V1ServicePort(80, "tcp"),
                            }
                        }
                    }
                }
            };
            var mockClient = new Mock <IKubernetes>();

            AddServiceResult(mockClient, clusterResult, serviceName);

            CreateMockFactory(mockClient);

            var resolver = serviceCollection.BuildServiceProvider().GetService <IServiceResolver>() as KubernetesServiceResolver;

            Assert.NotNull(resolver);

            var result = resolver.ResolveService(serviceName).Result;

            Assert.Equal(expectedUrl, result);
            VerifyKubernetesServiceSearch(mockClient, serviceName);
        }
예제 #21
0
        public Task <string> ResolveService(string serviceName)
        {
            var labelFilter = $"{LABEL_FILTER_NAME}={serviceName}";

            // Are we configured? if not we won't have setup the client
            if (string.IsNullOrEmpty(_kubernetesNamespace) || _kubernetesClient == null)
            {
                return(Task.FromResult <string>(null));
            }

            // Attempt to get the list of services in our namespace that match our label
            // We must use our Namespace as there could more than one of the service in our cluster, across namespaces
            // E.g Alpha and Dev pods are hosted in the same cluster
            V1ServiceList list = null;

            try
            {
                list = _kubernetesClient.ListNamespacedService(_kubernetesNamespace, labelSelector: labelFilter);
            }
            catch (HttpOperationException e)
            {
                // If we don't have access to query the namespace (e.g default), we will get a forbidden exception
                _log.LogWarning($"Failed to query cluster for service {serviceName} due to error. Returning empty result. Error: {e.Message}");
                return(Task.FromResult <string>(null));
            }

            if (list?.Items == null || list.Items.Count == 0)
            {
                return(Task.FromResult <string>(null));
            }

            foreach (var item in list.Items)
            {
                // We require the kubernetes service to define an HTTP Port, and have a CLUSTER IP specified.
                // Any service without both of these will be ignored.
                var spec     = item.Spec;
                var httpPort = item
                               .Spec
                               .Ports
                               .FirstOrDefault(p => p.Port == PORT_NUMBER);

                if (httpPort == null)
                {
                    _log.LogWarning($"Could not find Port {PORT_NUMBER} for the service '{item.Metadata?.Name}' - ignoring");
                }
                else if (string.IsNullOrEmpty(item.Spec.ClusterIP))
                {
                    _log.LogWarning($"No clusterIP provided for service {item.Metadata.Name} - ignoring");
                }
                else
                {
                    // First one found that matches
                    var url = $"http://{spec.ClusterIP}:{httpPort.Port}";
                    _log.LogInformation($"Found '{url}' for the service name '{serviceName}' from kubernetes");
                    return(Task.FromResult(url));
                }
            }

            // No results
            return(Task.FromResult <string>(null));
        }
예제 #22
0
        private async Task ManageDeployments(V1ServiceList currentServices, V1DeploymentList currentDeployments, EdgeDeploymentDefinition <TConfig> customObject)
        {
            var desiredModules   = ModuleSet.Create(customObject.Spec.ToArray());
            var moduleIdentities = await this.moduleIdentityLifecycleManager.GetModuleIdentitiesAsync(desiredModules, this.currentModules);

            // Pull current configuration from annotations.
            Dictionary <string, string> currentV1ServicesFromAnnotations = this.GetCurrentServiceConfig(currentServices);
            // strip out edgeAgent so edgeAgent doesn't update itself.
            // TODO: remove this filter.
            var agentDeploymentName = this.DeploymentName(CoreConstants.EdgeAgentModuleName);
            Dictionary <string, string> currentDeploymentsFromAnnotations = this.GetCurrentDeploymentConfig(currentDeployments)
                                                                            .ToDictionary(
                pair => pair.Key,
                pair => pair.Value);

            var desiredServices    = new List <V1Service>();
            var desiredDeployments = new List <V1Deployment>();

            foreach (KubernetesModule <TConfig> module in customObject.Spec)
            {
                var moduleId = moduleIdentities[module.Name];
                if (string.Equals(module.Type, "docker"))
                {
                    // Default labels
                    var labels = new Dictionary <string, string>
                    {
                        [Constants.K8sEdgeModuleLabel]  = KubeUtils.SanitizeLabelValue(moduleId.ModuleId),
                        [Constants.K8sEdgeDeviceLabel]  = KubeUtils.SanitizeLabelValue(this.deviceId),
                        [Constants.K8sEdgeHubNameLabel] = KubeUtils.SanitizeLabelValue(this.iotHubHostname)
                    };

                    // Create a Service for every network interface of each module. (label them with hub, device and module id)
                    Option <V1Service> moduleService = this.GetServiceFromModule(labels, module, moduleId);
                    moduleService.ForEach(service => desiredServices.Add(service));

                    // Create a Pod for each module, and a proxy container.
                    V1PodTemplateSpec v1PodSpec = this.GetPodFromModule(labels, module, moduleId);

                    // if this is the edge agent's deployment then it needs to run under a specific service account
                    if (moduleIdentities[module.Name].ModuleId == CoreConstants.EdgeAgentModuleIdentityName)
                    {
                        v1PodSpec.Spec.ServiceAccountName = this.serviceAccountName;
                    }

                    // Bundle into a deployment
                    string deploymentName = this.DeploymentName(moduleIdentities[module.Name].ModuleId);
                    // Deployment data
                    var deploymentMeta = new V1ObjectMeta(name: deploymentName, labels: labels);

                    var selector       = new V1LabelSelector(matchLabels: labels);
                    var deploymentSpec = new V1DeploymentSpec(replicas: 1, selector: selector, template: v1PodSpec);

                    desiredDeployments.Add(new V1Deployment(metadata: deploymentMeta, spec: deploymentSpec));
                }
                else
                {
                    Events.InvalidModuleType(module);
                }
            }

            // Find current Services/Deployments which need to be removed and updated
            var servicesRemoved = new List <V1Service>(currentServices.Items);

            servicesRemoved.RemoveAll(s => desiredServices.Exists(i => string.Equals(i.Metadata.Name, s.Metadata.Name)));
            var deploymentsRemoved = new List <V1Deployment>(currentDeployments.Items);

            deploymentsRemoved.RemoveAll(
                d =>
            {
                return(desiredDeployments.Exists(i => string.Equals(i.Metadata.Name, d.Metadata.Name)));
            });

            var newServices = new List <V1Service>();

            desiredServices.ForEach(
                s =>
            {
                string creationString = JsonConvert.SerializeObject(s);

                if (currentV1ServicesFromAnnotations.ContainsKey(s.Metadata.Name))
                {
                    string serviceAnnotation = currentV1ServicesFromAnnotations[s.Metadata.Name];
                    // If configuration matches, no need to update service
                    if (string.Equals(serviceAnnotation, creationString))
                    {
                        return;
                    }

                    if (s.Metadata.Annotations == null)
                    {
                        s.Metadata.Annotations = new Dictionary <string, string>();
                    }

                    s.Metadata.Annotations[Constants.CreationString] = creationString;

                    servicesRemoved.Add(s);
                    newServices.Add(s);
                    Events.UpdateService(s.Metadata.Name);
                }
                else
                {
                    if (s.Metadata.Annotations == null)
                    {
                        s.Metadata.Annotations = new Dictionary <string, string>();
                    }

                    s.Metadata.Annotations[Constants.CreationString] = creationString;

                    newServices.Add(s);
                    Events.CreateService(s.Metadata.Name);
                }
            });
            var deploymentsUpdated = new List <V1Deployment>();
            var newDeployments     = new List <V1Deployment>();
            List <V1Deployment> currentDeploymentsList = currentDeployments.Items.ToList();

            desiredDeployments.ForEach(
                d =>
            {
                if (currentDeploymentsFromAnnotations.ContainsKey(d.Metadata.Name))
                {
                    V1Deployment current         = currentDeploymentsList.Find(i => string.Equals(i.Metadata.Name, d.Metadata.Name));
                    string currentFromAnnotation = currentDeploymentsFromAnnotations[d.Metadata.Name];
                    string creationString        = JsonConvert.SerializeObject(d);

                    // If configuration matches, or this is edgeAgent deployment and the images match,
                    // no need to do update deployment
                    if (string.Equals(currentFromAnnotation, creationString) ||
                        (string.Equals(d.Metadata.Name, this.DeploymentName(CoreConstants.EdgeAgentModuleName)) && V1DeploymentEx.ImageEquals(current, d)))
                    {
                        return;
                    }

                    d.Metadata.ResourceVersion = current.Metadata.ResourceVersion;
                    if (d.Metadata.Annotations == null)
                    {
                        var annotations = new Dictionary <string, string>
                        {
                            [Constants.CreationString] = creationString
                        };
                        d.Metadata.Annotations = annotations;
                    }
                    else
                    {
                        d.Metadata.Annotations[Constants.CreationString] = creationString;
                    }

                    deploymentsUpdated.Add(d);
                    Events.UpdateDeployment(d.Metadata.Name);
                }
                else
                {
                    string creationString = JsonConvert.SerializeObject(d);
                    var annotations       = new Dictionary <string, string>
                    {
                        [Constants.CreationString] = creationString
                    };
                    d.Metadata.Annotations = annotations;
                    newDeployments.Add(d);
                    Events.CreateDeployment(d.Metadata.Name);
                }
            });

            // Remove the old
            IEnumerable <Task <V1Status> > removeServiceTasks = servicesRemoved.Select(
                i =>
            {
                Events.DeletingService(i);
                return(this.client.DeleteNamespacedServiceAsync(i.Metadata.Name, this.k8sNamespace, new V1DeleteOptions()));
            });
            await Task.WhenAll(removeServiceTasks);

            IEnumerable <Task <V1Status> > removeDeploymentTasks = deploymentsRemoved.Select(
                d =>
            {
                Events.DeletingDeployment(d);
                return(this.client.DeleteNamespacedDeployment1Async(d.Metadata.Name, this.k8sNamespace, new V1DeleteOptions(propagationPolicy: "Foreground"), propagationPolicy: "Foreground"));
            });
            await Task.WhenAll(removeDeploymentTasks);

            // Create the new.
            IEnumerable <Task <V1Service> > createServiceTasks = newServices.Select(
                s =>
            {
                Events.CreatingService(s);
                return(this.client.CreateNamespacedServiceAsync(s, this.k8sNamespace));
            });
            await Task.WhenAll(createServiceTasks);

            IEnumerable <Task <V1Deployment> > createDeploymentTasks = newDeployments.Select(
                deployment =>
            {
                Events.CreatingDeployment(deployment);
                return(this.client.CreateNamespacedDeploymentAsync(deployment, this.k8sNamespace));
            });
            await Task.WhenAll(createDeploymentTasks);

            // Update the existing - should only do this when different.
            IEnumerable <Task <V1Deployment> > updateDeploymentTasks = deploymentsUpdated.Select(deployment => this.client.ReplaceNamespacedDeploymentAsync(deployment, deployment.Metadata.Name, this.k8sNamespace));
            await Task.WhenAll(updateDeploymentTasks);

            this.currentModules = desiredModules;
        }