From 1d26629b514d281d02525ac15b8c798cbccc749c Mon Sep 17 00:00:00 2001 From: Luke Kingland Date: Wed, 19 Feb 2025 16:35:47 +0900 Subject: [PATCH] feat: remote storage class (#2693) * feat: remote storage class * why isn't the schema regenerated on each make --- cmd/deploy.go | 12 ++++- docs/reference/func_deploy.md | 47 ++++++++++--------- pkg/functions/function.go | 4 ++ pkg/k8s/persistent_volumes.go | 7 ++- pkg/k8s/persistent_volumes_test.go | 4 +- pkg/pipelines/tekton/pipelines_provider.go | 2 +- .../tekton/pipelines_provider_test.go | 8 ++-- schema/func_yaml-schema.json | 4 ++ 8 files changed, 55 insertions(+), 33 deletions(-) diff --git a/cmd/deploy.go b/cmd/deploy.go index a518b4e0c..86bc2b072 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -35,7 +35,7 @@ SYNOPSIS [-b|--build] [--builder] [--builder-image] [-p|--push] [--domain] [--platform] [--build-timestamp] [--pvc-size] [--service-account] [-c|--confirm] [-v|--verbose] - [--registry-insecure] + [--registry-insecure] [--remote-storage-class] DESCRIPTION @@ -127,7 +127,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", "service-account", "registry", "registry-insecure", "remote", "username", "password", "token", "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", "registry-insecure", "remote", "username", "password", "token", "verbose", "remote-storage-class"), RunE: func(cmd *cobra.Command, args []string) error { return runDeploy(cmd, newClient) }, @@ -179,6 +179,8 @@ EXAMPLES "Directory in the Git repository containing the function (default is the root) ($FUNC_GIT_DIR)") cmd.Flags().BoolP("remote", "R", f.Local.Remote, "Trigger a remote deployment. Default is to deploy and build from the local system ($FUNC_REMOTE)") + cmd.Flags().StringP("remote-storage-class", "", f.Build.RemoteStorageClass, + "Specify a storage class to use for the volume on-cluster during remote builds") 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, @@ -517,6 +519,10 @@ type deployConfig struct { // be triggered in a remote environment rather than run locally. Remote bool + // RemoteStorageClass defines the storage class to use for the remote + // volume when building on-cluster. + RemoteStorageClass string + // PVCSize configures the PVC size used by the pipeline if --remote flag is set. PVCSize string @@ -538,6 +544,7 @@ func newDeployConfig(cmd *cobra.Command) deployConfig { GitURL: viper.GetString("git-url"), Namespace: viper.GetString("namespace"), Remote: viper.GetBool("remote"), + RemoteStorageClass: viper.GetString("remote-storage-class"), PVCSize: viper.GetString("pvc-size"), Timestamp: viper.GetBool("build-timestamp"), ServiceAccountName: viper.GetString("service-account"), @@ -573,6 +580,7 @@ func (c deployConfig) Configure(f fn.Function) (fn.Function, error) { f.Build.Git.URL = c.GitURL f.Build.Git.ContextDir = c.GitDir f.Build.Git.Revision = c.GitBranch // TODO: should match; perhaps "refSpec" + f.Build.RemoteStorageClass = c.RemoteStorageClass f.Deploy.ServiceAccountName = c.ServiceAccountName f.Local.Remote = c.Remote diff --git a/docs/reference/func_deploy.md b/docs/reference/func_deploy.md index 701ae6dfb..951109838 100644 --- a/docs/reference/func_deploy.md +++ b/docs/reference/func_deploy.md @@ -14,7 +14,7 @@ SYNOPSIS [-b|--build] [--builder] [--builder-image] [-p|--push] [--domain] [--platform] [--build-timestamp] [--pvc-size] [--service-account] [-c|--confirm] [-v|--verbose] - [--registry-insecure] + [--registry-insecure] [--remote-storage-class] DESCRIPTION @@ -113,28 +113,29 @@ 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 the function's current namespace by default if already deployed, and the currently active context if it can be determined. ($FUNC_NAMESPACE) (default "default") - -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) - --registry-insecure Skip TLS certificate verification when communicating in HTTPS with the registry ($FUNC_REGISTRY_INSECURE) - -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) + --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 the function's current namespace by default if already deployed, and the currently active context if it can be determined. ($FUNC_NAMESPACE) (default "default") + -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) + --registry-insecure Skip TLS certificate verification when communicating in HTTPS with the registry ($FUNC_REGISTRY_INSECURE) + -R, --remote Trigger a remote deployment. Default is to deploy and build from the local system ($FUNC_REMOTE) + --remote-storage-class string Specify a storage class to use for the volume on-cluster during remote builds + --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/functions/function.go b/pkg/functions/function.go index f5cbd998c..0cb1843f3 100644 --- a/pkg/functions/function.go +++ b/pkg/functions/function.go @@ -141,6 +141,10 @@ type BuildSpec struct { // when using deployment and remote build process (only relevant when Remote is true). PVCSize string `yaml:"pvcSize,omitempty"` + // RemoteStorageClass specifies the storage class to use for the volume used + // on-cluster during when built remotely. + RemoteStorageClass string `yaml:"remoteStorageClass,omitempty"` + // Image stores last built image name NOT in func.yaml, but instead // in .func/built-image Image string `yaml:"-"` diff --git a/pkg/k8s/persistent_volumes.go b/pkg/k8s/persistent_volumes.go index b46f4a216..eeadbd371 100644 --- a/pkg/k8s/persistent_volumes.go +++ b/pkg/k8s/persistent_volumes.go @@ -32,7 +32,7 @@ func GetPersistentVolumeClaim(ctx context.Context, name, namespaceOverride strin return client.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, name, metav1.GetOptions{}) } -func CreatePersistentVolumeClaim(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity) (err error) { +func CreatePersistentVolumeClaim(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity, storageClassName string) (err error) { client, namespace, err := NewClientAndResolvedNamespace(namespaceOverride) if err != nil { return @@ -52,8 +52,13 @@ func CreatePersistentVolumeClaim(ctx context.Context, name, namespaceOverride st }, }, } + pvc.Spec.Resources.Requests[corev1.ResourceStorage] = resourceRequest + if storageClassName != "" { + pvc.Spec.StorageClassName = &storageClassName + } + _, err = client.CoreV1().PersistentVolumeClaims(namespace).Create(ctx, pvc, metav1.CreateOptions{}) return } diff --git a/pkg/k8s/persistent_volumes_test.go b/pkg/k8s/persistent_volumes_test.go index 9b329abec..ab3475db0 100644 --- a/pkg/k8s/persistent_volumes_test.go +++ b/pkg/k8s/persistent_volumes_test.go @@ -34,8 +34,8 @@ func TestUploadToVolume(t *testing.T) { testingPVCName := "testing-pvc-" + rnd err = k8s.CreatePersistentVolumeClaim(ctx, testingPVCName, testingNS, - nil, nil, - corev1.ReadWriteOnce, *resource.NewQuantity(1024, resource.DecimalSI)) + nil, nil, corev1.ReadWriteOnce, + *resource.NewQuantity(1024, resource.DecimalSI), "") if err != nil { t.Fatal(err) } diff --git a/pkg/pipelines/tekton/pipelines_provider.go b/pkg/pipelines/tekton/pipelines_provider.go index 1c31ecc7d..470eebca1 100644 --- a/pkg/pipelines/tekton/pipelines_provider.go +++ b/pkg/pipelines/tekton/pipelines_provider.go @@ -568,7 +568,7 @@ func createPipelinePersistentVolumeClaim(ctx context.Context, f fn.Function, nam return fmt.Errorf("PVC size value could not be parsed. %w", err) } } - err = createPersistentVolumeClaim(ctx, getPipelinePvcName(f), namespace, labels, f.Deploy.Annotations, corev1.ReadWriteOnce, pvcs) + err = createPersistentVolumeClaim(ctx, getPipelinePvcName(f), namespace, labels, f.Deploy.Annotations, corev1.ReadWriteOnce, pvcs, f.Build.RemoteStorageClass) if err != nil && !k8serrors.IsAlreadyExists(err) { return fmt.Errorf("problem creating persistent volume claim: %v", err) } diff --git a/pkg/pipelines/tekton/pipelines_provider_test.go b/pkg/pipelines/tekton/pipelines_provider_test.go index 99a1d6f3a..f7973eeeb 100644 --- a/pkg/pipelines/tekton/pipelines_provider_test.go +++ b/pkg/pipelines/tekton/pipelines_provider_test.go @@ -74,7 +74,7 @@ func TestSourcesAsTarStream(t *testing.T) { } func Test_createPipelinePersistentVolumeClaim(t *testing.T) { - type mockType func(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity) (err error) + type mockType func(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity, storageClass string) (err error) type args struct { ctx context.Context @@ -98,7 +98,7 @@ func Test_createPipelinePersistentVolumeClaim(t *testing.T) { labels: nil, size: DefaultPersistentVolumeClaimSize.String(), }, - mock: func(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity) (err error) { + mock: func(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity, storageClass string) (err error) { return errors.New("creation of pvc failed") }, wantErr: true, @@ -112,7 +112,7 @@ func Test_createPipelinePersistentVolumeClaim(t *testing.T) { labels: nil, size: DefaultPersistentVolumeClaimSize.String(), }, - mock: func(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity) (err error) { + mock: func(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity, storageClass string) (err error) { return &apiErrors.StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonAlreadyExists}} }, wantErr: false, @@ -126,7 +126,7 @@ func Test_createPipelinePersistentVolumeClaim(t *testing.T) { labels: nil, size: DefaultPersistentVolumeClaimSize.String(), }, - mock: func(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity) (err error) { + mock: func(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity, storageClass string) (err error) { return errors.New("no namespace defined") }, wantErr: true, diff --git a/schema/func_yaml-schema.json b/schema/func_yaml-schema.json index c10439cab..633336d91 100644 --- a/schema/func_yaml-schema.json +++ b/schema/func_yaml-schema.json @@ -44,6 +44,10 @@ "pvcSize": { "type": "string", "description": "PVCSize specifies the size of persistent volume claim used to store function\nwhen using deployment and remote build process (only relevant when Remote is true)." + }, + "remoteStorageClass": { + "type": "string", + "description": "RemoteStorageClass specifies the storage class to use for the volume used\non-cluster during when built remotely." } }, "additionalProperties": false,