Skip to content

Commit

Permalink
feat: Support setting variables on applications.
Browse files Browse the repository at this point in the history
  • Loading branch information
paladin-devops committed Oct 16, 2024
1 parent 2a17c0c commit f8982d7
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .changelog/172.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
waypoint: Support setting variables when creating apps.
```
3 changes: 3 additions & 0 deletions internal/commands/waypoint/applications/applications.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ type ApplicationOpts struct {
ActionConfigNames []string
ReadmeMarkdownFile string

Variables map[string]string
VariablesFile string

testFunc func(c *cmd.Command, args []string) error
}

Expand Down
60 changes: 60 additions & 0 deletions internal/commands/waypoint/applications/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/hashicorp/hcp-sdk-go/clients/cloud-waypoint-service/preview/2023-08-18/client/waypoint_service"
"github.com/hashicorp/hcp-sdk-go/clients/cloud-waypoint-service/preview/2023-08-18/models"
"github.com/hashicorp/hcp/internal/commands/waypoint/internal"
"github.com/hashicorp/hcp/internal/pkg/cmd"
"github.com/hashicorp/hcp/internal/pkg/flagvalue"
"github.com/hashicorp/hcp/internal/pkg/heredoc"
Expand Down Expand Up @@ -65,6 +66,27 @@ $ hcp waypoint application create -n=my-application -t=my-template
Required: false,
Repeatable: true,
},
{
Name: "var",
DisplayValue: "KEY=VALUE",
Description: "A variable to be used in the application. The" +
" flag can be repeated to specify multiple variables. " +
"Variables specified with the flag will override " +
"variables specified in a file.",
Value: flagvalue.SimpleMap(nil, &opts.Variables),
Required: false,
Repeatable: true,
},
{
Name: "var-file",
DisplayValue: "FILE",
Description: "A file containing variables to be used in the " +
"application. The file should be in HCL format Variables" +
" in the file will be overridden by variables specified" +
" with the --var flag.",
Value: flagvalue.Simple("", &opts.VariablesFile),
Required: false,
},
},
},
}
Expand All @@ -85,6 +107,43 @@ func applicationCreate(opts *ApplicationOpts) error {
}
}

// Variable Processing

// a map is used with the key being the variable name, so that
// flags can override file values.
ivs := make(map[string]*models.HashicorpCloudWaypointInputVariable)
if opts.VariablesFile != "" {
variables, err := internal.ParseInputVariablesFile(opts.VariablesFile)
if err != nil {
return errors.Wrapf(err, "%s failed to parse input variables file %q",
opts.IO.ColorScheme().FailureIcon(),
opts.VariablesFile,
)
}
for _, v := range variables {
ivs[v.Name] = &models.HashicorpCloudWaypointInputVariable{
Name: v.Name,
Value: v.Value,
}
}
}

// Flags are processed second, so that they can override file values.
// Flags take precedence over file values.
for k, v := range opts.Variables {
ivs[k] = &models.HashicorpCloudWaypointInputVariable{
Name: k,
Value: v,
}
}

var vars []*models.HashicorpCloudWaypointInputVariable
for _, v := range ivs {
vars = append(vars, v)
}

// End Variable Processing

_, err = opts.WS.WaypointServiceCreateApplicationFromTemplate(
&waypoint_service.WaypointServiceCreateApplicationFromTemplateParams{
NamespaceID: ns.ID,
Expand All @@ -95,6 +154,7 @@ func applicationCreate(opts *ApplicationOpts) error {
Name: opts.TemplateName,
},
ActionCfgRefs: actionConfigs,
Variables: vars,
},
}, nil)
if err != nil {
Expand Down
11 changes: 10 additions & 1 deletion internal/commands/waypoint/applications/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,19 @@ func TestNewCmdCreateApplication(t *testing.T) {
{
Name: "Happy",
Profile: profile.TestProfile,
Args: []string{"-n", "app-name", "-t", "templates-name"},
Args: []string{
"-n", "app-name",
"-t", "templates-name",
"--var", "var1=val1",
"--var-file", "vars.hcl",
},
Expect: &ApplicationOpts{
Name: "app-name",
TemplateName: "templates-name",
Variables: map[string]string{
"var1": "val1",
},
VariablesFile: "vars.hcl",
},
},
}
Expand Down
45 changes: 45 additions & 0 deletions internal/commands/waypoint/internal/input_variables.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package internal

import (
"os"
"sort"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsimple"
"github.com/hashicorp/hcp-sdk-go/clients/cloud-waypoint-service/preview/2023-08-18/models"
)

func ParseInputVariablesFile(path string) ([]*models.HashicorpCloudWaypointInputVariable, error) {
input, err := os.ReadFile(path)
if err != nil {
return nil, err
}
return parseInputVariables(path, input)
}

func parseInputVariables(filename string, input []byte) ([]*models.HashicorpCloudWaypointInputVariable, error) {
var hc hclInputVariablesFile
var ctx hcl.EvalContext
if err := hclsimple.Decode(filename, input, &ctx, &hc); err != nil {
return nil, err
}

var variables []*models.HashicorpCloudWaypointInputVariable
if len(hc.Variables) > 0 {
for k, v := range hc.Variables {
variables = append(variables, &models.HashicorpCloudWaypointInputVariable{
Name: k,
Value: v,
})
}
}

sort.Slice(variables, func(i, j int) bool {
return variables[i].Name < variables[j].Name
})
return variables, nil
}

type hclInputVariablesFile struct {
Variables map[string]string `hcl:"variables,remain"`
}
74 changes: 74 additions & 0 deletions internal/commands/waypoint/internal/input_variables_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package internal

import (
"testing"

"github.com/stretchr/testify/require"
)

func Test_InputVariablesFile(t *testing.T) {
t.Parallel()

t.Run("can parse variables", func(t *testing.T) {
t.Parallel()

r := require.New(t)

hcl := `
key="value"
key2="value2"
`

inputVars, err := parseInputVariables("blah.hcl", []byte(hcl))
r.NoError(err)
r.Equal(2, len(inputVars))
r.Equal("key", inputVars[0].Name)
r.Equal("value", inputVars[0].Value)
r.Equal("key2", inputVars[1].Name)
r.Equal("value2", inputVars[1].Value)
})

t.Run("handles empty file", func(t *testing.T) {
t.Parallel()

r := require.New(t)

hcl := ``

inputVars, err := parseInputVariables("blah.hcl", []byte(hcl))
r.NoError(err)
r.Equal(0, len(inputVars))
})

t.Run("handles empty values", func(t *testing.T) {
t.Parallel()

r := require.New(t)

hcl := `
key=""
key2=""
`

inputVars, err := parseInputVariables("blah.hcl", []byte(hcl))
r.NoError(err)
r.Equal(2, len(inputVars))
r.Equal("key", inputVars[0].Name)
r.Equal("", inputVars[0].Value)
r.Equal("key2", inputVars[1].Name)
r.Equal("", inputVars[1].Value)
})

t.Run("fail to parse", func(t *testing.T) {
t.Parallel()

r := require.New(t)

hcl := `
key======
`

_, err := parseInputVariables("blah.hcl", []byte(hcl))
r.Error(err)
})
}

0 comments on commit f8982d7

Please sign in to comment.