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)); }
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)); }
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))); }