Skip to content

Commit

Permalink
[CLI-3190] Add CRUD for confluent network gateway commands (#2916)
Browse files Browse the repository at this point in the history
Co-authored-by: Brian Strauch <[email protected]>
  • Loading branch information
sgagniere and brianstrauch authored Oct 28, 2024
1 parent 00a35f0 commit 3962325
Show file tree
Hide file tree
Showing 32 changed files with 643 additions and 117 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ require (
github.com/confluentinc/ccloud-sdk-go-v2/networking v0.12.0
github.com/confluentinc/ccloud-sdk-go-v2/networking-access-point v0.3.0
github.com/confluentinc/ccloud-sdk-go-v2/networking-dnsforwarder v0.2.0
github.com/confluentinc/ccloud-sdk-go-v2/networking-gateway v0.2.0
github.com/confluentinc/ccloud-sdk-go-v2/networking-ip v0.2.0
github.com/confluentinc/ccloud-sdk-go-v2/networking-privatelink v0.2.0
github.com/confluentinc/ccloud-sdk-go-v2/org v0.9.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ github.com/confluentinc/ccloud-sdk-go-v2/networking-access-point v0.3.0 h1:8STgb
github.com/confluentinc/ccloud-sdk-go-v2/networking-access-point v0.3.0/go.mod h1:HV1xGUwTsGEU3Mgvc+7Ya/0HRpUO69L2rqqxO7LeWMc=
github.com/confluentinc/ccloud-sdk-go-v2/networking-dnsforwarder v0.2.0 h1:OdIeCGfy8iQ2Bm+08CDXYttwZOUme0e9FVGrBjBJGx4=
github.com/confluentinc/ccloud-sdk-go-v2/networking-dnsforwarder v0.2.0/go.mod h1:472T8ufudvXgXea2BhYxhE/2eowwhoulZzboDh6+ec4=
github.com/confluentinc/ccloud-sdk-go-v2/networking-gateway v0.2.0 h1:a3eTOQy6tlbcZIFzcAKQPoZALi6Csg4phlCQLPyUdsA=
github.com/confluentinc/ccloud-sdk-go-v2/networking-gateway v0.2.0/go.mod h1:zDswAVbulZWmPVEREWPU6jvwi5E7Q95SdY7abYdqVfA=
github.com/confluentinc/ccloud-sdk-go-v2/networking-ip v0.2.0 h1:ZHNF2DeqVlNPuKGZ41SBMLGj8GBlvvcwOPnfZLZXA/4=
github.com/confluentinc/ccloud-sdk-go-v2/networking-ip v0.2.0/go.mod h1:KTShFBZA7WG8LcxlWjJpoZFdWkJ+uOw3dDuwAHs5eKU=
github.com/confluentinc/ccloud-sdk-go-v2/networking-privatelink v0.2.0 h1:3nm/8KnxWeEL4djyGs293g++4xvOGKUNklf5J7MYsas=
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package network

import (
"fmt"
"strings"

"github.com/spf13/cobra"
Expand All @@ -11,7 +10,6 @@ import (
pcloud "github.com/confluentinc/cli/v4/pkg/cloud"
pcmd "github.com/confluentinc/cli/v4/pkg/cmd"
"github.com/confluentinc/cli/v4/pkg/examples"
"github.com/confluentinc/cli/v4/pkg/utils"
)

func (c *accessPointCommand) newCreateCommand() *cobra.Command {
Expand All @@ -32,7 +30,7 @@ func (c *accessPointCommand) newCreateCommand() *cobra.Command {
),
}

cmd.Flags().String("cloud", "", fmt.Sprintf("Specify the cloud provider as %s.", utils.ArrayToCommaDelimitedString([]string{"aws", "azure"}, "or")))
pcmd.AddCloudAwsAzureFlag(cmd)
cmd.Flags().String("service", "", "Name of an AWS VPC endpoint service or ID of an Azure Private Link service.")
addGatewayFlag(cmd, c.AuthenticatedCLICommand)
cmd.Flags().String("subresource", "", "Name of an Azure Private Link subresource.")
Expand Down
106 changes: 103 additions & 3 deletions internal/network/command_gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,43 @@ import (

"github.com/spf13/cobra"

networkingv1 "github.com/confluentinc/ccloud-sdk-go-v2/networking/v1"
networkinggatewayv1 "github.com/confluentinc/ccloud-sdk-go-v2/networking-gateway/v1"

"github.com/confluentinc/cli/v4/pkg/ccloudv2"
pcloud "github.com/confluentinc/cli/v4/pkg/cloud"
pcmd "github.com/confluentinc/cli/v4/pkg/cmd"
"github.com/confluentinc/cli/v4/pkg/errors"
"github.com/confluentinc/cli/v4/pkg/network"
"github.com/confluentinc/cli/v4/pkg/output"
"github.com/confluentinc/cli/v4/pkg/utils"
)

const (
awsEgressPrivateLink = "AwsEgressPrivateLink"
awsPeering = "AwsPeering"
azureEgressPrivateLink = "AzureEgressPrivateLink"
azurePeering = "AzurePeering"
)

var (
createGatewayTypes = []string{"egress-privatelink"}
listGatewayTypes = []string{"aws-egress-privatelink", "azure-egress-privatelink"}
gatewayTypeMap = map[string]string{
"aws-egress-privatelink": awsEgressPrivateLink,
"azure-egress-privatelink": azureEgressPrivateLink,
}
)

type gatewayOut struct {
Id string `human:"ID" serialized:"id"`
Name string `human:"Name,omitempty" serialized:"name,omitempty"`
Environment string `human:"Environment" serialized:"environment"`
Region string `human:"Region,omitempty" serialized:"region,omitempty"`
Type string `human:"Type,omitempty" serialized:"type,omitempty"`
AwsPrincipalArn string `human:"AWS Principal ARN,omitempty" serialized:"aws_principal_arn,omitempty"`
AzureSubscription string `human:"Azure Subscription,omitempty" serialized:"azure_subscription,omitempty"`
Phase string `human:"Phase" serialized:"phase"`
ErrorMessage string `human:"Error Message,omitempty" serialized:"error_message,omitempty"`
}

func (c *command) newGatewayCommand() *cobra.Command {
Expand All @@ -28,17 +51,49 @@ func (c *command) newGatewayCommand() *cobra.Command {
Args: cobra.NoArgs,
}

cmd.AddCommand(c.newGatewayCreateCommand())
cmd.AddCommand(c.newGatewayDeleteCommand())
cmd.AddCommand(c.newGatewayDescribeCommand())
cmd.AddCommand(c.newGatewayListCommand())
cmd.AddCommand(c.newGatewayUpdateCommand())

return cmd
}

func addGatewayTypeFlag(cmd *cobra.Command) {
cmd.Flags().String("type", "", fmt.Sprintf("Specify the gateway type as %s.", utils.ArrayToCommaDelimitedString(createGatewayTypes, "or")))
pcmd.RegisterFlagCompletionFunc(cmd, "type", func(_ *cobra.Command, _ []string) []string { return createGatewayTypes })
}

func (c *command) addRegionFlagGateway(cmd *cobra.Command, command *pcmd.AuthenticatedCLICommand) {
cmd.Flags().String("region", "", "AWS or Azure region of the gateway.")
pcmd.RegisterFlagCompletionFunc(cmd, "region", func(cmd *cobra.Command, args []string) []string {
if err := c.PersistentPreRunE(cmd, args); err != nil {
return nil
}

cloud, _ := cmd.Flags().GetString("cloud")
regions, err := network.ListRegions(command.Client, cloud)
if err != nil {
return nil
}

suggestions := make([]string, len(regions))
for i, region := range regions {
suggestions[i] = region.RegionId
}
return suggestions
})
}

func (c *command) validGatewayArgs(cmd *cobra.Command, args []string) []string {
if len(args) > 0 {
return nil
}
return c.validGatewayArgsMultiple(cmd, args)
}

func (c *command) validGatewayArgsMultiple(cmd *cobra.Command, args []string) []string {
if err := c.PersistentPreRunE(cmd, args); err != nil {
return nil
}
Expand All @@ -52,7 +107,7 @@ func (c *command) validGatewayArgs(cmd *cobra.Command, args []string) []string {
}

func autocompleteGateways(client *ccloudv2.Client, environmentId string) []string {
gateways, err := client.ListGateways(environmentId)
gateways, err := client.ListGateways(environmentId, nil)
if err != nil {
return nil
}
Expand All @@ -64,7 +119,7 @@ func autocompleteGateways(client *ccloudv2.Client, environmentId string) []strin
return suggestions
}

func getGatewayCloud(gateway networkingv1.NetworkingV1Gateway) string {
func getGatewayCloud(gateway networkinggatewayv1.NetworkingV1Gateway) string {
cloud := gateway.Status.GetCloudGateway()

if cloud.NetworkingV1AwsEgressPrivateLinkGatewayStatus != nil {
Expand All @@ -77,3 +132,48 @@ func getGatewayCloud(gateway networkingv1.NetworkingV1Gateway) string {

return ""
}

func printGatewayTable(cmd *cobra.Command, gateway networkinggatewayv1.NetworkingV1Gateway) error {
if gateway.Spec == nil {
return fmt.Errorf(errors.CorruptedNetworkResponseErrorMsg, "spec")
}
if gateway.Status == nil {
return fmt.Errorf(errors.CorruptedNetworkResponseErrorMsg, "status")
}

out := &gatewayOut{
Id: gateway.GetId(),
Name: gateway.Spec.GetDisplayName(),
Environment: gateway.Spec.Environment.GetId(),
Phase: gateway.Status.GetPhase(),
ErrorMessage: gateway.Status.GetErrorMessage(),
}

if gateway.Spec.Config != nil && gateway.Spec.Config.NetworkingV1AwsEgressPrivateLinkGatewaySpec != nil {
out.Region = gateway.Spec.Config.NetworkingV1AwsEgressPrivateLinkGatewaySpec.GetRegion()
out.Type = awsEgressPrivateLink
}
if gateway.Spec.Config != nil && gateway.Spec.Config.NetworkingV1AwsPeeringGatewaySpec != nil {
out.Region = gateway.Spec.Config.NetworkingV1AwsPeeringGatewaySpec.GetRegion()
out.Type = awsPeering
}
if gateway.Spec.Config != nil && gateway.Spec.Config.NetworkingV1AzureEgressPrivateLinkGatewaySpec != nil {
out.Region = gateway.Spec.Config.NetworkingV1AzureEgressPrivateLinkGatewaySpec.GetRegion()
out.Type = azureEgressPrivateLink
}
if gateway.Spec.Config != nil && gateway.Spec.Config.NetworkingV1AzurePeeringGatewaySpec != nil {
out.Region = gateway.Spec.Config.NetworkingV1AzurePeeringGatewaySpec.GetRegion()
out.Type = azurePeering
}

switch getGatewayCloud(gateway) {
case pcloud.Aws:
out.AwsPrincipalArn = gateway.Status.CloudGateway.NetworkingV1AwsEgressPrivateLinkGatewayStatus.GetPrincipalArn()
case pcloud.Azure:
out.AzureSubscription = gateway.Status.CloudGateway.NetworkingV1AzureEgressPrivateLinkGatewayStatus.GetSubscription()
}

table := output.NewTable(cmd)
table.Add(out)
return table.Print()
}
102 changes: 102 additions & 0 deletions internal/network/command_gateway_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package network

import (
"strings"

"github.com/spf13/cobra"

networkinggatewayv1 "github.com/confluentinc/ccloud-sdk-go-v2/networking-gateway/v1"

pcloud "github.com/confluentinc/cli/v4/pkg/cloud"
pcmd "github.com/confluentinc/cli/v4/pkg/cmd"
"github.com/confluentinc/cli/v4/pkg/examples"
)

func (c *command) newGatewayCreateCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "create [name]",
Short: "Create a network gateway.",
Args: cobra.MaximumNArgs(1),
RunE: c.gatewayCreate,
Example: examples.BuildExampleString(
examples.Example{
Text: `Create network gateway "my-gateway".`,
Code: "confluent network gateway create my-gateway --cloud aws --region us-east-1 --type egress-privatelink",
},
),
}

pcmd.AddCloudAwsAzureFlag(cmd)
addGatewayTypeFlag(cmd)
c.addRegionFlagGateway(cmd, c.AuthenticatedCLICommand)
pcmd.AddContextFlag(cmd, c.CLICommand)
pcmd.AddEnvironmentFlag(cmd, c.AuthenticatedCLICommand)
pcmd.AddOutputFlag(cmd)

cobra.CheckErr(cmd.MarkFlagRequired("cloud"))
cobra.CheckErr(cmd.MarkFlagRequired("type"))
cobra.CheckErr(cmd.MarkFlagRequired("region"))

return cmd
}

func (c *command) gatewayCreate(cmd *cobra.Command, args []string) error {
cloud, err := cmd.Flags().GetString("cloud")
if err != nil {
return err
}
cloud = strings.ToUpper(cloud)

region, err := cmd.Flags().GetString("region")
if err != nil {
return err
}

gatewayType, err := cmd.Flags().GetString("type")
if err != nil {
return err
}

environmentId, err := c.Context.EnvironmentId()
if err != nil {
return err
}

createGateway := networkinggatewayv1.NetworkingV1Gateway{
Spec: &networkinggatewayv1.NetworkingV1GatewaySpec{
Environment: &networkinggatewayv1.ObjectReference{Id: environmentId},
},
}

switch cloud {
case pcloud.Aws:
if gatewayType == "egress-privatelink" {
createGateway.Spec.Config = &networkinggatewayv1.NetworkingV1GatewaySpecConfigOneOf{
NetworkingV1AwsEgressPrivateLinkGatewaySpec: &networkinggatewayv1.NetworkingV1AwsEgressPrivateLinkGatewaySpec{
Kind: "AwsEgressPrivateLinkGatewaySpec",
Region: region,
},
}
}
case pcloud.Azure:
if gatewayType == "egress-privatelink" {
createGateway.Spec.Config = &networkinggatewayv1.NetworkingV1GatewaySpecConfigOneOf{
NetworkingV1AzureEgressPrivateLinkGatewaySpec: &networkinggatewayv1.NetworkingV1AzureEgressPrivateLinkGatewaySpec{
Kind: "AzureEgressPrivateLinkGatewaySpec",
Region: region,
},
}
}
}

if len(args) == 1 {
createGateway.Spec.SetDisplayName(args[0])
}

gateway, err := c.V2Client.CreateGateway(createGateway)
if err != nil {
return err
}

return printGatewayTable(cmd, gateway)
}
63 changes: 63 additions & 0 deletions internal/network/command_gateway_delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package network

import (
"fmt"

"github.com/spf13/cobra"

pcmd "github.com/confluentinc/cli/v4/pkg/cmd"
"github.com/confluentinc/cli/v4/pkg/deletion"
"github.com/confluentinc/cli/v4/pkg/errors"
"github.com/confluentinc/cli/v4/pkg/output"
"github.com/confluentinc/cli/v4/pkg/resource"
"github.com/confluentinc/cli/v4/pkg/utils"
)

func (c *command) newGatewayDeleteCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "delete <id-1> [id-2] ... [id-n]",
Short: "Delete one or more gateways.",
Args: cobra.MinimumNArgs(1),
ValidArgsFunction: pcmd.NewValidArgsFunction(c.validGatewayArgsMultiple),
RunE: c.gatewayDelete,
}

pcmd.AddForceFlag(cmd)
pcmd.AddContextFlag(cmd, c.CLICommand)
pcmd.AddEnvironmentFlag(cmd, c.AuthenticatedCLICommand)

return cmd
}

func (c *command) gatewayDelete(cmd *cobra.Command, args []string) error {
environmentId, err := c.Context.EnvironmentId()
if err != nil {
return err
}

existenceFunc := func(id string) bool {
_, err := c.V2Client.GetGateway(environmentId, id)
return err == nil
}

if err := deletion.ValidateAndConfirm(cmd, args, existenceFunc, resource.Gateway); err != nil {
return err
}

deleteFunc := func(id string) error {
if err := c.V2Client.DeleteGateway(environmentId, id); err != nil {
return fmt.Errorf(errors.DeleteResourceErrorMsg, resource.Gateway, id, err)
}
return nil
}

deletedIds, err := deletion.DeleteWithoutMessage(args, deleteFunc)
deleteMsg := "Requested to delete %s %s.\n"
if len(deletedIds) == 1 {
output.Printf(c.Config.EnableColor, deleteMsg, resource.Gateway, fmt.Sprintf(`"%s"`, deletedIds[0]))
} else if len(deletedIds) > 1 {
output.Printf(c.Config.EnableColor, deleteMsg, resource.Plural(resource.Gateway), utils.ArrayToCommaDelimitedString(deletedIds, "and"))
}

return err
}
Loading

0 comments on commit 3962325

Please sign in to comment.