Skip to content
This repository has been archived by the owner on Jan 9, 2025. It is now read-only.

Commit

Permalink
feat: improvements to cli (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
lostbean authored Aug 2, 2024
1 parent 3cba3b3 commit eb2e0c4
Show file tree
Hide file tree
Showing 10 changed files with 458 additions and 241 deletions.
160 changes: 129 additions & 31 deletions kardinal-cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"kardinal.cli/multi_os_cmd_executor"

"github.com/kurtosis-tech/stacktrace"
"github.com/samber/lo"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"kardinal.cli/deployment"
Expand Down Expand Up @@ -78,24 +79,34 @@ var deployCmd = &cobra.Command{
},
}

var listCmd = &cobra.Command{
Use: "ls",
Short: "List all active flows",
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
tenantUuid, err := tenant.GetOrCreateUserTenantUUID()
if err != nil {
log.Fatal("Error getting or creating user tenant UUID", err)
}

listDevFlow(tenantUuid.String())
},
}

var createCmd = &cobra.Command{
Use: "create [service name] [image name]",
Short: "Create a new service in development mode",
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
serviceName, imageName := args[0], args[1]
serviceConfigs, err := parseKubernetesManifestFile(kubernetesManifestFile)
if err != nil {
log.Fatalf("Error loading k8s manifest file: %v", err)
}

tenantUuid, err := tenant.GetOrCreateUserTenantUUID()
if err != nil {
log.Fatal("Error getting or creating user tenant UUID", err)
}

fmt.Printf("Creating service %s with image %s in development mode...\n", serviceName, imageName)
createDevFlow(tenantUuid.String(), serviceConfigs, imageName, serviceName)
createDevFlow(tenantUuid.String(), imageName, serviceName)
},
}

Expand All @@ -105,16 +116,12 @@ var deleteCmd = &cobra.Command{
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
flowId := args[0]
serviceConfigs, err := parseKubernetesManifestFile(kubernetesManifestFile)
if err != nil {
log.Fatalf("Error loading k8s manifest file: %v", err)
}

tenantUuid, err := tenant.GetOrCreateUserTenantUUID()
if err != nil {
log.Fatal("Error getting or creating user tenant UUID", err)
}
deleteFlow(tenantUuid.String(), flowId, serviceConfigs)
deleteFlow(tenantUuid.String(), flowId)

fmt.Print("Deleting dev flow")
},
Expand Down Expand Up @@ -179,11 +186,9 @@ func init() {
rootCmd.AddCommand(managerCmd)
rootCmd.AddCommand(deployCmd)
rootCmd.AddCommand(dashboardCmd)
flowCmd.AddCommand(createCmd, deleteCmd)
flowCmd.AddCommand(listCmd, createCmd, deleteCmd)
managerCmd.AddCommand(deployManagerCmd, removeManagerCmd)

flowCmd.PersistentFlags().StringVarP(&kubernetesManifestFile, "k8s-manifest", "k", "", "Path to the K8S manifest file")
flowCmd.MarkPersistentFlagRequired("k8s-manifest")
deployCmd.PersistentFlags().StringVarP(&kubernetesManifestFile, "k8s-manifest", "k", "", "Path to the K8S manifest file")
deployCmd.MarkPersistentFlagRequired("k8s-manifest")
}
Expand Down Expand Up @@ -265,28 +270,62 @@ func getObjectName(obj *metav1.ObjectMeta) string {
return obj.GetName()
}

func createDevFlow(tenantUuid api_types.Uuid, serviceConfigs []api_types.ServiceConfig, imageLocator, serviceName string) {
func listDevFlow(tenantUuid api_types.Uuid) {
ctx := context.Background()
client := getKontrolServiceClient()

body := api_types.PostTenantUuidFlowCreateJSONRequestBody{
ServiceConfigs: &serviceConfigs,
ServiceName: &serviceName,
ImageLocator: &imageLocator,
resp, err := client.GetTenantUuidFlowsWithResponse(ctx, tenantUuid)
if err != nil {
log.Fatalf("Failed to create dev flow: %v", err)
}

if resp.StatusCode() == 200 {
printTable(*resp.JSON200)
return
}

if resp.StatusCode() == 404 {
fmt.Printf("Could not create flow, missing %s: %s\n", resp.JSON404.ResourceType, resp.JSON404.Id)
} else if resp.StatusCode() == 500 {
fmt.Printf("Could not create flow, error %s: %v\n", resp.JSON500.Error, resp.JSON500.Msg)
} else {
fmt.Printf("Failed to create dev flow: %s\n", string(resp.Body))
}
os.Exit(1)
}

func createDevFlow(tenantUuid api_types.Uuid, imageLocator, serviceName string) {
ctx := context.Background()

devSpec := api_types.FlowSpec{
{
ServiceName: serviceName,
ImageLocator: imageLocator,
},
}
client := getKontrolServiceClient()

resp, err := client.PostTenantUuidFlowCreateWithResponse(ctx, tenantUuid, body)
resp, err := client.PostTenantUuidFlowCreateWithResponse(ctx, tenantUuid, devSpec)
if err != nil {
log.Fatalf("Failed to create dev flow: %v", err)
}

if resp.StatusCode() == 200 {
fmt.Printf("Create new dev flow: %s\n", string(*resp.JSON200.DevFlowId))
} else if resp.StatusCode() == 404 {
fmt.Printf("Could not create flow, missing %s: %s\n", *resp.JSON404.ResourceType, *resp.JSON404.Id)
fmt.Printf("Flow \"%s\" created. Access it on:\n", resp.JSON200.FlowId)
for url := range resp.JSON200.FlowUrls {
fmt.Printf("http://%s\n", url)
}
return
}

if resp.StatusCode() == 404 {
fmt.Printf("Could not create flow, missing %s: %s\n", resp.JSON404.ResourceType, resp.JSON404.Id)
} else if resp.StatusCode() == 500 {
fmt.Printf("Could not create flow, error %s: %v\n", resp.JSON500.Error, resp.JSON500.Msg)
} else {
fmt.Printf("Failed to create dev flow: %s\n", string(resp.Body))
}
os.Exit(1)
}

func deploy(tenantUuid api_types.Uuid, serviceConfigs []api_types.ServiceConfig) {
Expand All @@ -302,31 +341,48 @@ func deploy(tenantUuid api_types.Uuid, serviceConfigs []api_types.ServiceConfig)
log.Fatalf("Failed to deploy: %v", err)
}

fmt.Printf("Response: %s\n", string(resp.Body))

trafficConfigurationURL, err := getTrafficConfigurationURL(tenantUuid)
if err != nil {
logrus.Warningf("The command run successfully but it was impossible to print the traffic configuration URL because and error ocurred, please make sure to run the 'kardinal manager deploy' command first")
return
}

logrus.Infof("Visit: %s", trafficConfigurationURL)
if resp.StatusCode() == 200 {
fmt.Printf("Flow \"%s\" created. Access it on:\n", resp.JSON200.FlowId)
for url := range resp.JSON200.FlowUrls {
fmt.Printf("http://%s\n", url)
}
fmt.Printf("Visit Kardinal Kontrol: %s", trafficConfigurationURL)
return
}

if resp.StatusCode() == 404 {
fmt.Printf("Could not create flow, missing %s: %s\n", resp.JSON404.ResourceType, resp.JSON404.Id)
} else if resp.StatusCode() == 500 {
fmt.Printf("Could not create flow, error %s: %v\n", resp.JSON500.Error, resp.JSON500.Msg)
} else {
fmt.Printf("Failed to create dev flow: %s\n", string(resp.Body))
}
os.Exit(1)
}

func deleteFlow(tenantUuid api_types.Uuid, flowId api_types.FlowId, serviceConfigs []api_types.ServiceConfig) {
func deleteFlow(tenantUuid api_types.Uuid, flowId api_types.FlowId) {
ctx := context.Background()

body := api_types.PostTenantUuidFlowFlowIdDeleteJSONRequestBody{
ServiceConfigs: &serviceConfigs,
}
client := getKontrolServiceClient()

resp, err := client.PostTenantUuidFlowFlowIdDeleteWithResponse(ctx, tenantUuid, flowId, body)
resp, err := client.DeleteTenantUuidFlowFlowId(ctx, tenantUuid, flowId)
if err != nil {
log.Fatalf("Failed to delete flow: %v", err)
}

fmt.Printf("Response: %s\n", string(resp.Body))
respCode := resp.StatusCode
if respCode == 200 || respCode == 204 {
fmt.Printf("Dev flow %s has been deleted", flowId)
} else {
fmt.Printf("Failed to delete dev flow!\n")
os.Exit(1)
}
}

func deployManager(tenantUuid api_types.Uuid, kontrolLocation string) error {
Expand Down Expand Up @@ -426,3 +482,45 @@ func getClusterResourcesURL(tenantUuid api_types.Uuid) (string, error) {

return clusterResourcesURL, nil
}

func printTable(flows []api_types.Flow) {
// Find the maximum width of each column
data := lo.Map(flows, func(flow api_types.Flow, _ int) []string {
return []string{
flow.FlowId,
strings.Join(lo.Map(flow.FlowUrls, func(item string, _ int) string { return fmt.Sprintf("http://%s", item) }), ", "),
}
})
header := [][]string{{"Flow ID", "Flow URL"}}
data = append(header, data...)

colWidths := make([]int, len(data[0]))
for _, row := range data {
for i, cell := range row {
if len(cell) > colWidths[i] {
colWidths[i] = len(cell)
}
}
}

for _, width := range colWidths {
fmt.Print("|", strings.Repeat("-", width+2))
}
fmt.Println("|")

// Print the table
for rowNum, row := range data {
for i, cell := range row {
fmt.Printf("| %-*s ", colWidths[i], cell)
}
fmt.Println("|")

// Print separator after header
if rowNum == 0 {
for _, width := range colWidths {
fmt.Print("|", strings.Repeat("-", width+2))
}
fmt.Println("|")
}
}
}
3 changes: 2 additions & 1 deletion kardinal-cli/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/adrg/xdg v0.4.0
github.com/google/uuid v1.5.0
github.com/kurtosis-tech/stacktrace v0.0.0-20211028211901-1c67a77b5409
github.com/samber/lo v1.46.0
github.com/spf13/cobra v1.8.0
k8s.io/api v0.30.2
k8s.io/apimachinery v0.30.2
Expand Down Expand Up @@ -36,7 +37,7 @@ require (
golang.org/x/net v0.25.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/term v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.33.0 // indirect
Expand Down
10 changes: 6 additions & 4 deletions kardinal-cli/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/samber/lo v1.46.0 h1:w8G+oaCPgz1PoCJztqymCFaKwXt+5cCXn51uPxExFfQ=
github.com/samber/lo v1.46.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
Expand Down Expand Up @@ -132,16 +134,16 @@ golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
12 changes: 6 additions & 6 deletions kardinal-cli/gomod2nix.toml
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,8 @@ schema = 3
version = "v2.1.0"
hash = "sha256-R+84l1si8az5yDqd5CYcFrTyNZ1eSYlpXKq6nFt4OTQ="
[mod."github.com/samber/lo"]
version = "v1.39.0"
hash = "sha256-bFJXbzFpUQM2xoNrHrlzn0RJZujd21H00zGBgo/JEb0="
version = "v1.46.0"
hash = "sha256-ZvyiOnjqh3nt8OxofUPbXxN14j5bHcmT9TqOCPdwAVQ="
[mod."github.com/schollz/closestmatch"]
version = "v2.1.0+incompatible"
hash = "sha256-SpWqGfqlMkZPQ6TSf7NTaYMbQllBaBgPM8oxTBOTn7w="
Expand Down Expand Up @@ -458,14 +458,14 @@ schema = 3
version = "v0.20.0"
hash = "sha256-kU+OVJbYktTIn4ZTAdomsOjL069Vj45sdroEMRKaRDI="
[mod."golang.org/x/text"]
version = "v0.15.0"
hash = "sha256-pBnj0AEkfkvZf+3bN7h6epCD2kurw59clDP7yWvxKlk="
version = "v0.16.0"
hash = "sha256-hMTO45upjEuA4sJzGplJT+La2n3oAfHccfYWZuHcH+8="
[mod."golang.org/x/time"]
version = "v0.5.0"
hash = "sha256-W6RgwgdYTO3byIPOFxrP2IpAZdgaGowAaVfYby7AULU="
[mod."golang.org/x/tools"]
version = "v0.21.0"
hash = "sha256-TU0gAxUX410AYc/nMxxZiaqXeORih1cXbKh3sxKufVg="
version = "v0.21.1-0.20240508182429-e35e4ccd0d2d"
hash = "sha256-KfnS+3fREPAWQUBoUedPupQp9yLrugxMmmEoHvyzKNE="
[mod."golang.org/x/xerrors"]
version = "v0.0.0-20220907171357-04be3eba64a2"
hash = "sha256-6+zueutgefIYmgXinOflz8qGDDDj0Zhv+2OkGhBTKno="
Expand Down
Loading

0 comments on commit eb2e0c4

Please sign in to comment.