Skip to content

Commit

Permalink
Merge pull request #23 from conduktor/get_kind_from_openapi
Browse files Browse the repository at this point in the history
Get kind from openapi
  • Loading branch information
strokyl authored Mar 20, 2024
2 parents 204b7e0 + 6e9429d commit 2cc0327
Show file tree
Hide file tree
Showing 11 changed files with 400 additions and 74 deletions.
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,6 @@ How to run integration test:
First login to your teleport proxy, for example:
```
tsh login --proxy=teleport-01.prd.tooling.cdkt.dev --auth=github
```

```
conduktor get application --cert $(tsh apps config --format=cert) --key $(tsh apps config --format=key)
```

Or:
```
export CDK_CERT=$(tsh apps config --format=cert)
export CDK_KEY=$(tsh apps config --format=key)
conduktor get application
Expand Down
90 changes: 40 additions & 50 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"os"
"strings"

"github.com/conduktor/ctl/printutils"
"github.com/conduktor/ctl/resource"
"github.com/conduktor/ctl/utils"
"github.com/go-resty/resty/v2"
"os"
"strings"
)

type Client struct {
Expand All @@ -18,16 +18,16 @@ type Client struct {
client *resty.Client
}

func Make(token string, baseUrl string, debug bool, key, cert string) Client {
func Make(token string, baseUrl string, debug bool, key, cert string) *Client {
certificate, _ := tls.LoadX509KeyPair(cert, key)
return Client{
return &Client{
token: token,
baseUrl: baseUrl + "/public/v1",
baseUrl: baseUrl,
client: resty.New().SetDebug(debug).SetHeader("Authorization", "Bearer "+token).SetCertificates(certificate),
}
}

func MakeFromEnv(debug bool, key, cert string) Client {
func MakeFromEnv() *Client {
token := os.Getenv("CDK_TOKEN")
if token == "" {
fmt.Fprintln(os.Stderr, "Please set CDK_TOKEN")
Expand All @@ -38,16 +38,11 @@ func MakeFromEnv(debug bool, key, cert string) Client {
fmt.Fprintln(os.Stderr, "Please set CDK_BASE_URL")
os.Exit(2)
}
finalKey := key
finalCert := cert
if finalKey == "" {
finalKey = os.Getenv("CDK_KEY")
}
if finalCert == "" {
finalCert = os.Getenv("CDK_CERT")
}
debug := strings.ToLower(os.Getenv("CDK_DEBUG")) == "true"
key := os.Getenv("CDK_KEY")
cert := os.Getenv("CDK_CERT")

return Make(token, baseUrl, debug, finalKey, finalCert)
return Make(token, baseUrl, debug, key, cert)
}

type UpsertResponse struct {
Expand All @@ -64,17 +59,24 @@ func extractApiError(resp *resty.Response) string {
}
}

func (client *Client) publicV1Url() string {
return client.baseUrl + "/public/v1"
}

func (client *Client) ActivateDebug() {
client.client.SetDebug(true)
}

func (client *Client) Apply(resource *resource.Resource, dryMode bool) (string, error) {
url := client.baseUrl + "/" + UpperCamelToKebab(resource.Kind)
url := client.publicV1Url() + "/" + utils.UpperCamelToKebab(resource.Kind)
builder := client.client.R().SetBody(resource.Json)
if dryMode {
builder = builder.SetQueryParam("dryMode", "true")
}
resp, err := builder.Put(url)
if err != nil {
return "", err
}
if resp.IsError() {
} else if resp.IsError() {
return "", fmt.Errorf(extractApiError(resp))
}
bodyBytes := resp.Body()
Expand All @@ -97,32 +99,33 @@ func printResponseAsYaml(bytes []byte) error {
}

func (client *Client) Get(kind string) error {
url := client.baseUrl + "/" + UpperCamelToKebab(kind)
url := client.publicV1Url() + "/" + utils.UpperCamelToKebab(kind)
resp, err := client.client.R().Get(url)
if resp.IsError() {
return fmt.Errorf(extractApiError(resp))
}
if err != nil {
return err
} else if resp.IsError() {
return fmt.Errorf(extractApiError(resp))
}
return printResponseAsYaml(resp.Body())
}

func (client *Client) Describe(kind, name string) error {
url := client.baseUrl + "/" + UpperCamelToKebab(kind) + "/" + name
url := client.publicV1Url() + "/" + utils.UpperCamelToKebab(kind) + "/" + name
resp, err := client.client.R().Get(url)
if resp.IsError() {
return fmt.Errorf("error describing resources %s/%s, got status code: %d:\n %s", kind, name, resp.StatusCode(), string(resp.Body()))
}
if err != nil {
return err
} else if resp.IsError() {
return fmt.Errorf("error describing resources %s/%s, got status code: %d:\n %s", kind, name, resp.StatusCode(), string(resp.Body()))
}
return printResponseAsYaml(resp.Body())
}

func (client *Client) Delete(kind, name string) error {
url := client.baseUrl + "/" + UpperCamelToKebab(kind) + "/" + name
url := client.publicV1Url() + "/" + utils.UpperCamelToKebab(kind) + "/" + name
resp, err := client.client.R().Delete(url)
if resp.IsError() {
if err != nil {
return err
} else if resp.IsError() {
return fmt.Errorf(extractApiError(resp))
} else {
fmt.Printf("%s/%s deleted\n", kind, name)
Expand All @@ -131,26 +134,13 @@ func (client *Client) Delete(kind, name string) error {
return err
}

func UpperCamelToKebab(input string) string {
// Split the input string into words
words := make([]string, 0)
currentWord := ""
for _, char := range input {
if char >= 'A' && char <= 'Z' {
if currentWord != "" {
words = append(words, currentWord)
}
currentWord = string(char)
} else {
currentWord += string(char)
}
}
if currentWord != "" {
words = append(words, currentWord)
func (client *Client) GetOpenApi() ([]byte, error) {
url := client.baseUrl + "/public/docs/docs.yaml"
resp, err := client.client.R().Get(url)
if err != nil {
return nil, err
} else if resp.IsError() {
return nil, fmt.Errorf(resp.String())
}

// Join the words with hyphens
kebabCase := strings.ToLower(strings.Join(words, "-"))

return kebabCase
return resp.Body(), nil
}
6 changes: 2 additions & 4 deletions cmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cmd

import (
"fmt"
"github.com/conduktor/ctl/client"
"github.com/conduktor/ctl/resource"
"github.com/spf13/cobra"
"os"
Expand All @@ -26,9 +25,8 @@ var applyCmd = &cobra.Command{
}
resources = append(resources, r...)
}
client := client.MakeFromEnv(*debug, *key, *cert)
for _, resource := range resources {
upsertResult, err := client.Apply(&resource, *dryRun)
upsertResult, err := apiClient.Apply(&resource, *dryRun)
if err != nil {
fmt.Fprintf(os.Stderr, "Could not apply resource %s/%s: %s\n", resource.Kind, resource.Name, err)
os.Exit(1)
Expand All @@ -52,7 +50,7 @@ func resourceForPath(path string) ([]resource.Resource, error) {
}
}

func init() {
func initApply() {
rootCmd.AddCommand(applyCmd)

// Here you will define your flags and configuration settings.
Expand Down
6 changes: 2 additions & 4 deletions cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cmd

import (
"fmt"
"github.com/conduktor/ctl/client"
"github.com/spf13/cobra"
"os"
)
Expand All @@ -14,15 +13,14 @@ var deleteCmd = &cobra.Command{
Long: ``,
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
client := client.MakeFromEnv(*debug, *key, *cert)
err := client.Delete(args[0], args[1])
err := apiClient.Delete(args[0], args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
},
}

func init() {
func initDelete() {
rootCmd.AddCommand(deleteCmd)
}
52 changes: 46 additions & 6 deletions cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@ package cmd

import (
"fmt"
"github.com/conduktor/ctl/client"
"github.com/spf13/cobra"
"os"
)

// applyCmd represents the apply command
var getCmd = &cobra.Command{
Use: "get",
Short: "get resource of a given kind",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
// Root command does nothing
cmd.Help()
os.Exit(1)
},
}

var getCmdWhenNoSchema = &cobra.Command{
Use: "get kind [name]",
Short: "get resource of a given kind",
Long: `If name not provided it will list all resource. For example:
Expand All @@ -18,12 +27,11 @@ conduktor get application myapp
will describe the application myapp`,
Args: cobra.MatchAll(cobra.MinimumNArgs(1), cobra.MaximumNArgs(2)),
Run: func(cmd *cobra.Command, args []string) {
client := client.MakeFromEnv(*debug, *key, *cert)
var err error
if len(args) == 1 {
err = client.Get(args[0])
err = apiClient.Get(args[0])
} else if len(args) == 2 {
err = client.Describe(args[0], args[1])
err = apiClient.Describe(args[0], args[1])
}
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
Expand All @@ -32,6 +40,38 @@ will describe the application myapp`,
},
}

func init() {
func initGet() {
if schemaClient == nil {
rootCmd.AddCommand(getCmdWhenNoSchema)
return
}
tags, err := schemaClient.GetKind()
if err != nil {
fmt.Fprintf(os.Stderr, "Could not load kind from openapi: %s\n", err)
rootCmd.AddCommand(getCmdWhenNoSchema)
return
}
rootCmd.AddCommand(getCmd)

for _, tag := range tags {
tagCmd := &cobra.Command{
Use: fmt.Sprintf("%s [name]", tag),
Short: "get resource of kind " + tag,
Args: cobra.MatchAll(cobra.MaximumNArgs(1)),
Long: `If name not provided it will list all resource`,
Run: func(cmd *cobra.Command, args []string) {
var err error
if len(args) == 0 {
err = apiClient.Get(tag)
} else if len(args) == 1 {
err = apiClient.Describe(tag, args[1])
}
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
},
}
getCmd.AddCommand(tagCmd)
}
}
26 changes: 26 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,35 @@ Copyright © 2024 NAME HERE <EMAIL ADDRESS>
package cmd

import (
"fmt"
"github.com/conduktor/ctl/client"
"github.com/conduktor/ctl/schema"
"github.com/spf13/cobra"
"os"
)

var debug *bool
var key *string
var cert *string
var apiClient *client.Client
var schemaClient *schema.Schema = nil

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "conduktor",
Short: "command line tools for conduktor",
Long: `You need to define the CDK_TOKEN and CDK_BASE_URL environment variables to use this tool.
You can also use the CDK_KEY,CDK_CERT instead of --key and --cert flags to use a certificate for tls authentication.`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if *debug {
apiClient.ActivateDebug()
}
},
Run: func(cmd *cobra.Command, args []string) {
// Root command does nothing
cmd.Help()
os.Exit(1)
},
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
Expand All @@ -34,7 +49,18 @@ func Execute() {
}

func init() {
apiClient = client.MakeFromEnv()
openApi, err := apiClient.GetOpenApi()
if err == nil {
schemaClient, err = schema.New(openApi)
}
if err != nil {
fmt.Fprintf(os.Stderr, "Could not load server openapi: %s\n", err)
}
debug = rootCmd.PersistentFlags().BoolP("verbose", "v", false, "Show more information for debugging")
key = rootCmd.PersistentFlags().String("key", "", "Set pem key for certificate authentication (useful for teleport)")
cert = rootCmd.PersistentFlags().String("cert", "", "Set pem cert for certificate authentication (useful for teleport)")
initGet()
initDelete()
initApply()
}
11 changes: 10 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,23 @@ go 1.22.0
require (
github.com/ghodss/yaml v1.0.0
github.com/go-resty/resty/v2 v2.11.0
github.com/jarcoal/httpmock v1.3.1
github.com/pb33f/libopenapi v0.15.14
github.com/spf13/cobra v1.8.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jarcoal/httpmock v1.3.1 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sync v0.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Loading

0 comments on commit 2cc0327

Please sign in to comment.