Beispiel #1
0
        public static ModuleRuntimeInfo ConvertToRuntime(this V1Pod pod, string name)
        {
            Option <V1ContainerStatus> containerStatus = GetContainerByName(name, pod);
            ReportedModuleStatus       moduleStatus    = ConvertPodStatusToModuleStatus(containerStatus);
            RuntimeData runtimeData = GetRuntimedata(containerStatus.OrDefault());

            string moduleName = string.Empty;

            if (!(pod.Metadata?.Annotations?.TryGetValue(Constants.K8sEdgeOriginalModuleId, out moduleName) ?? false))
            {
                moduleName = name;
            }

            var reportedConfig = new AgentDocker.DockerReportedConfig(runtimeData.ImageName, string.Empty, string.Empty);

            return(new ModuleRuntimeInfo <AgentDocker.DockerReportedConfig>(
                       ModuleIdentityHelper.GetModuleName(moduleName),
                       "docker",
                       moduleStatus.Status,
                       moduleStatus.Description,
                       runtimeData.ExitStatus,
                       runtimeData.StartTime,
                       runtimeData.EndTime,
                       reportedConfig));
        }
Beispiel #2
0
        V1PodTemplateSpec GetPod(string name, IModuleIdentity identity, KubernetesModule module, IDictionary <string, string> labels)
        {
            // Convert docker labels to annotations because docker labels don't have the same restrictions as Kubernetes labels.
            Dictionary <string, string> annotations = module.Config.CreateOptions.Labels
                                                      .Map(dockerLabels => dockerLabels.ToDictionary(label => KubeUtils.SanitizeAnnotationKey(label.Key), label => label.Value))
                                                      .GetOrElse(() => new Dictionary <string, string>());

            annotations[KubernetesConstants.K8sEdgeOriginalModuleId] = ModuleIdentityHelper.GetModuleName(identity.ModuleId);

            var(proxyContainer, proxyVolumes)   = this.PrepareProxyContainer(module);
            var(moduleContainer, moduleVolumes) = this.PrepareModuleContainer(name, identity, module);

            Option <List <V1LocalObjectReference> > imageSecret = module.Config.AuthConfig
                                                                  .Map(auth => new List <V1LocalObjectReference> {
                new V1LocalObjectReference(auth.Name)
            });

            Option <IDictionary <string, string> > nodeSelector = Option.Maybe(module.Config.CreateOptions).FlatMap(options => options.NodeSelector);

            var modulePodSpec = new V1PodSpec(
                containers: new List <V1Container> {
                proxyContainer, moduleContainer
            },
                volumes: proxyVolumes.Concat(moduleVolumes).ToList(),
                imagePullSecrets: imageSecret.OrDefault(),
                serviceAccountName: name,
                nodeSelector: nodeSelector.OrDefault());

            var objectMeta = new V1ObjectMeta(name: name, labels: labels, annotations: annotations);

            return(new V1PodTemplateSpec(objectMeta, modulePodSpec));
        }
        V1PodTemplateSpec GetPod(string name, IModuleIdentity identity, KubernetesModule module, IDictionary <string, string> labels)
        {
            List <V1EnvVar> envVars = this.CollectEnv(module, identity);

            // Convert docker labels to annotations because docker labels don't have the same restrictions as Kubernetes labels.
            var annotations = Option.Maybe(module.Config.CreateOptions?.Labels)
                              .Map(dockerLabels => dockerLabels.ToDictionary(label => KubeUtils.SanitizeAnnotationKey(label.Key), label => label.Value))
                              .GetOrElse(() => new Dictionary <string, string>());

            annotations[KubernetesConstants.K8sEdgeOriginalModuleId] = ModuleIdentityHelper.GetModuleName(identity.ModuleId);

            // Per container settings:
            // exposed ports
            Option <List <V1ContainerPort> > exposedPorts = Option.Maybe(module.Config?.CreateOptions?.ExposedPorts)
                                                            .Map(PortExtensions.GetContainerPorts);

            // privileged container
            Option <V1SecurityContext> securityContext = Option.Maybe(module.Config?.CreateOptions?.HostConfig?.Privileged)
                                                         .Map(config => new V1SecurityContext(privileged: true));

            // Bind mounts
            (List <V1Volume> volumes, List <V1VolumeMount> proxyMounts, List <V1VolumeMount> volumeMounts) = this.GetVolumesFromModule(module);

            var containers = new List <V1Container>
            {
                new V1Container(
                    name,
                    env: envVars,
                    image: module.Config.Image,
                    volumeMounts: volumeMounts,
                    securityContext: securityContext.OrDefault(),
                    ports: exposedPorts.OrDefault()),

                new V1Container(
                    "proxy",
                    env: envVars, // TODO: check these for validity for proxy.
                    image: this.proxyImage,
                    volumeMounts: proxyMounts)
            };

            Option <List <V1LocalObjectReference> > imageSecret = module.Config.AuthConfig.Map(
                auth =>
            {
                var secret   = new ImagePullSecret(auth);
                var authList = new List <V1LocalObjectReference>
                {
                    new V1LocalObjectReference(secret.Name)
                };
                return(authList);
            });

            var modulePodSpec = new V1PodSpec(containers, volumes: volumes, imagePullSecrets: imageSecret.OrDefault(), serviceAccountName: name);

            var objectMeta = new V1ObjectMeta(name: name, labels: labels, annotations: annotations);

            return(new V1PodTemplateSpec(objectMeta, modulePodSpec));
        }
        public V1ServiceAccount CreateServiceAccount(IModuleIdentity identity, IDictionary <string, string> labels)
        {
            string name        = identity.DeploymentName();
            var    annotations = new Dictionary <string, string>
            {
                [KubernetesConstants.K8sEdgeOriginalModuleId] = ModuleIdentityHelper.GetModuleName(identity.ModuleId)
            };
            var metadata = new V1ObjectMeta(annotations, name: name, labels: labels);

            return(new V1ServiceAccount(metadata: metadata));
        }
Beispiel #5
0
        async Task <IImmutableDictionary <string, IModuleIdentity> > GetModuleIdentitiesAsync(Diff diff)
        {
            // System modules have different module names and identity names. We need to convert module names to module identity names
            // and vice versa, to make sure the right values are being used.
            // TODO - This will fail if the user adds modules with the same module name as a system module - for example a module called
            // edgeHub. We might have to catch such cases and flag them as error (or handle them in some other way).
            IEnumerable <string> updatedModuleIdentites = diff.AddedOrUpdated.Select(m => ModuleIdentityHelper.GetModuleIdentityName(m.Name));
            IEnumerable <string> removedModuleIdentites = diff.Removed.Select(m => ModuleIdentityHelper.GetModuleIdentityName(m));

            List <Module> modules = (await this.serviceClient.GetModules()).ToList();

            ImmutableDictionary <string, Module> modulesDict = modules.ToImmutableDictionary(p => p.Id);

            IEnumerable <string> createIdentities = updatedModuleIdentites.Where(m => !modulesDict.ContainsKey(m));
            IEnumerable <string> removeIdentities = removedModuleIdentites.Where(
                m => modulesDict.ContainsKey(m) &&
                string.Equals(modulesDict.GetValueOrDefault(m).ManagedBy, Constants.ModuleIdentityEdgeManagedByValue, StringComparison.OrdinalIgnoreCase));

            // Update any identities that don't have SAS auth type or where the keys are null (this will happen for single device deployments,
            // where the identities of modules are created, but the auth keys are not set).
            IEnumerable <Module> updateIdentities = modules.Where(
                m => m.Authentication == null ||
                m.Authentication.Type != AuthenticationType.Sas ||
                m.Authentication.SymmetricKey == null ||
                (m.Authentication.SymmetricKey.PrimaryKey == null && m.Authentication.SymmetricKey.SecondaryKey == null))
                                                    .Select(
                m =>
            {
                m.Authentication = new AuthenticationMechanism
                {
                    Type = AuthenticationType.Sas
                };
                return(m);
            }).ToList();

            List <Module> updatedModulesIndentity            = (await this.UpdateServiceModulesIdentityAsync(removeIdentities, createIdentities, updateIdentities)).ToList();
            ImmutableDictionary <string, Module> updatedDict = updatedModulesIndentity.ToImmutableDictionary(p => p.Id);

            IEnumerable <IModuleIdentity> moduleIdentities;

            moduleIdentities = updatedModulesIndentity.Concat(modules.Where(p => !updatedDict.ContainsKey(p.Id))).Select(
                p =>
            {
                string connectionString = this.GetModuleConnectionString(p);
                return(new ModuleIdentity(
                           this.iothubHostName,
                           this.deviceId,
                           p.Id,
                           new ConnectionStringCredentials(connectionString)));
            });

            return(moduleIdentities.ToImmutableDictionary(m => ModuleIdentityHelper.GetModuleName(m.ModuleId)));
        }
        V1PodTemplateSpec GetPod(string name, IModuleIdentity identity, KubernetesModule module, IDictionary <string, string> labels)
        {
            // Convert docker labels to annotations because docker labels don't have the same restrictions as Kubernetes labels.
            Dictionary <string, string> annotations = module.Config.CreateOptions.Labels
                                                      .Map(dockerLabels => dockerLabels.ToDictionary(label => KubeUtils.SanitizeAnnotationKey(label.Key), label => label.Value))
                                                      .GetOrElse(() => new Dictionary <string, string>());

            annotations[KubernetesConstants.K8sEdgeOriginalModuleId] = ModuleIdentityHelper.GetModuleName(identity.ModuleId);

            var(proxyContainer, proxyVolumes)   = this.PrepareProxyContainer(module);
            var(moduleContainer, moduleVolumes) = this.PrepareModuleContainer(name, identity, module);
            bool?hostIpc = this.IsHostIpc(module.Config.CreateOptions);

            var imagePullSecrets = new List <Option <string> > {
                this.proxyImagePullSecretName, module.Config.AuthConfig.Map(auth => auth.Name)
            }
            .FilterMap()
            .Distinct()
            .Select(pullSecretName => new V1LocalObjectReference(pullSecretName))
            .ToList();

            V1PodSecurityContext securityContext = module.Config.CreateOptions.SecurityContext.GetOrElse(
                () => this.runAsNonRoot
                    ? new V1PodSecurityContext {
                RunAsNonRoot = true, RunAsUser = 1000
            }
                    : null);

            return(new V1PodTemplateSpec
            {
                Metadata = new V1ObjectMeta
                {
                    Name = name,
                    Labels = labels,
                    Annotations = annotations
                },
                Spec = new V1PodSpec
                {
                    Containers = new List <V1Container> {
                        proxyContainer, moduleContainer
                    },
                    Volumes = proxyVolumes.Concat(moduleVolumes).ToList(),
                    ImagePullSecrets = imagePullSecrets.Any() ? imagePullSecrets : null,
                    SecurityContext = securityContext,
                    ServiceAccountName = name,
                    NodeSelector = module.Config.CreateOptions.NodeSelector.OrDefault(),
                    HostIPC = hostIpc,
                }
            });
        }
        public async Task <IImmutableDictionary <string, IModuleIdentity> > GetModuleIdentitiesAsync(ModuleSet desired, ModuleSet current)
        {
            Diff diff = desired.Diff(current);

            if (diff.IsEmpty)
            {
                return(ImmutableDictionary <string, IModuleIdentity> .Empty);
            }

            IList <string>       updatedModuleNames = diff.Updated.Select(m => ModuleIdentityHelper.GetModuleIdentityName(m.Name)).ToList();
            IEnumerable <string> removedModuleNames = diff.Removed.Select(m => ModuleIdentityHelper.GetModuleIdentityName(m));

            IImmutableDictionary <string, Identity> identities = (await this.identityManager.GetIdentities()).ToImmutableDictionary(i => i.ModuleId);

            // Create identities for all modules that are in the deployment but aren't in iotedged.
            IEnumerable <string> createIdentities = updatedModuleNames.Where(m => !identities.ContainsKey(m));

            // Update identities for all modules that are in the deployment and are in iotedged (except for Edge Agent which gets special
            // treatment in iotedged).
            //
            // NOTE: This update can potentiatlly be made more efficient by checking that an update is actually needed, i.e. if auth type
            // is not SAS and/or if the credentials are not what iotedged expects it to be.
            IEnumerable <Identity> updateIdentities = updatedModuleNames
                                                      .Where(m => identities.ContainsKey(m) && m != Constants.EdgeAgentModuleIdentityName)
                                                      .Select(m => identities[m]);

            // Remove identities which exist in iotedged but don't exist in the deployment anymore. We exclude however, identities that
            // aren't managed by Edge since these have been created by some out-of-band process and Edge doesn't "own" the identity.
            IEnumerable <string> removeIdentities = removedModuleNames.Where(m => identities.ContainsKey(m) &&
                                                                             Constants.ModuleIdentityEdgeManagedByValue.Equals(identities[m].ManagedBy, StringComparison.OrdinalIgnoreCase));

            // First remove identities (so that we don't go over the IoTHub limit).
            await Task.WhenAll(removeIdentities.Select(i => this.identityManager.DeleteIdentityAsync(i)));

            // Create/update identities.
            IEnumerable <Task <Identity> > createTasks = createIdentities.Select(i => this.identityManager.CreateIdentityAsync(i, Constants.ModuleIdentityEdgeManagedByValue));
            IEnumerable <Task <Identity> > updateTasks = updateIdentities.Select(i => this.identityManager.UpdateIdentityAsync(i.ModuleId, i.GenerationId, i.ManagedBy));

            Identity[] upsertedIdentities = await Task.WhenAll(createTasks.Concat(updateTasks));

            List <IModuleIdentity> moduleIdentities = upsertedIdentities.Select(m => this.GetModuleIdentity(m)).ToList();

            // Add the module identity for Edge Agent in the returned dictionary
            // because is was excluded from the update call.
            if (identities.ContainsKey(Constants.EdgeAgentModuleIdentityName))
            {
                moduleIdentities.Add(this.GetModuleIdentity(identities[Constants.EdgeAgentModuleIdentityName]));
            }
            return(moduleIdentities.ToImmutableDictionary(m => ModuleIdentityHelper.GetModuleName(m.ModuleId)));
        }