diff --git a/cmd/deploy.go b/cmd/deploy.go index b873407827..abb4148d6a 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -32,8 +32,8 @@ SYNOPSIS {{rootCmdUse}} deploy [-R|--remote] [-r|--registry] [-i|--image] [-n|--namespace] [-e|--env] [-g|--git-url] [-t|--git-branch] [-d|--git-dir] [-b|--build] [--builder] [--builder-image] [-p|--push] - [--domain] [--platform] [--build-timestamp] - [-c|--confirm] [-v|--verbose] + [--domain] [--platform] [--build-timestamp] [--pvc-size] + [--service-account] [-c|--confirm] [-v|--verbose] DESCRIPTION @@ -125,7 +125,7 @@ EXAMPLES `, SuggestFor: []string{"delpoy", "deplyo"}, - PreRunE: bindEnv("build", "build-timestamp", "builder", "builder-image", "confirm", "domain", "env", "git-branch", "git-dir", "git-url", "image", "namespace", "path", "platform", "push", "pvc-size", "registry", "remote", "verbose"), + PreRunE: bindEnv("build", "build-timestamp", "builder", "builder-image", "confirm", "domain", "env", "git-branch", "git-dir", "git-url", "image", "namespace", "path", "platform", "push", "pvc-size", "service-account", "registry", "remote", "verbose"), RunE: func(cmd *cobra.Command, args []string) error { return runDeploy(cmd, newClient) }, @@ -180,7 +180,8 @@ EXAMPLES "Trigger a remote deployment. Default is to deploy and build from the local system ($FUNC_REMOTE)") cmd.Flags().String("pvc-size", f.Build.PVCSize, "When triggering a remote deployment, set a custom volume size to allocate for the build operation ($FUNC_PVC_SIZE)") - + cmd.Flags().String("service-account", f.Deploy.ServiceAccountName, + "Service account to be used in the deployed function ($FUNC_SERVICE_ACCOUNT)") // Static Flags: // Options which have static defaults only (not globally configurable nor // persisted with the function) @@ -435,6 +436,9 @@ type deployConfig struct { // (~/.kube/config) in the case of Kubernetes. Namespace string + //Service account to be used in deployed function + ServiceAccountName string + // Remote indicates the deployment (and possibly build) process are to // be triggered in a remote environment rather than run locally. Remote bool @@ -451,17 +455,18 @@ type deployConfig struct { // environment variables; in that precedence. func newDeployConfig(cmd *cobra.Command) (c deployConfig) { c = deployConfig{ - buildConfig: newBuildConfig(), - Build: viper.GetString("build"), - Env: viper.GetStringSlice("env"), - Domain: viper.GetString("domain"), - GitBranch: viper.GetString("git-branch"), - GitDir: viper.GetString("git-dir"), - GitURL: viper.GetString("git-url"), - Namespace: viper.GetString("namespace"), - Remote: viper.GetBool("remote"), - PVCSize: viper.GetString("pvc-size"), - Timestamp: viper.GetBool("build-timestamp"), + buildConfig: newBuildConfig(), + Build: viper.GetString("build"), + Env: viper.GetStringSlice("env"), + Domain: viper.GetString("domain"), + GitBranch: viper.GetString("git-branch"), + GitDir: viper.GetString("git-dir"), + GitURL: viper.GetString("git-url"), + Namespace: viper.GetString("namespace"), + Remote: viper.GetBool("remote"), + PVCSize: viper.GetString("pvc-size"), + Timestamp: viper.GetBool("build-timestamp"), + ServiceAccountName: viper.GetString("service-account"), } // NOTE: .Env should be viper.GetStringSlice, but this returns unparsed // results and appears to be an open issue since 2017: @@ -494,6 +499,7 @@ func (c deployConfig) Configure(f fn.Function) (fn.Function, error) { f.Build.Git.Revision = c.GitBranch // TODO: should match; perhaps "refSpec" f.Deploy.Namespace = c.Namespace f.Deploy.Remote = c.Remote + f.Deploy.ServiceAccountName = c.ServiceAccountName // PVCSize // If a specific value is requested, ensure it parses as a resource.Quantity diff --git a/docs/reference/func_deploy.md b/docs/reference/func_deploy.md index 895ab7386a..61e985baec 100644 --- a/docs/reference/func_deploy.md +++ b/docs/reference/func_deploy.md @@ -12,8 +12,8 @@ SYNOPSIS func deploy [-R|--remote] [-r|--registry] [-i|--image] [-n|--namespace] [-e|--env] [-g|--git-url] [-t|--git-branch] [-d|--git-dir] [-b|--build] [--builder] [--builder-image] [-p|--push] - [--domain] [--platform] [--build-timestamp] - [-c|--confirm] [-v|--verbose] + [--domain] [--platform] [--build-timestamp] [--pvc-size] + [--service-account] [-c|--confirm] [-v|--verbose] DESCRIPTION @@ -112,26 +112,27 @@ func deploy ### Options ``` - --build string[="true"] Build the function. [auto|true|false]. ($FUNC_BUILD) (default "auto") - --build-timestamp Use the actual time as the created time for the docker image. This is only useful for buildpacks builder. - -b, --builder string Builder to use when creating the function's container. Currently supported builders are "pack" and "s2i". (default "pack") - --builder-image string Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE) - -c, --confirm Prompt to confirm options interactively ($FUNC_CONFIRM) - --domain string Domain to use for the function's route. Cluster must be configured with domain matching for the given domain (ignored if unrecognized) ($FUNC_DOMAIN) - -e, --env stringArray Environment variable to set in the form NAME=VALUE. You may provide this flag multiple times for setting multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-). - -t, --git-branch string Git revision (branch) to be used when deploying via the Git repository ($FUNC_GIT_BRANCH) - -d, --git-dir string Directory in the Git repository containing the function (default is the root) ($FUNC_GIT_DIR) - -g, --git-url string Repository url containing the function to build ($FUNC_GIT_URL) - -h, --help help for deploy - -i, --image string Full image name in the form [registry]/[namespace]/[name]:[tag]@[digest]. This option takes precedence over --registry. Specifying digest is optional, but if it is given, 'build' and 'push' phases are disabled. ($FUNC_IMAGE) - -n, --namespace string Deploy into a specific namespace. Will use function's current namespace by default if already deployed, and the currently active namespace if it can be determined. ($FUNC_NAMESPACE) - -p, --path string Path to the function. Default is current directory ($FUNC_PATH) - --platform string Optionally specify a specific platform to build for (e.g. linux/amd64). ($FUNC_PLATFORM) - -u, --push Push the function image to registry before deploying. ($FUNC_PUSH) (default true) - --pvc-size string When triggering a remote deployment, set a custom volume size to allocate for the build operation ($FUNC_PVC_SIZE) - -r, --registry string Container registry + registry namespace. (ex 'ghcr.io/myuser'). The full image name is automatically determined using this along with function name. ($FUNC_REGISTRY) - -R, --remote Trigger a remote deployment. Default is to deploy and build from the local system ($FUNC_REMOTE) - -v, --verbose Print verbose logs ($FUNC_VERBOSE) + --build string[="true"] Build the function. [auto|true|false]. ($FUNC_BUILD) (default "auto") + --build-timestamp Use the actual time as the created time for the docker image. This is only useful for buildpacks builder. + -b, --builder string Builder to use when creating the function's container. Currently supported builders are "pack" and "s2i". (default "pack") + --builder-image string Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE) + -c, --confirm Prompt to confirm options interactively ($FUNC_CONFIRM) + --domain string Domain to use for the function's route. Cluster must be configured with domain matching for the given domain (ignored if unrecognized) ($FUNC_DOMAIN) + -e, --env stringArray Environment variable to set in the form NAME=VALUE. You may provide this flag multiple times for setting multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-). + -t, --git-branch string Git revision (branch) to be used when deploying via the Git repository ($FUNC_GIT_BRANCH) + -d, --git-dir string Directory in the Git repository containing the function (default is the root) ($FUNC_GIT_DIR) + -g, --git-url string Repository url containing the function to build ($FUNC_GIT_URL) + -h, --help help for deploy + -i, --image string Full image name in the form [registry]/[namespace]/[name]:[tag]@[digest]. This option takes precedence over --registry. Specifying digest is optional, but if it is given, 'build' and 'push' phases are disabled. ($FUNC_IMAGE) + -n, --namespace string Deploy into a specific namespace. Will use function's current namespace by default if already deployed, and the currently active namespace if it can be determined. ($FUNC_NAMESPACE) + -p, --path string Path to the function. Default is current directory ($FUNC_PATH) + --platform string Optionally specify a specific platform to build for (e.g. linux/amd64). ($FUNC_PLATFORM) + -u, --push Push the function image to registry before deploying. ($FUNC_PUSH) (default true) + --pvc-size string When triggering a remote deployment, set a custom volume size to allocate for the build operation ($FUNC_PVC_SIZE) + -r, --registry string Container registry + registry namespace. (ex 'ghcr.io/myuser'). The full image name is automatically determined using this along with function name. ($FUNC_REGISTRY) + -R, --remote Trigger a remote deployment. Default is to deploy and build from the local system ($FUNC_REMOTE) + --service-account string Service account to be used in the deployed function ($FUNC_SERVICE_ACCOUNT) + -v, --verbose Print verbose logs ($FUNC_VERBOSE) ``` ### SEE ALSO diff --git a/pkg/k8s/serviceaccount.go b/pkg/k8s/serviceaccount.go new file mode 100644 index 0000000000..8180cae270 --- /dev/null +++ b/pkg/k8s/serviceaccount.go @@ -0,0 +1,19 @@ +package k8s + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func GetServiceAccount(ctx context.Context, referencedServiceAccount, namespace string) error { + k8sClient, err := NewKubernetesClientset() + if err != nil { + return err + } + _, err = k8sClient.CoreV1().ServiceAccounts(namespace).Get(ctx, referencedServiceAccount, metav1.GetOptions{}) + if err != nil { + return err + } + return nil +} diff --git a/pkg/knative/deployer.go b/pkg/knative/deployer.go index faed10e074..2aa095eae4 100644 --- a/pkg/knative/deployer.go +++ b/pkg/knative/deployer.go @@ -155,7 +155,7 @@ func (d *Deployer) Deploy(ctx context.Context, f fn.Function) (fn.DeploymentResu return fn.DeploymentResult{}, err } - err = checkResourcesArePresent(ctx, d.Namespace, &referencedSecrets, &referencedConfigMaps, &referencedPVCs) + err = checkResourcesArePresent(ctx, d.Namespace, &referencedSecrets, &referencedConfigMaps, &referencedPVCs, f.Deploy.ServiceAccountName) if err != nil { err = fmt.Errorf("knative deployer failed to generate the Knative Service: %v", err) return fn.DeploymentResult{}, err @@ -252,7 +252,7 @@ func (d *Deployer) Deploy(ctx context.Context, f fn.Function) (fn.DeploymentResu return fn.DeploymentResult{}, err } - err = checkResourcesArePresent(ctx, d.Namespace, &referencedSecrets, &referencedConfigMaps, &referencedPVCs) + err = checkResourcesArePresent(ctx, d.Namespace, &referencedSecrets, &referencedConfigMaps, &referencedPVCs, f.Deploy.ServiceAccountName) if err != nil { err = fmt.Errorf("knative deployer failed to update the Knative Service: %v", err) return fn.DeploymentResult{}, err @@ -510,7 +510,7 @@ func updateService(f fn.Function, previousService *v1.Service, newEnv []corev1.E cp.EnvFrom = newEnvFrom cp.VolumeMounts = newVolumeMounts service.Spec.ConfigurationSpec.Template.Spec.Volumes = newVolumes - + service.Spec.ConfigurationSpec.Template.Spec.PodSpec.ServiceAccountName = f.Deploy.ServiceAccountName return service, nil } } @@ -815,7 +815,7 @@ func processVolumes(volumes []fn.Volume, referencedSecrets, referencedConfigMaps // checkResourcesArePresent returns error if Secrets or ConfigMaps // referenced in input sets are not deployed on the cluster in the specified namespace -func checkResourcesArePresent(ctx context.Context, namespace string, referencedSecrets, referencedConfigMaps, referencedPVCs *sets.String) error { +func checkResourcesArePresent(ctx context.Context, namespace string, referencedSecrets, referencedConfigMaps, referencedPVCs *sets.String, referencedServiceAccount string) error { errMsg := "" for s := range *referencedSecrets { @@ -839,6 +839,14 @@ func checkResourcesArePresent(ctx context.Context, namespace string, referencedS } } + // check if referenced ServiceAccount is present in the namespace if it is not default + if referencedServiceAccount != "" && referencedServiceAccount != "default" { + err := k8s.GetServiceAccount(ctx, referencedServiceAccount, namespace) + if err != nil { + errMsg += fmt.Sprintf(" referenced ServiceAccount \"%s\" is not present in namespace \"%s\"\n", referencedServiceAccount, namespace) + } + } + if errMsg != "" { return fmt.Errorf("\n" + errMsg) }