Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/akamai/api_client_credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func APIClientCredentials() schema.CredentialType {
},
DefaultProvisioner: provision.TempFile(configFile,
provision.Filename(".edgerc"),
provision.AddArgs(
provision.AppendArgs(
"--edgerc", "{{ .Path }}",
"--section", "default",
),
Expand Down
2 changes: 1 addition & 1 deletion plugins/mysql/database_credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func DatabaseCredentials() schema.CredentialType {
Optional: true,
},
},
DefaultProvisioner: provision.TempFile(mysqlConfig, provision.Filename("my.cnf"), provision.AddArgs("--defaults-file={{ .Path }}")),
DefaultProvisioner: provision.TempFile(mysqlConfig, provision.Filename("my.cnf"), provision.AppendArgs("--defaults-file={{ .Path }}")),
Importer: importer.TryAll(
TryMySQLConfigFile("/etc/my.cnf"),
TryMySQLConfigFile("/etc/mysql/my.cnf"),
Expand Down
49 changes: 40 additions & 9 deletions sdk/provision/file_provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,20 @@ type FileProvisioner struct {
outpathFixed string
outpathEnvVar string
outdirEnvVar string
setOutpathAsArg bool
argPlacementMode ArgPlacementMode
outpathArgTemplates []string
}

type ItemToFileContents func(in sdk.ProvisionInput) ([]byte, error)

type ArgPlacementMode int

const (
NotSet ArgPlacementMode = iota
Prepend
Append
)

// FieldAsFile can be used to store the value of a single field as a file.
func FieldAsFile(fieldName sdk.FieldName) ItemToFileContents {
return ItemToFileContents(func(in sdk.ProvisionInput) ([]byte, error) {
Expand Down Expand Up @@ -83,14 +91,30 @@ func SetOutputDirAsEnvVar(envVarName string) FileOption {
}
}

// AddArgs can be used to add args to the command line. This is useful when the output file path
// should be passed as an arg. The output path is available as "{{ .Path }}" in each arg.
// AppendArgs appends arguments to the command line for a FileProvisioner.
// This is particularly useful when you need to add arguments that reference the output file path.
// The output path is available as "{{ .Path }}" within the provided argument templates.
// For example:
// * `AppendArgs("--log", "{{ .Path }}")` results in `--log /path/to/tempfile`.
// * `AppendArgs("--log={{ .Path }}")` results in `--log=/path/to/tempfile`.
func AppendArgs(argTemplates ...string) FileOption {
return func(p *FileProvisioner) {
p.argPlacementMode = Append
p.outpathArgTemplates = argTemplates
}
}

// PrependArgs prepends arguments to the command line for a FileProvisioner.
// This is particularly useful when you need to add arguments that reference the output file path.
// The output path is available as "{{ .Path }}" within the provided argument templates.
// For example:
// * `AddArgs("--config-file", "{{ .Path }}")` will result in `--config-file /path/to/tempfile`.
// * `AddArgs("--config-file={{ .Path }}")` will result in `--config-file=/path/to/tempfile`.
func AddArgs(argTemplates ...string) FileOption {
// * `PrependArgs("--input", "{{ .Path }}")` results in `--input /path/to/tempfile`.
// * `PrependArgs("--input={{ .Path }}")` results in `--input=/path/to/tempfile`.
//
// The arguments provided are added before any pre-existing arguments in the command line, but after the command itself.
func PrependArgs(argTemplates ...string) FileOption {
return func(p *FileProvisioner) {
p.setOutpathAsArg = true
p.argPlacementMode = Prepend
p.outpathArgTemplates = argTemplates
}
}
Expand Down Expand Up @@ -134,7 +158,7 @@ func (p FileProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, o
}

// Add args to specify the output path.
if p.setOutpathAsArg {
if p.argPlacementMode != NotSet {
tmplData := struct{ Path string }{
Path: outpath,
}
Expand All @@ -159,7 +183,14 @@ func (p FileProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, o
argsResolved[i] = result.String()
}

out.AddArgs(argsResolved...)
switch p.argPlacementMode {
case Append:
out.AppendArgs(argsResolved...)
case Prepend:
out.PrependArgs(argsResolved...)
default:
out.AddError(fmt.Errorf("invalid argument placement mode"))
}
}
}

Expand Down
30 changes: 30 additions & 0 deletions sdk/provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,36 @@ func (out *ProvisionOutput) AddEnvVar(name string, value string) {
out.Environment[name] = value
}

// AddArgsAtIndex inserts additional arguments into the command line at the specified index.
// - If position is -1 or greater than the current length, the arguments are appended.
// - If position is 0 or negative, the arguments are prepended.
// - Otherwise, the arguments are inserted at the specified index, shifting existing elements forward.
func (out *ProvisionOutput) AddArgsAtIndex(position int, args ...string) {
if position == -1 || position >= len(out.CommandLine) {
out.CommandLine = append(out.CommandLine, args...)
return
}

if position <= 0 {
out.CommandLine = append(args, out.CommandLine...)
return
}

out.CommandLine = append(out.CommandLine[:position], append(args, out.CommandLine[position:]...)...)
}

// PrependArgs inserts additional arguments at the beginning of the command line, after the first argument.
// This ensures that the first argument (typically the executable name) remains unchanged.
func (out *ProvisionOutput) PrependArgs(args ...string) {
out.AddArgsAtIndex(1, args...)
}

// AppendArgs appends additional arguments to the end of the command line.
// This ensures that new arguments are always added last, preserving existing order.
func (out *ProvisionOutput) AppendArgs(args ...string) {
out.AddArgsAtIndex(-1, args...)
}

// AddArgs can be used to add additional arguments to the command line of the provision output.
func (out *ProvisionOutput) AddArgs(args ...string) {
out.CommandLine = append(out.CommandLine, args...)
Expand Down
58 changes: 58 additions & 0 deletions sdk/provisioner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sdk

import (
"encoding/json"
"reflect"
"testing"
"time"

Expand Down Expand Up @@ -122,3 +123,60 @@ func TestCacheOperationsPutStruct(t *testing.T) {

assert.Equal(t, structData, structResult)
}

func TestProvisionOutputAddArgsAtIndex(t *testing.T) {
tc := []struct {
name string
initial []string
position int
args []string
expected []string
}{
{
name: "Insert at the beginning",
initial: []string{"arg2", "arg3"},
position: 0,
args: []string{"arg1"},
expected: []string{"arg1", "arg2", "arg3"},
},
{
name: "Insert in the middle",
initial: []string{"arg1", "arg3"},
position: 1,
args: []string{"arg2"},
expected: []string{"arg1", "arg2", "arg3"},
},
{
name: "Insert at the end",
initial: []string{"arg1", "arg2"},
position: -1,
args: []string{"arg3"},
expected: []string{"arg1", "arg2", "arg3"},
},
{
name: "Append at out-of-range index",
initial: []string{"arg1", "arg2"},
position: 5,
args: []string{"arg3"},
expected: []string{"arg1", "arg2", "arg3"},
},
{
name: "Insert at negative index (should prepend)",
initial: []string{"arg2", "arg3"},
position: -5,
args: []string{"arg1"},
expected: []string{"arg1", "arg2", "arg3"},
},
}

for _, tc := range tc {
t.Run(tc.name, func(t *testing.T) {
out := ProvisionOutput{CommandLine: append([]string{}, tc.initial...)}
out.AddArgsAtIndex(tc.position, tc.args...)

if !reflect.DeepEqual(out.CommandLine, tc.expected) {
t.Errorf("expected %v, got %v", tc.expected, out.CommandLine)
}
})
}
}