Skip to content

Commit

Permalink
Merge pull request #44 from hashicorp/WAYP-2344-add-on-definitions
Browse files Browse the repository at this point in the history
`hcp waypoint add-ons definitions`
  • Loading branch information
paladin-devops authored Mar 28, 2024
2 parents 0de1805 + 115f515 commit caf8075
Show file tree
Hide file tree
Showing 16 changed files with 1,082 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .changelog/44.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
waypoint: Add `hcp waypoint add-ons definitions` CLI for managing HCP Waypoint add-on definitions.
```
21 changes: 21 additions & 0 deletions internal/commands/waypoint/add-on/add_on.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package addon

import (
"github.com/hashicorp/hcp/internal/pkg/cmd"
"github.com/hashicorp/hcp/internal/pkg/heredoc"
)

func NewCmdAddOn(ctx *cmd.Context) *cmd.Command {
cmd := &cmd.Command{
Name: "add-ons",
ShortHelp: "Manage HCP Waypoint add-ons and add-on definitions.",
LongHelp: heredoc.New(ctx.IO).Must(`
The {{ template "mdCodeOrBold" "hcp waypoint add-ons" }} command group lets you
manage HCP Waypoint add-ons and add-on definitions.
`),
}

cmd.AddChild(NewCmdAddOnDefinition(ctx))

return cmd
}
49 changes: 49 additions & 0 deletions internal/commands/waypoint/add-on/add_on_definition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package addon

import (
"github.com/hashicorp/hcp/internal/commands/waypoint/opts"
"github.com/hashicorp/hcp/internal/pkg/cmd"
"github.com/hashicorp/hcp/internal/pkg/heredoc"
)

type AddOnDefinitionOpts struct {
opts.WaypointOpts

Name string
Summary string
Description string
ReadmeMarkdownTemplateFile string
Labels []string

TerraformNoCodeModuleSource string
TerraformNoCodeModuleVersion string
TerraformCloudProjectName string
TerraformCloudProjectID string

// testFunc is used for testing, so that the command can be tested without
// using the real API.
testFunc func(c *cmd.Command, args []string) error
}

func NewCmdAddOnDefinition(ctx *cmd.Context) *cmd.Command {
opts := &AddOnDefinitionOpts{
WaypointOpts: opts.New(ctx),
}

cmd := &cmd.Command{
Name: "definitions",
ShortHelp: "Manage HCP Waypoint add-on definitions.",
LongHelp: heredoc.New(ctx.IO).Must(`
The {{ template "mdCodeOrBold" "hcp waypoint add-ons definitions" }} command
group lets you manage HCP Waypoint add-on definitions.
`),
}

cmd.AddChild(NewCmdAddOnDefinitionCreate(ctx, opts))
cmd.AddChild(NewCmdAddOnDefinitionDelete(ctx, opts))
cmd.AddChild(NewCmdAddOnDefinitionList(ctx, opts))
cmd.AddChild(NewCmdAddOnDefinitionRead(ctx, opts))
cmd.AddChild(NewCmdAddOnDefinitionUpdate(ctx, opts))

return cmd
}
169 changes: 169 additions & 0 deletions internal/commands/waypoint/add-on/add_on_definition_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package addon

import (
"fmt"
"os"

"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/pkg/cmd"
"github.com/hashicorp/hcp/internal/pkg/flagvalue"
"github.com/hashicorp/hcp/internal/pkg/heredoc"
"github.com/pkg/errors"
)

func NewCmdAddOnDefinitionCreate(ctx *cmd.Context, opts *AddOnDefinitionOpts) *cmd.Command {
cmd := &cmd.Command{
Name: "create",
ShortHelp: "Create a new HCP Waypoint add-on definition.",
LongHelp: heredoc.New(ctx.IO).Must(`
The {{ template "mdCodeOrBold" "hcp waypoint add-ons definitions create" }}
command lets you create HCP Waypoint add-on definitions.
`),
Examples: []cmd.Example{
{
Preamble: "Create a new HCP Waypoint add-on definition:",
Command: heredoc.New(ctx.IO, heredoc.WithPreserveNewlines()).Must(`
$ hcp waypoint add-ons definitions create -n my-add-on-definition \
-s "My Add-on Definition summary." \
-d "My Add-on Definition description." \
--readme-markdown-template-file "README.tpl" \
--tfc-no-code-module-source "app.terraform.io/hashicorp/dir/template" \
--tfc-no-code-module-version "1.0.2" \
--tfc-project-name "my-tfc-project" \
--tfc-project-id "prj-123456" \
-l label1 \
-l label2
`),
},
},
RunF: func(c *cmd.Command, args []string) error {
if opts.testFunc != nil {
return opts.testFunc(c, args)
}
return addOnDefinitionCreate(opts)
},
PersistentPreRun: func(c *cmd.Command, args []string) error {
return cmd.RequireOrgAndProject(ctx)
},
Flags: cmd.Flags{
Local: []*cmd.Flag{
{
Name: "name",
Shorthand: "n",
DisplayValue: "NAME",
Description: "The name of the add-on definition.",
Value: flagvalue.Simple("", &opts.Name),
},
{
Name: "summary",
Shorthand: "s",
DisplayValue: "SUMMARY",
Description: "The summary of the add-on definition.",
Value: flagvalue.Simple("", &opts.Summary),
},
{
Name: "description",
Shorthand: "d",
DisplayValue: "DESCRIPTION",
Description: "The description of the add-on definition.",
Value: flagvalue.Simple("", &opts.Description),
},
{
Name: "readme-markdown-template-file",
DisplayValue: "README_MARKDOWN_TEMPLATE_FILE_PATH",
Description: "The file containing the README markdown template.",
Value: flagvalue.Simple("", &opts.ReadmeMarkdownTemplateFile),
},
{
Name: "label",
Shorthand: "l",
DisplayValue: "LABEL",
Description: "A label to apply to the add-on definition.",
Repeatable: true,
Value: flagvalue.SimpleSlice(nil, &opts.Labels),
},
{
Name: "tfc-no-code-module-source",
DisplayValue: "TFC_NO_CODE_MODULE_SOURCE",
Description: heredoc.New(ctx.IO).Must(`
The source of the Terraform no-code module.
The expected format is "NAMESPACE/NAME/PROVIDER". An
optional "HOSTNAME/" can be added at the beginning for
a private registry.
`),
Value: flagvalue.Simple("", &opts.TerraformNoCodeModuleSource),
Required: true,
},
{
Name: "tfc-no-code-module-version",
DisplayValue: "TFC_NO_CODE_MODULE_VERSION",
Description: "The version of the Terraform no-code module.",
Value: flagvalue.Simple("", &opts.TerraformNoCodeModuleVersion),
Required: true,
},
{
Name: "tfc-project-name",
DisplayValue: "TFC_PROJECT_NAME",
Description: "The name of the Terraform Cloud project where" +
" applications using this add-on definition will be created.",
Value: flagvalue.Simple("", &opts.TerraformCloudProjectName),
Required: true,
},
{
Name: "tfc-project-id",
DisplayValue: "TFC_PROJECT_ID",
Description: "The ID of the Terraform Cloud project where" +
" applications using this add-on definition will be created.",
Value: flagvalue.Simple("", &opts.TerraformCloudProjectID),
Required: true,
},
},
},
}

return cmd
}

func addOnDefinitionCreate(opts *AddOnDefinitionOpts) error {
ns, err := opts.Namespace()
if err != nil {
return errors.Wrapf(err, "Unable to access HCP project")
}

var readmeTpl []byte
if opts.ReadmeMarkdownTemplateFile != "" {
readmeTpl, err = os.ReadFile(opts.ReadmeMarkdownTemplateFile)
if err != nil {
return errors.Wrapf(err, "failed to read README markdown template file %q", opts.ReadmeMarkdownTemplateFile)
}
}

_, err = opts.WS.WaypointServiceCreateAddOnDefinition(
&waypoint_service.WaypointServiceCreateAddOnDefinitionParams{
NamespaceID: ns.ID,
Body: &models.HashicorpCloudWaypointWaypointServiceCreateAddOnDefinitionBody{
Name: opts.Name,
Summary: opts.Summary,
Description: opts.Description,
ReadmeMarkdownTemplate: readmeTpl,
Labels: opts.Labels,
TerraformNocodeModule: &models.HashicorpCloudWaypointTerraformNocodeModule{
Source: opts.TerraformNoCodeModuleSource,
Version: opts.TerraformNoCodeModuleVersion,
},
TerraformCloudWorkspaceDetails: &models.HashicorpCloudWaypointTerraformCloudWorkspaceDetails{
Name: opts.TerraformCloudProjectName,
ProjectID: opts.TerraformCloudProjectID,
},
},
Context: opts.Ctx,
}, nil)
if err != nil {
return errors.Wrapf(err, "failed to create add-on definition %q", opts.Name)
}

fmt.Fprintf(opts.IO.Err(), "Add-on definition %q created.", opts.Name)

return nil
}
110 changes: 110 additions & 0 deletions internal/commands/waypoint/add-on/add_on_definition_create_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package addon

import (
"context"
"testing"

"github.com/go-openapi/runtime/client"
"github.com/hashicorp/hcp/internal/pkg/cmd"
"github.com/hashicorp/hcp/internal/pkg/format"
"github.com/hashicorp/hcp/internal/pkg/iostreams"
"github.com/hashicorp/hcp/internal/pkg/profile"
"github.com/stretchr/testify/require"
)

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

cases := []struct {
Name string
Args []string
Profile func(t *testing.T) *profile.Profile
Error string
Expect *AddOnDefinitionOpts
}{
{
Name: "No Org",
Profile: profile.TestProfile,
Args: []string{},
Error: "Organization ID must be configured",
},
{
Name: "no args",
Profile: func(t *testing.T) *profile.Profile {
return profile.TestProfile(t).SetOrgID("123")
},
Args: []string{},
Error: "accepts 1 arg(s), received 0",
},
{
Name: "happy",
Profile: func(t *testing.T) *profile.Profile {
return profile.TestProfile(t).SetOrgID("123")
},
Args: []string{
"-n=cli-test",
"-s", "An add-on definition created using the CLI.",
"--tfc-project-id", "prj-abcdefghij",
"--tfc-project-name", "test",
"--tfc-no-code-module-source", "private/waypoint/waypoint-nocode-module/null",
"--tfc-no-code-module-version", "0.0.1",
"-l", "cli",
"-d", "An add-on definition created with the CLI.",
"--readme-markdown-template-file", "readme_test.txt",
},
Expect: &AddOnDefinitionOpts{
Name: "cli-test",
Summary: "An add-on definition created using the CLI.",
Description: "An add-on definition created with the CLI.",
TerraformCloudProjectID: "prj-abcdefghij",
TerraformCloudProjectName: "test",
TerraformNoCodeModuleSource: "private/waypoint/waypoint-nocode-module/null",
TerraformNoCodeModuleVersion: "0.0.1",
Labels: []string{"cli"},
ReadmeMarkdownTemplateFile: "readme_test.txt",
},
},
}

for _, c := range cases {
c := c
t.Run(c.Name, func(t *testing.T) {
t.Parallel()

r := require.New(t)

// Create a context.
io := iostreams.Test()
ctx := &cmd.Context{
IO: io,
Profile: c.Profile(t),
Output: format.New(io),
HCP: &client.Runtime{},
ShutdownCtx: context.Background(),
}

var aodOpts AddOnDefinitionOpts
aodOpts.testFunc = func(c *cmd.Command, args []string) error {
return nil
}
cmd := NewCmdAddOnDefinitionCreate(ctx, &aodOpts)
cmd.SetIO(io)

cmd.Run(c.Args)

if c.Expect != nil {
r.NotNil(c.Expect)

r.Equal(c.Expect.Name, aodOpts.Name)
r.Equal(c.Expect.Description, aodOpts.Description)
r.Equal(c.Expect.Summary, aodOpts.Summary)
r.Equal(c.Expect.TerraformCloudProjectID, aodOpts.TerraformCloudProjectID)
r.Equal(c.Expect.TerraformCloudProjectName, aodOpts.TerraformCloudProjectName)
r.Equal(c.Expect.TerraformNoCodeModuleSource, aodOpts.TerraformNoCodeModuleSource)
r.Equal(c.Expect.TerraformNoCodeModuleVersion, aodOpts.TerraformNoCodeModuleVersion)
r.Equal(c.Expect.ReadmeMarkdownTemplateFile, aodOpts.ReadmeMarkdownTemplateFile)
r.Equal(c.Expect.Labels, aodOpts.Labels)
}
})
}
}
Loading

0 comments on commit caf8075

Please sign in to comment.