diff --git a/cmd/config_volumes.go b/cmd/config_volumes.go index c68dc9071..6a8a9ce51 100644 --- a/cmd/config_volumes.go +++ b/cmd/config_volumes.go @@ -66,19 +66,49 @@ func NewConfigVolumesAddCmd() *cobra.Command { Interactive prompt to add Secrets and ConfigMaps as Volume mounts to the function project in the current directory or from the directory specified with --path. + +For non-interactive usage, use flags to specify the volume type and configuration. `, + Example: `# Add a ConfigMap volume +{{rootCmdUse}} config volumes add --type=configmap --source=my-config --path=/etc/config + +# Add a Secret volume +{{rootCmdUse}} config volumes add --type=secret --source=my-secret --path=/etc/secret + +# Add a PersistentVolumeClaim volume +{{rootCmdUse}} config volumes add --type=pvc --source=my-pvc --path=/data +{{rootCmdUse}} config volumes add --type=pvc --source=my-pvc --path=/data --read-only + +# Add an EmptyDir volume +{{rootCmdUse}} config volumes add --type=emptydir --path=/tmp/cache +{{rootCmdUse}} config volumes add --type=emptydir --path=/tmp/cache --size=1Gi --medium=Memory`, SuggestFor: []string{"ad", "create", "insert", "append"}, - PreRunE: bindEnv("path", "verbose"), + PreRunE: bindEnv("path", "verbose", "type", "source", "mount-path", "read-only", "size", "medium"), RunE: func(cmd *cobra.Command, args []string) (err error) { function, err := initConfigCommand(defaultLoaderSaver) if err != nil { return } + // Check if flags are provided for non-interactive mode + volumeType, _ := cmd.Flags().GetString("type") + if volumeType != "" { + return runAddVolume(cmd, function) + } + + // Fall back to interactive mode return runAddVolumesPrompt(cmd.Context(), function) }, } + // Add flags for non-interactive mode + cmd.Flags().StringP("type", "t", "", "Volume type: configmap, secret, pvc, or emptydir") + cmd.Flags().StringP("source", "s", "", "Name of the ConfigMap, Secret, or PVC to mount (not used for emptydir)") + cmd.Flags().StringP("mount-path", "m", "", "Path where the volume should be mounted in the container") + cmd.Flags().BoolP("read-only", "r", false, "Mount volume as read-only (only for PVC)") + cmd.Flags().StringP("size", "", "", "Maximum size limit for EmptyDir volume (e.g., 1Gi)") + cmd.Flags().StringP("medium", "", "", "Storage medium for EmptyDir volume: 'Memory' or '' (default)") + return cmd } @@ -90,20 +120,34 @@ func NewConfigVolumesRemoveCmd() *cobra.Command { Interactive prompt to remove Volume mounts from the function project in the current directory or from the directory specified with --path. + +For non-interactive usage, use the --mount-path flag to specify which volume to remove. `, + Example: `# Remove a volume by its mount path +{{rootCmdUse}} config volumes remove --mount-path=/etc/config`, Aliases: []string{"rm"}, SuggestFor: []string{"del", "delete", "rmeove"}, - PreRunE: bindEnv("path", "verbose"), + PreRunE: bindEnv("path", "verbose", "mount-path"), RunE: func(cmd *cobra.Command, args []string) (err error) { function, err := initConfigCommand(defaultLoaderSaver) if err != nil { return } + // Check if mount-path flag is provided for non-interactive mode + mountPath, _ := cmd.Flags().GetString("mount-path") + if mountPath != "" { + return runRemoveVolume(cmd, function, mountPath) + } + + // Fall back to interactive mode return runRemoveVolumesPrompt(function) }, } + // Add flag for non-interactive mode + cmd.Flags().StringP("mount-path", "m", "", "Path of the volume mount to remove") + return cmd } @@ -286,3 +330,104 @@ func runRemoveVolumesPrompt(f fn.Function) (err error) { return } + +// runAddVolume handles adding volumes using command line flags +func runAddVolume(cmd *cobra.Command, f fn.Function) error { + var ( + volumeType, _ = cmd.Flags().GetString("type") + source, _ = cmd.Flags().GetString("source") + mountPath, _ = cmd.Flags().GetString("mount-path") + readOnly, _ = cmd.Flags().GetBool("read-only") + sizeLimit, _ = cmd.Flags().GetString("size") + medium, _ = cmd.Flags().GetString("medium") + ) + + // Validate mount path + if mountPath == "" { + return fmt.Errorf("--mount-path is required") + } + if !strings.HasPrefix(mountPath, "/") { + return fmt.Errorf("mount path must be an absolute path (start with /)") + } + + // Create the volume based on type + newVolume := fn.Volume{Path: &mountPath} + + // All volumeTypes except emptydir require a source + if volumeType != "emptydir" && source == "" { + return fmt.Errorf("--source is required for %s volumes", volumeType) + } + + switch volumeType { + case "configmap": + newVolume.ConfigMap = &source + case "secret": + newVolume.Secret = &source + case "pvc": + newVolume.PersistentVolumeClaim = &fn.PersistentVolumeClaim{ + ClaimName: &source, + ReadOnly: readOnly, + } + if readOnly { + fmt.Fprintf(cmd.OutOrStderr(), "PersistentVolumeClaim will be mounted as read-only") + } + fmt.Fprintf(cmd.OutOrStderr(), "Please ensure the PersistentVolumeClaim extension flag is enabled:\nhttps://knative.dev/docs/serving/configuration/feature-flags/\n") + case "emptydir": + emptyDir := &fn.EmptyDir{} + if sizeLimit != "" { + emptyDir.SizeLimit = &sizeLimit + } + if medium != "" { + if medium != fn.StorageMediumMemory && medium != fn.StorageMediumDefault { + return fmt.Errorf("invalid medium: must be 'Memory' or empty") + } + emptyDir.Medium = medium + } + newVolume.EmptyDir = emptyDir + fmt.Fprintf(cmd.OutOrStderr(), "Please make sure to enable the EmptyDir extension flag:\nhttps://knative.dev/docs/serving/configuration/feature-flags/\n") + + default: + return fmt.Errorf("invalid volume type: %s (must be one of: configmap, secret, pvc, emptydir)", volumeType) + } + + // Add the volume to the function + f.Run.Volumes = append(f.Run.Volumes, newVolume) + + // Save the function + err := f.Write() + if err == nil { + fmt.Printf("Volume entry was added to the function configuration\n") + fmt.Printf("Added: %s\n", newVolume.String()) + } + return err +} + +// runRemoveVolume handles removing volumes by mount path +func runRemoveVolume(cmd *cobra.Command, f fn.Function, mountPath string) error { + if !strings.HasPrefix(mountPath, "/") { + return fmt.Errorf("mount path must be an absolute path (start with /)") + } + + // Find and remove the volume with the specified path + var newVolumes []fn.Volume + removed := false + for _, v := range f.Run.Volumes { + if v.Path != nil && *v.Path == mountPath { + removed = true + } else { + newVolumes = append(newVolumes, v) + } + } + + if !removed { + return fmt.Errorf("no volume found with mount path: %s", mountPath) + } + + f.Run.Volumes = newVolumes + err := f.Write() + if err == nil { + fmt.Fprintf(cmd.OutOrStderr(), "Volume entry was removed from the function configuration\n") + fmt.Fprintf(cmd.OutOrStderr(), "Removed volume at path: %s\n", mountPath) + } + return err +} diff --git a/docs/reference/func_config_volumes_add.md b/docs/reference/func_config_volumes_add.md index 85045662f..e2a732b37 100644 --- a/docs/reference/func_config_volumes_add.md +++ b/docs/reference/func_config_volumes_add.md @@ -9,17 +9,43 @@ Add volume to the function configuration Interactive prompt to add Secrets and ConfigMaps as Volume mounts to the function project in the current directory or from the directory specified with --path. +For non-interactive usage, use flags to specify the volume type and configuration. + ``` func config volumes add ``` +### Examples + +``` +# Add a ConfigMap volume +func config volumes add --type=configmap --source=my-config --path=/etc/config + +# Add a Secret volume +func config volumes add --type=secret --source=my-secret --path=/etc/secret + +# Add a PersistentVolumeClaim volume +func config volumes add --type=pvc --source=my-pvc --path=/data +func config volumes add --type=pvc --source=my-pvc --path=/data --read-only + +# Add an EmptyDir volume +func config volumes add --type=emptydir --path=/tmp/cache +func config volumes add --type=emptydir --path=/tmp/cache --size=1Gi --medium=Memory +``` + ### Options ``` - -h, --help help for add - -p, --path string Path to the function. Default is current directory ($FUNC_PATH) - -v, --verbose Print verbose logs ($FUNC_VERBOSE) + -h, --help help for add + --medium string Storage medium for EmptyDir volume: 'Memory' or '' (default) + -m, --mount-path string Path where the volume should be mounted in the container + -p, --path string Path to the function. Default is current directory ($FUNC_PATH) + -r, --read-only Mount volume as read-only (only for PVC) + --size string Maximum size limit for EmptyDir volume (e.g., 1Gi) + -s, --source string Name of the ConfigMap, Secret, or PVC to mount (not used for emptydir) + -t, --type string Volume type: configmap, secret, pvc, or emptydir + -v, --verbose Print verbose logs ($FUNC_VERBOSE) ``` ### SEE ALSO diff --git a/docs/reference/func_config_volumes_remove.md b/docs/reference/func_config_volumes_remove.md index 2a277ad01..c717a1fd1 100644 --- a/docs/reference/func_config_volumes_remove.md +++ b/docs/reference/func_config_volumes_remove.md @@ -9,17 +9,27 @@ Remove volume from the function configuration Interactive prompt to remove Volume mounts from the function project in the current directory or from the directory specified with --path. +For non-interactive usage, use the --mount-path flag to specify which volume to remove. + ``` func config volumes remove ``` +### Examples + +``` +# Remove a volume by its mount path +func config volumes remove --mount-path=/etc/config +``` + ### Options ``` - -h, --help help for remove - -p, --path string Path to the function. Default is current directory ($FUNC_PATH) - -v, --verbose Print verbose logs ($FUNC_VERBOSE) + -h, --help help for remove + -m, --mount-path string Path of the volume mount to remove + -p, --path string Path to the function. Default is current directory ($FUNC_PATH) + -v, --verbose Print verbose logs ($FUNC_VERBOSE) ``` ### SEE ALSO