public override async Task RunAsync()
        {
            (var resolvedImageName, var shouldBuild) = ResolveImageName();
            TriggersPayload triggers = null;

            if (DryRun)
            {
                if (shouldBuild)
                {
                    // don't build on a --dry-run.
                    // read files from the local dir
                    triggers = await GetTriggersLocalFiles();
                }
                else
                {
                    triggers = await DockerHelpers.GetTriggersFromDockerImage(resolvedImageName);
                }
            }
            else
            {
                if (shouldBuild)
                {
                    await DockerHelpers.DockerBuild(resolvedImageName, Environment.CurrentDirectory);
                }
                triggers = await DockerHelpers.GetTriggersFromDockerImage(resolvedImageName);
            }

            (var resources, var funcKeys) = await KubernetesHelper.GetFunctionsDeploymentResources(
                Name,
                resolvedImageName,
                Namespace,
                triggers,
                _secretsManager.GetSecrets(),
                PullSecret,
                SecretsCollectionName,
                ConfigMapName,
                UseConfigMap,
                PollingInterval,
                CooldownPeriod,
                ServiceType,
                MinReplicaCount,
                MaxReplicaCount,
                KeysSecretCollectionName,
                MountFuncKeysAsContainerVolume);

            if (DryRun)
            {
                ColoredConsole.WriteLine(KubernetesHelper.SerializeResources(resources, OutputSerializationOptions.Yaml));
            }
            else
            {
                if (!await KubernetesHelper.NamespaceExists(Namespace))
                {
                    await KubernetesHelper.CreateNamespace(Namespace);
                }

                if (shouldBuild)
                {
                    await DockerHelpers.DockerPush(resolvedImageName);
                }

                foreach (var resource in resources)
                {
                    await KubectlHelper.KubectlApply(resource, showOutput : true, ignoreError : IgnoreErrors, @namespace : Namespace);
                }

                //Print the function keys message to the console
                await KubernetesHelper.PrintFunctionsInfo($"{Name}-http", Namespace, funcKeys, triggers);
            }
        }
コード例 #2
0
 private static ScaledObjectV1Alpha1 GetScaledObject(string name, string @namespace, TriggersPayload triggers, DeploymentV1Apps deployment, int?pollingInterval, int?cooldownPeriod, int?minReplicas, int?maxReplicas)
 {
     return(new ScaledObjectV1Alpha1
     {
         ApiVersion = "keda.k8s.io/v1alpha1",
         Kind = "ScaledObject",
         Metadata = new ObjectMetadataV1
         {
             Name = name,
             Namespace = @namespace,
             Labels = new Dictionary <string, string>
             {
                 { "deploymentName", deployment.Metadata.Name }
             }
         },
         Spec = new ScaledObjectSpecV1Alpha1
         {
             ScaleTargetRef = new ScaledObjectScaleTargetRefV1Alpha1
             {
                 DeploymentName = deployment.Metadata.Name
             },
             PollingInterval = pollingInterval,
             CooldownPeriod = cooldownPeriod,
             MinReplicaCount = minReplicas,
             MaxReplicaCount = maxReplicas,
             Triggers = triggers
                        .FunctionsJson
                        .Select(kv => kv.Value)
                        .Where(v => v["bindings"] != null)
                        .Select(b => b["bindings"])
                        .SelectMany(i => i)
                        .Where(b => b?["type"] != null)
                        .Where(b => b["type"].ToString().IndexOf("Trigger", StringComparison.OrdinalIgnoreCase) != -1)
                        .Where(b => b["type"].ToString().IndexOf("httpTrigger", StringComparison.OrdinalIgnoreCase) == -1)
                        .Select(t => new ScaledObjectTriggerV1Alpha1
             {
                 Type = GetKedaTrigger(t["type"]?.ToString()),
                 Metadata = PopulateMetadataDictionary(t)
             })
         }
     });
 }
コード例 #3
0
        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);
        }
コード例 #4
0
        internal async static Task PrintFunctionsInfo(string serviceName, string @namespace, IDictionary <string, string> funcKeys, TriggersPayload triggers)
        {
            if (string.IsNullOrWhiteSpace(serviceName) ||
                string.IsNullOrWhiteSpace(@namespace) ||
                funcKeys?.Any() == false ||
                triggers == null)
            {
                return;
            }

            var httpFunctions = triggers.FunctionsJson
                                .Where(b => b.Value["bindings"]?.Any(e => e?["type"].ToString().IndexOf("httpTrigger", StringComparison.OrdinalIgnoreCase) != -1) == true)
                                .Select(item => item.Key);

            var loadBalancerIp = await GetLoadBalancerIp(serviceName, @namespace, 24);

            if (string.IsNullOrEmpty(loadBalancerIp))
            {
                ColoredConsole.WriteLine(WarningColor($"The service: {serviceName} is not yet ready, please re-run the deployment to get the function keys."));
                return;
            }

            var masterKey = funcKeys["host.master"];

            if (httpFunctions?.Any() == true)
            {
                foreach (var functionName in httpFunctions)
                {
                    var getFunctionAdminUri = $"http://{loadBalancerIp}/admin/functions/{functionName}?code={masterKey}";
                    var httpResponseMessage = await GetHttpResponse(new HttpRequestMessage(HttpMethod.Get, getFunctionAdminUri), 20);

                    if (httpResponseMessage.StatusCode == System.Net.HttpStatusCode.NotFound)
                    {
                        ColoredConsole.WriteLine(WarningColor($"The service: {functionName} is not yet ready in the runtime yet, please re-run the deployment to get the function keys."));
                        return;
                    }

                    if (httpResponseMessage.IsSuccessStatusCode)
                    {
                        var responseContent = await httpResponseMessage.Content.ReadAsStringAsync();

                        var functionsInfo = JsonConvert.DeserializeObject <FunctionInfo>(responseContent);

                        var trigger = functionsInfo
                                      .Config?["bindings"]
                                      ?.FirstOrDefault(o => o["type"]?.ToString().IndexOf("Trigger", StringComparison.OrdinalIgnoreCase) != -1)
                            ?["type"];

                        trigger = trigger ?? "No Trigger Found";
                        var showFunctionKey = true;

                        var authLevel = functionsInfo
                                        .Config?["bindings"]
                                        ?.FirstOrDefault(o => !string.IsNullOrEmpty(o["authLevel"]?.ToString()))
                            ?["authLevel"];

                        if (authLevel != null && authLevel.ToString().Equals("anonymous", StringComparison.OrdinalIgnoreCase))
                        {
                            showFunctionKey = false;
                        }

                        ColoredConsole.WriteLine($"\t{functionName} - [{VerboseColor(trigger.ToString())}]");
                        if (!string.IsNullOrEmpty(functionsInfo.InvokeUrlTemplate))
                        {
                            if (showFunctionKey)
                            {
                                ColoredConsole.WriteLine($"\tInvoke url: {VerboseColor($"{functionsInfo.InvokeUrlTemplate}?code={funcKeys[$"functions.{functionName.ToLower()}.default"]}")}");
                            }
                            else
                            {
                                ColoredConsole.WriteLine($"\tInvoke url: {VerboseColor(functionsInfo.InvokeUrlTemplate)}");
                            }
                        }
                        ColoredConsole.WriteLine();
                    }
                }
            }

            //Print the master key as well for the user
            ColoredConsole.WriteLine($"\tMaster key: {VerboseColor($"{funcKeys[$"host.master"]}")}");
        }
コード例 #5
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);
        }