internal async static Task <(IEnumerable <IKubernetesResource>, IDictionary <string, string>)> GetFunctionsDeploymentResources(
            string name,
            string imageName,
            string @namespace,
            TriggersPayload triggers,
            IDictionary <string, string> secrets,
            string pullSecret            = null,
            string secretsCollectionName = null,
            string configMapName         = null,
            bool useConfigMap            = false,
            int?pollingInterval          = null,
            int?cooldownPeriod           = null,
            string serviceType           = "LoadBalancer",
            int?minReplicas = null,
            int?maxReplicas = null,
            string keysSecretCollectionName = null,
            bool mountKeysAsContainerVolume = false)
        {
            ScaledObjectV1Alpha1 scaledobject = null;
            var result        = new List <IKubernetesResource>();
            var deployments   = new List <DeploymentV1Apps>();
            var httpFunctions = triggers.FunctionsJson
                                .Where(b => b.Value["bindings"]?.Any(e => e?["type"].ToString().IndexOf("httpTrigger", StringComparison.OrdinalIgnoreCase) != -1) == true);
            var nonHttpFunctions = triggers.FunctionsJson.Where(f => httpFunctions.All(h => h.Key != f.Key));

            keysSecretCollectionName = string.IsNullOrEmpty(keysSecretCollectionName)
                ? $"func-keys-kube-secret-{name}"
                : keysSecretCollectionName;
            if (httpFunctions.Any())
            {
                int position         = 0;
                var enabledFunctions = httpFunctions.ToDictionary(k => $"AzureFunctionsJobHost__functions__{position++}", v => v.Key);
                //Environment variables for the func app keys kubernetes secret
                var kubernetesSecretEnvironmentVariable = FuncAppKeysHelper.FuncKeysKubernetesEnvironVariables(keysSecretCollectionName, mountKeysAsContainerVolume);
                var additionalEnvVars = enabledFunctions.Concat(kubernetesSecretEnvironmentVariable).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
                var deployment        = GetDeployment(name + "-http", @namespace, imageName, pullSecret, 1, additionalEnvVars, port: 80);
                deployments.Add(deployment);
                var service = GetService(name + "-http", @namespace, deployment, serviceType);
                result.Add(service);
            }

            if (nonHttpFunctions.Any())
            {
                int position         = 0;
                var enabledFunctions = nonHttpFunctions.ToDictionary(k => $"AzureFunctionsJobHost__functions__{position++}", v => v.Key);
                var deployment       = GetDeployment(name, @namespace, imageName, pullSecret, minReplicas ?? 0, enabledFunctions);
                deployments.Add(deployment);
                scaledobject = GetScaledObject(name, @namespace, triggers, deployment, pollingInterval, cooldownPeriod, minReplicas, maxReplicas);
            }

            // Set worker runtime if needed.
            if (!secrets.ContainsKey(Constants.FunctionsWorkerRuntime))
            {
                secrets[Constants.FunctionsWorkerRuntime] = GlobalCoreToolsSettings.CurrentWorkerRuntime.ToString();
            }

            int resourceIndex = 0;

            if (useConfigMap)
            {
                var configMap = GetConfigMap(name, @namespace, secrets);
                result.Insert(resourceIndex, configMap);
                resourceIndex++;
                foreach (var deployment in deployments)
                {
                    deployment.Spec.Template.Spec.Containers.First().EnvFrom = new ContainerEnvironmentFromV1[]
                    {
                        new ContainerEnvironmentFromV1
                        {
                            ConfigMapRef = new NamedObjectV1
                            {
                                Name = configMap.Metadata.Name
                            }
                        }
                    };
                }
            }
            else if (!string.IsNullOrEmpty(secretsCollectionName))
            {
                foreach (var deployment in deployments)
                {
                    deployment.Spec.Template.Spec.Containers.First().EnvFrom = new ContainerEnvironmentFromV1[]
                    {
                        new ContainerEnvironmentFromV1
                        {
                            SecretRef = new NamedObjectV1
                            {
                                Name = secretsCollectionName
                            }
                        }
                    };
                }
            }
            else if (!string.IsNullOrEmpty(configMapName))
            {
                foreach (var deployment in deployments)
                {
                    deployment.Spec.Template.Spec.Containers.First().EnvFrom = new ContainerEnvironmentFromV1[]
                    {
                        new ContainerEnvironmentFromV1
                        {
                            ConfigMapRef = new NamedObjectV1
                            {
                                Name = configMapName
                            }
                        }
                    };
                }
            }
            else
            {
                var secret = GetSecret(name, @namespace, secrets);
                result.Insert(resourceIndex, secret);
                resourceIndex++;
                foreach (var deployment in deployments)
                {
                    deployment.Spec.Template.Spec.Containers.First().EnvFrom = new ContainerEnvironmentFromV1[]
                    {
                        new ContainerEnvironmentFromV1
                        {
                            SecretRef = new NamedObjectV1
                            {
                                Name = secret.Metadata.Name
                            }
                        }
                    };
                }
            }

            IDictionary <string, string> resultantFunctionKeys = new Dictionary <string, string>();

            if (httpFunctions.Any())
            {
                var currentImageFuncKeys = FuncAppKeysHelper.CreateKeys(httpFunctions.Select(f => f.Key));
                resultantFunctionKeys = GetFunctionKeys(currentImageFuncKeys, await GetExistingFunctionKeys(keysSecretCollectionName, @namespace));
                if (resultantFunctionKeys?.Any() == true)
                {
                    result.Insert(resourceIndex, GetSecret(keysSecretCollectionName, @namespace, resultantFunctionKeys));
                    resourceIndex++;
                }

                //if function keys Secrets needs to be mounted as volume in the function runtime container
                if (mountKeysAsContainerVolume)
                {
                    FuncAppKeysHelper.CreateFuncAppKeysVolumeMountDeploymentResource(deployments, keysSecretCollectionName);
                }
                //Create the Pod identity with the role to modify the function kubernetes secret
                else
                {
                    var svcActName = $"{name}-function-keys-identity-svc-act";
                    var svcActDeploymentResource = GetServiceAccount(svcActName, @namespace);
                    result.Insert(resourceIndex, svcActDeploymentResource);
                    resourceIndex++;

                    var funcKeysManagerRoleName = "functions-keys-manager-role";
                    var secretManagerRole       = GetSecretManagerRole(funcKeysManagerRoleName, @namespace);
                    result.Insert(resourceIndex, secretManagerRole);
                    resourceIndex++;
                    var roleBindingName = $"{svcActName}-functions-keys-manager-rolebinding";
                    var funcKeysRoleBindingDeploymentResource = GetRoleBinding(roleBindingName, @namespace, funcKeysManagerRoleName, svcActName);
                    result.Insert(resourceIndex, funcKeysRoleBindingDeploymentResource);
                    resourceIndex++;

                    //add service account identity to the pod
                    foreach (var deployment in deployments)
                    {
                        deployment.Spec.Template.Spec.ServiceAccountName = svcActName;
                    }
                }
            }

            result = result.Concat(deployments).ToList();
            return(scaledobject != null ? result.Append(scaledobject) : result, resultantFunctionKeys);
        }
Example #2
0
        internal static IEnumerable <IKubernetesResource> GetFunctionsDeploymentResources(
            string name,
            string imageName,
            string @namespace,
            TriggersPayload triggers,
            IDictionary <string, string> secrets,
            string pullSecret            = null,
            string secretsCollectionName = null,
            string configMapName         = null,
            bool useConfigMap            = false,
            int?pollingInterval          = null,
            int?cooldownPeriod           = null,
            string serviceType           = "LoadBalancer",
            int?minReplicas = null,
            int?maxReplicas = null)
        {
            ScaledObjectV1Alpha1 scaledobject = null;
            var result        = new List <IKubernetesResource>();
            var deployments   = new List <DeploymentV1Apps>();
            var httpFunctions = triggers.FunctionsJson
                                .Where(b => b.Value["bindings"]?.Any(e => e?["type"].ToString().IndexOf("httpTrigger", StringComparison.OrdinalIgnoreCase) != -1) == true);
            var nonHttpFunctions = triggers.FunctionsJson.Where(f => httpFunctions.All(h => h.Key != f.Key));

            if (httpFunctions.Any())
            {
                int position         = 0;
                var enabledFunctions = httpFunctions.ToDictionary(k => $"AzureFunctionsJobHost__functions__{position++}", v => v.Key);
                var deployment       = GetDeployment(name + "-http", @namespace, imageName, pullSecret, 1, enabledFunctions, new Dictionary <string, string>
                {
                    { "osiris.deislabs.io/enabled", "true" },
                    { "osiris.deislabs.io/minReplicas", "1" }
                }, port: 80);
                deployments.Add(deployment);
                var service = GetService(name + "-http", @namespace, deployment, serviceType, new Dictionary <string, string>
                {
                    { "osiris.deislabs.io/enabled", "true" },
                    { "osiris.deislabs.io/deployment", deployment.Metadata.Name }
                });
                result.Add(service);
            }

            if (nonHttpFunctions.Any())
            {
                int position         = 0;
                var enabledFunctions = nonHttpFunctions.ToDictionary(k => $"AzureFunctionsJobHost__functions__{position++}", v => v.Key);
                var deployment       = GetDeployment(name, @namespace, imageName, pullSecret, minReplicas ?? 0, enabledFunctions);
                deployments.Add(deployment);
                scaledobject = GetScaledObject(name, @namespace, triggers, deployment, pollingInterval, cooldownPeriod, minReplicas, maxReplicas);
            }

            // Set worker runtime if needed.
            if (!secrets.ContainsKey(Constants.FunctionsWorkerRuntime))
            {
                secrets[Constants.FunctionsWorkerRuntime] = GlobalCoreToolsSettings.CurrentWorkerRuntime.ToString();
            }

            if (useConfigMap)
            {
                var configMap = GetConfigMap(name, @namespace, secrets);
                result.Insert(0, configMap);
                foreach (var deployment in deployments)
                {
                    deployment.Spec.Template.Spec.Containers.First().EnvFrom = new ContainerEnvironmentFromV1[]
                    {
                        new ContainerEnvironmentFromV1
                        {
                            ConfigMapRef = new NamedObjectV1
                            {
                                Name = configMap.Metadata.Name
                            }
                        }
                    };
                }
            }
            else if (!string.IsNullOrEmpty(secretsCollectionName))
            {
                foreach (var deployment in deployments)
                {
                    deployment.Spec.Template.Spec.Containers.First().EnvFrom = new ContainerEnvironmentFromV1[]
                    {
                        new ContainerEnvironmentFromV1
                        {
                            SecretRef = new NamedObjectV1
                            {
                                Name = secretsCollectionName
                            }
                        }
                    };
                }
            }
            else if (!string.IsNullOrEmpty(configMapName))
            {
                foreach (var deployment in deployments)
                {
                    deployment.Spec.Template.Spec.Containers.First().EnvFrom = new ContainerEnvironmentFromV1[]
                    {
                        new ContainerEnvironmentFromV1
                        {
                            ConfigMapRef = new NamedObjectV1
                            {
                                Name = configMapName
                            }
                        }
                    };
                }
            }
            else
            {
                var secret = GetSecret(name, @namespace, secrets);
                result.Insert(0, secret);
                foreach (var deployment in deployments)
                {
                    deployment.Spec.Template.Spec.Containers.First().EnvFrom = new ContainerEnvironmentFromV1[]
                    {
                        new ContainerEnvironmentFromV1
                        {
                            SecretRef = new NamedObjectV1
                            {
                                Name = secret.Metadata.Name
                            }
                        }
                    };
                }
            }

            result = result.Concat(deployments).ToList();

            return(scaledobject != null
                ? result.Append(scaledobject)
                : result);
        }