Skip to content

Commit

Permalink
wire up Kubernetes commands
Browse files Browse the repository at this point in the history
  • Loading branch information
jaxxstorm committed Oct 30, 2022
1 parent c7810ef commit 75728b9
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 0 deletions.
2 changes: 2 additions & 0 deletions cmd/cli/connect/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package connect
import (
"github.com/jaxxstorm/connecti/cmd/cli/connect/aws"
"github.com/jaxxstorm/connecti/cmd/cli/connect/azure"
"github.com/jaxxstorm/connecti/cmd/cli/connect/kubernetes"
"github.com/spf13/cobra"
)

Expand All @@ -15,6 +16,7 @@ func Command() *cobra.Command {

command.AddCommand(aws.Command())
command.AddCommand(azure.Command())
command.AddCommand(kubernetes.Command())

return command
}
114 changes: 114 additions & 0 deletions cmd/cli/connect/kubernetes/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package kubernetes

import (
"context"
"fmt"

"github.com/jaxxstorm/connecti/pkg/kubernetes"
randomname "github.com/jaxxstorm/connecti/pkg/name"
tui "github.com/jaxxstorm/connecti/pkg/terminal"
"github.com/pulumi/pulumi/sdk/v3/go/auto/optup"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var (
name string
tailnet string
apiKey string
routes []string
)

func Command() *cobra.Command {
command := &cobra.Command{
Use: "kubernetes",
Short: "Connect to Kubernetes infrastructure.",
Long: `Create a tailscale bastion in a Kubernetes cluster via a deployment.`,
RunE: tui.WrapCobraCommand(func(cmd *cobra.Command, args []string, view tui.View) error {
// Grab all the configuration variables
tailnet = viper.GetString("tailnet")
apiKey = viper.GetString("apiKey")
name = viper.GetString("name")
routes = viper.GetStringSlice("routes")

view.Ready()

// apparently you can't specify if a flag is required
// and set in configuration, so manual validation is the only way
// see: https://github.com/spf13/viper/issues/397
if tailnet == "" {
return fmt.Errorf("must specify a tailnet. See --help")
}

if apiKey == "" {
return fmt.Errorf("must specify a tailscale api key. See --help")
}


if name == "" {
name = randomname.Generate()
}

ctx := context.Background()
program, err := kubernetes.Program(name, ctx, kubernetes.BastionArgs{
Name: name,
Routes: routes,
Tailnet: tailnet,
ApiKey: apiKey,
CreateNamespace: true, // FIXME: should we allow usage of an existing namespace?
})
if err != nil {
return err
}

view.SetPulumiProgramCancelFn(func() error {
return program.Cancel(ctx)
})

outputHandler := view.NewPulumiOutputHandler("update")
stdoutStreamer := optup.ProgressStreams(outputHandler)
_, err = program.Refresh(ctx)
if err != nil {
return fmt.Errorf("error refreshing stack: %v", err)
}
_, err = program.Up(ctx, stdoutStreamer)
if err != nil {
view.SendPulumiProgressOutput(outputHandler.CurrentProgress, "Failed to create resources. Cleaning up.", "")
// If the update errors, we should clean up the stack for the user.
_, dErr := program.Destroy(ctx)
if dErr != nil {
return fmt.Errorf("failed update: %v\n\n\tfailed clean up: %v", err, dErr)
}
rmErr := program.Workspace().RemoveStack(ctx, name)
if rmErr != nil {
return fmt.Errorf("failed update: %v\n\n\tfailed stack removal: %v", err, rmErr)
}

return fmt.Errorf("failed update: %v", err)
}

return nil

}),
}

command.Flags().StringVar(&name, "name", "", "Unique name to use for your bastion.")
command.Flags().StringVar(&tailnet, "tailnet", "", "The name of the tailnet to connect to. See: https://login.tailscale.com/admin/settings/general")
command.Flags().StringVar(&apiKey, "api-key", "", "The tailnet api key to use. See: https://login.tailscale.com/admin/settings/keys")
command.Flags().StringSliceVar(&routes, "routes", nil, "The routes to advertise. This is likely the cluster Pod CIDR and Service CIDR.")


viper.BindPFlag("name", command.Flags().Lookup("name"))
viper.BindPFlag("tailnet", command.Flags().Lookup("tailnet"))
viper.BindPFlag("apiKey", command.Flags().Lookup("api-key"))
viper.BindPFlag("route", command.Flags().Lookup("route"))


// Bind the env vars to the provider env vars
viper.BindEnv("tailnet", "TAILSCALE_TAILNET")
viper.BindEnv("apiKey", "TAILSCALE_API_KEY")

command.MarkFlagRequired("routes")

return command
}
64 changes: 64 additions & 0 deletions cmd/cli/disconnect/kubernetes/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package azure

import (
"context"
"fmt"

"github.com/jaxxstorm/connecti/pkg/kubernetes"
tui "github.com/jaxxstorm/connecti/pkg/terminal"
"github.com/pulumi/pulumi/sdk/v3/go/auto/optdestroy"
"github.com/spf13/cobra"
)

var (
name string
)

func Command() *cobra.Command {
command := &cobra.Command{
Use: "kubernetes",
Short: "Disconnect from Kubernetes infrastructure",
Long: `Tear down a tailscale bastion in an Kubernetes cluster via a deployment`,
RunE: tui.WrapCobraCommand(func(cmd *cobra.Command, args []string, view tui.View) error {

view.Ready()
ctx := context.Background()
// FIXME: do we need to specify credentials here?
// I suspect not because I believe they're hardcoded into the provider
// but we should check
program, err := kubernetes.Program(name, ctx, kubernetes.BastionArgs{
Name: name,
})
if err != nil {
return err
}

view.SetPulumiProgramCancelFn(func() error {
return program.Cancel(ctx)
})

pulumiOutputHandler := view.NewPulumiOutputHandler("destroy")
stdoutStreamer := optdestroy.ProgressStreams(pulumiOutputHandler)
_, err = program.Refresh(ctx)
if err != nil {
return fmt.Errorf("error refreshing stack: %v", err)
}
_, err = program.Destroy(ctx, stdoutStreamer)
if err != nil {
return fmt.Errorf("failed destroy: %v", err)
}

err = program.Workspace().RemoveStack(ctx, name)
if err != nil {
return fmt.Errorf("failed to remove stack: %v", err)
}

return nil
}),
}

command.Flags().StringVar(&name, "name", "", "The name of the bastion to tear down")
command.MarkFlagRequired("name")

return command
}

0 comments on commit 75728b9

Please sign in to comment.