Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for gateway api v2 #54

Merged
merged 19 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func TestGetShouldWork(t *testing.T) {
)

app := client.GetKinds()["Application"]
result, err := client.Get(&app, []string{})
result, err := client.Get(&app, []string{}, nil)
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -225,7 +225,7 @@ func TestGetShouldFailIfN2xx(t *testing.T) {
)

app := client.GetKinds()["Application"]
_, err = client.Get(&app, []string{})
_, err = client.Get(&app, []string{}, nil)
if err == nil {
t.Failed()
}
Expand Down
14 changes: 9 additions & 5 deletions client/client.go → client/console_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func Make(apiParameter ApiParameter) (*Client, error) {
err := result.initKindFromApi()
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot access the Conduktor API: %s\nUsing offline defaults.\n", err)
result.kinds = schema.DefaultKind()
result.kinds = schema.ConsoleDefaultKind()
}

return result, nil
Expand All @@ -106,7 +106,7 @@ func Make(apiParameter ApiParameter) (*Client, error) {
func MakeFromEnv() (*Client, error) {
apiParameter := ApiParameter{
BaseUrl: os.Getenv("CDK_BASE_URL"),
Debug: strings.ToLower(os.Getenv("CDK_DEBUG")) == "true",
Debug: utils.CdkDebug(),
Cert: os.Getenv("CDK_CERT"),
Cacert: os.Getenv("CDK_CACERT"),
ApiKey: os.Getenv("CDK_API_KEY"),
Expand Down Expand Up @@ -201,11 +201,15 @@ func (client *Client) Apply(resource *resource.Resource, dryMode bool) (string,
return upsertResponse.UpsertResult, nil
}

func (client *Client) Get(kind *schema.Kind, parentPathValue []string) ([]resource.Resource, error) {
func (client *Client) Get(kind *schema.Kind, parentPathValue []string, queryParams map[string]string) ([]resource.Resource, error) {
var result []resource.Resource
client.setApiKeyFromEnvIfNeeded()
url := client.baseUrl + kind.ListPath(parentPathValue)
resp, err := client.client.R().Get(url)
requestBuilder := client.client.R()
if queryParams != nil {
requestBuilder = requestBuilder.SetQueryParams(queryParams)
}
resp, err := requestBuilder.Get(url)
if err != nil {
return result, err
} else if resp.IsError() {
Expand Down Expand Up @@ -310,7 +314,7 @@ func (client *Client) initKindFromApi() error {
return fmt.Errorf("Cannot parse openapi: %s", err)
}
strict := false
client.kinds, err = schema.GetKinds(strict)
client.kinds, err = schema.GetConsoleKinds(strict)
if err != nil {
fmt.Errorf("Cannot extract kinds from openapi: %s", err)
}
Expand Down
334 changes: 334 additions & 0 deletions client/gateway_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
package client

import (
"crypto/tls"
"encoding/json"
"fmt"
"os"

"github.com/conduktor/ctl/resource"
"github.com/conduktor/ctl/schema"
"github.com/conduktor/ctl/utils"
"github.com/go-resty/resty/v2"
)

type GatewayClient struct {
cdkGatewayUser string
cdkGatewayPassword string
baseUrl string
client *resty.Client
kinds schema.KindCatalog
}

type GatewayApiParameter struct {
BaseUrl string
Debug bool
CdkGatewayUser string
CdkGatewayPassword string
}

func MakeGateway(apiParameter GatewayApiParameter) (*GatewayClient, error) {
restyClient := resty.New().SetDebug(apiParameter.Debug).SetHeader("X-CDK-CLIENT", "CLI/"+utils.GetConduktorVersion())

if apiParameter.BaseUrl == "" {
return nil, fmt.Errorf("Please set CDK_GATEWAY_BASE_URL")
}

if apiParameter.CdkGatewayUser == "" || apiParameter.CdkGatewayPassword == "" {
return nil, fmt.Errorf("CDK_GATEWAY_USER and CDK_GATEWAY_PASSWORD must be provided")
}

result := &GatewayClient{
cdkGatewayUser: apiParameter.CdkGatewayUser,
cdkGatewayPassword: apiParameter.CdkGatewayPassword,
baseUrl: apiParameter.BaseUrl,
client: restyClient,
kinds: nil,
}

result.client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
result.client.SetDisableWarn(true)
strokyl marked this conversation as resolved.
Show resolved Hide resolved
result.client.SetBasicAuth(apiParameter.CdkGatewayUser, apiParameter.CdkGatewayPassword)

err := result.initKindFromApi()
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot access the Gateway Conduktor API: %s\nUsing offline defaults.\n", err)
result.kinds = schema.GatewayDefaultKind()
}

return result, nil
}

func MakeGatewayClientFromEnv() (*GatewayClient, error) {
apiParameter := GatewayApiParameter{
BaseUrl: os.Getenv("CDK_GATEWAY_BASE_URL"),
Debug: utils.CdkDebug(),
CdkGatewayUser: os.Getenv("CDK_GATEWAY_USER"),
CdkGatewayPassword: os.Getenv("CDK_GATEWAY_PASSWORD"),
}

client, err := MakeGateway(apiParameter)
if err != nil {
return nil, fmt.Errorf("Cannot create client: %s", err)
}
return client, nil
}

func (client *GatewayClient) Get(kind *schema.Kind, parentPathValue []string, queryParams map[string]string) ([]resource.Resource, error) {
var result []resource.Resource
url := client.baseUrl + kind.ListPath(parentPathValue)
requestBuilder := client.client.R()
if queryParams != nil {
requestBuilder = requestBuilder.SetQueryParams(queryParams)
}
resp, err := requestBuilder.Get(url)
if err != nil {
return result, err
} else if resp.IsError() {
return result, fmt.Errorf(extractApiError(resp))
}
err = json.Unmarshal(resp.Body(), &result)
return result, err
}

func (client *GatewayClient) Describe(kind *schema.Kind, parentPathValue []string, name string) (resource.Resource, error) {
var result resource.Resource
url := client.baseUrl + kind.DescribePath(parentPathValue, name)
resp, err := client.client.R().Get(url)
if err != nil {
return result, err
} else if resp.IsError() {
return result, fmt.Errorf("error describing resources %s/%s, got status code: %d:\n %s", kind.GetName(), name, resp.StatusCode(), string(resp.Body()))
}
err = json.Unmarshal(resp.Body(), &result)
return result, err
}

func (client *GatewayClient) Delete(kind *schema.Kind, parentPathValue []string, name string) error {
url := client.baseUrl + kind.DescribePath(parentPathValue, name)
resp, err := client.client.R().Delete(url)
if err != nil {
return err
} else if resp.IsError() {
return fmt.Errorf(extractApiError(resp))
} else {
fmt.Printf("%s/%s deleted\n", kind.GetName(), name)
}

return err
}

func (client *GatewayClient) DeleteResourceByName(resource *resource.Resource) error {
kinds := client.GetKinds()
kind, ok := kinds[resource.Kind]
if !ok {
return fmt.Errorf("kind %s not found", resource.Kind)
}
deletePath, err := kind.DeletePath(resource)
if err != nil {
return err
}
url := client.baseUrl + deletePath
resp, err := client.client.R().Delete(url)
if err != nil {
return err
} else if resp.IsError() {
return fmt.Errorf(extractApiError(resp))
} else {
fmt.Printf("%s/%s deleted\n", kind.GetName(), resource.Name)
}

return err
}

func (client *GatewayClient) DeleteResourceByNameAndVCluster(resource *resource.Resource) error {
kinds := client.GetKinds()
kind, ok := kinds[resource.Kind]
name := resource.Name
vCluster := resource.Metadata["vCluster"]
if vCluster == nil {
vCluster = "passthrough"
}
if !ok {
return fmt.Errorf("kind %s not found", resource.Kind)
}
deletePath := kind.ListPath(nil)
url := client.baseUrl + deletePath
resp, err := client.client.R().SetBody(map[string]string{"name": name, "vCluster": vCluster.(string)}).Delete(url)
if err != nil {
return err
} else if resp.IsError() {
return fmt.Errorf(extractApiError(resp))
} else {
fmt.Printf("%s/%s deleted\n", kind.GetName(), resource.Name)
}

return err
}

type DeleteInterceptorPayload struct {
VCluster *string `json:"vCluster"`
Group *string `json:"group"`
Username *string `json:"username"`
}

func (client *GatewayClient) DeleteResourceInterceptors(resource *resource.Resource) error {
kinds := client.GetKinds()
kind, ok := kinds[resource.Kind]
scope := resource.Metadata["scope"]
passthrough := "passthrough"
var deleteInterceptorPayload DeleteInterceptorPayload
if scope == nil {
deleteInterceptorPayload = DeleteInterceptorPayload{
VCluster: &passthrough,
Group: nil,
Username: nil,
}
} else {
vCluster := scope.(map[string]interface{})["vCluster"]
var vClusterValue string
if vCluster != nil && vCluster.(string) != "" {
vClusterValue = vCluster.(string)
deleteInterceptorPayload.VCluster = &vClusterValue
} else {
deleteInterceptorPayload.VCluster = &passthrough
}
group := scope.(map[string]interface{})["group"]
var groupValue string
if group != nil && group.(string) != "" {
groupValue = group.(string)
deleteInterceptorPayload.Group = &groupValue
}
username := scope.(map[string]interface{})["username"]
var usernameValue string
if username != nil && username.(string) != "" {
usernameValue = username.(string)
deleteInterceptorPayload.Username = &usernameValue
}
}
if !ok {
return fmt.Errorf("kind %s not found", resource.Kind)
}
deletePath, err := kind.DeletePath(resource)
if err != nil {
return err
}
url := client.baseUrl + deletePath
resp, err := client.client.R().SetBody(deleteInterceptorPayload).Delete(url)
if err != nil {
return err
} else if resp.IsError() {
return fmt.Errorf(extractApiError(resp))
} else {
fmt.Printf("%s/%s deleted\n", kind.GetName(), resource.Name)
}

return err
}

func (client *GatewayClient) DeleteKindByNameAndVCluster(kind *schema.Kind, param map[string]string) error {
url := client.baseUrl + kind.ListPath(nil)
req := client.client.R()
req.SetBody(param)
resp, err := req.Delete(url)
if err != nil {
return err
} else if resp.IsError() {
return fmt.Errorf(extractApiError(resp))
} else {
fmt.Printf("%s/%s deleted\n", kind.GetName(), param)
}

return err
}

func (client *GatewayClient) DeleteInterceptor(kind *schema.Kind, name string, param map[string]string) error {
url := client.baseUrl + kind.ListPath(nil) + "/" + name
req := client.client.R()
var bodyParams = make(map[string]interface{})
for k, v := range param {
if v == "" {
bodyParams[k] = nil
} else {
bodyParams[k] = v
}
}
req.SetBody(bodyParams)
resp, err := req.Delete(url)
if err != nil {
return err
} else if resp.IsError() {
return fmt.Errorf(extractApiError(resp))
} else {
fmt.Printf("%s/%s deleted\n", kind.GetName(), param)
}

return err
}

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

func (client *GatewayClient) Apply(resource *resource.Resource, dryMode bool) (string, error) {
kinds := client.GetKinds()
kind, ok := kinds[resource.Kind]
if !ok {
return "", fmt.Errorf("kind %s not found", resource.Kind)
}
applyPath, err := kind.ApplyPath(resource)
if err != nil {
return "", err
}
url := client.baseUrl + applyPath
builder := client.client.R().SetBody(resource.Json)
if dryMode {
builder = builder.SetQueryParam("dryMode", "true")
}
resp, err := builder.Put(url)
if err != nil {
return "", err
} else if resp.IsError() {
return "", fmt.Errorf(extractApiError(resp))
}
bodyBytes := resp.Body()
var upsertResponse UpsertResponse
err = json.Unmarshal(bodyBytes, &upsertResponse)
//in case backend format change (not json string anymore). Let not fail the client for that
if err != nil {
return resp.String(), nil
}
return upsertResponse.UpsertResult, nil
}

func (client *GatewayClient) GetOpenApi() ([]byte, error) {
url := client.baseUrl + "/gateway/v2/docs"
resp, err := client.client.R().Get(url)
if err != nil {
return nil, err
} else if resp.IsError() {
return nil, fmt.Errorf(resp.String())
}
return resp.Body(), nil
}

func (client *GatewayClient) initKindFromApi() error {
data, err := client.GetOpenApi()
if err != nil {
return fmt.Errorf("Cannot get openapi: %s", err)
}
schema, err := schema.New(data)
if err != nil {
return fmt.Errorf("Cannot parse openapi: %s", err)
}
strict := false
client.kinds, err = schema.GetGatewayKinds(strict)
if err != nil {
fmt.Errorf("Cannot extract kinds from openapi: %s", err)
}
return nil
}

func (client *GatewayClient) GetKinds() schema.KindCatalog {
return client.kinds
}
Loading
Loading