diff --git a/client/client.go b/client/console_client.go similarity index 98% rename from client/client.go rename to client/console_client.go index a613adb..505c0db 100644 --- a/client/client.go +++ b/client/console_client.go @@ -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"), @@ -310,7 +310,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) } diff --git a/client/gateway_client.go b/client/gateway_client.go index 84d8afa..2be8a57 100644 --- a/client/gateway_client.go +++ b/client/gateway_client.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "os" - "strings" "github.com/conduktor/ctl/resource" "github.com/conduktor/ctl/schema" @@ -63,7 +62,7 @@ func MakeGateway(apiParameter GatewayApiParameter) (*GatewayClient, error) { func MakeGatewayClientFromEnv() (*GatewayClient, error) { apiParameter := GatewayApiParameter{ BaseUrl: os.Getenv("CDK_GATEWAY_BASE_URL"), - Debug: strings.ToLower(os.Getenv("CDK_DEBUG")) == "true", + Debug: utils.CdkDebug(), CdkGatewayUser: os.Getenv("CDK_GATEWAY_USER"), CdkGatewayPassword: os.Getenv("CDK_GATEWAY_PASSWORD"), } @@ -330,6 +329,10 @@ func (client *GatewayClient) DeleteInterceptor(kind *schema.Kind, name string, p 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] @@ -382,7 +385,7 @@ func (client *GatewayClient) initKindFromApi() error { return fmt.Errorf("Cannot parse openapi: %s", err) } strict := false - client.kinds, err = schema.GetKinds(strict) + client.kinds, err = schema.GetGatewayKinds(strict) if err != nil { fmt.Errorf("Cannot extract kinds from openapi: %s", err) } diff --git a/cmd/apply.go b/cmd/apply.go index 376e262..852999a 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -2,10 +2,11 @@ package cmd import ( "fmt" + "os" + "github.com/conduktor/ctl/resource" "github.com/conduktor/ctl/schema" "github.com/spf13/cobra" - "os" ) var dryRun *bool @@ -40,7 +41,7 @@ func initApply(kinds schema.KindCatalog) { if isGatewayResource(resource, kinds) { upsertResult, err = gatewayApiClient().Apply(&resource, *dryRun) } else { - upsertResult, err = apiClient().Apply(&resource, *dryRun) + upsertResult, err = consoleApiClient().Apply(&resource, *dryRun) } if err != nil { fmt.Fprintf(os.Stderr, "Could not apply resource %s/%s: %s\n", resource.Kind, resource.Name, err) diff --git a/cmd/consoleMkKind.go b/cmd/consoleMkKind.go new file mode 100644 index 0000000..3a600c1 --- /dev/null +++ b/cmd/consoleMkKind.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "github.com/conduktor/ctl/schema" + "github.com/conduktor/ctl/utils" + "github.com/spf13/cobra" +) + +func initConsoleMkKind() { + var prettyPrint *bool + var nonStrict *bool + + var makeKind = &cobra.Command{ + Use: "makeKind [file]", + Short: "Make kind json from openapi file if file not given it will read from api", + Long: ``, + Aliases: []string{"mkKind", "makeConsoleKind"}, + Args: cobra.RangeArgs(0, 1), + Hidden: !utils.CdkDebug(), + Run: func(cmd *cobra.Command, args []string) { + runMkKind(cmd, args, *prettyPrint, *nonStrict, func() ([]byte, error) { return consoleApiClient().GetOpenApi() }, func(schema *schema.Schema, strict bool) (schema.KindCatalog, error) { + return schema.GetConsoleKinds(strict) + }) + }, + } + rootCmd.AddCommand(makeKind) + + prettyPrint = makeKind.Flags().BoolP("pretty", "p", false, "Pretty print the output") + nonStrict = makeKind.Flags().BoolP("non-strict", "n", false, "Don't be strict on the parsing of the schema") +} diff --git a/cmd/delete.go b/cmd/delete.go index f5d952b..e879a54 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -3,7 +3,6 @@ package cmd import ( "fmt" "os" - "strings" "github.com/conduktor/ctl/schema" "github.com/spf13/cobra" @@ -33,7 +32,7 @@ func initDelete(kinds schema.KindCatalog) { err = gatewayApiClient().DeleteResourceInterceptors(&resource) } } else { - err = apiClient().DeleteResource(&resource) + err = consoleApiClient().DeleteResource(&resource) } if err != nil { fmt.Fprintf(os.Stderr, "Could not delete resource %s/%s: %s\n", resource.Kind, resource.Name, err) @@ -66,7 +65,7 @@ func initDelete(kinds schema.KindCatalog) { Use: fmt.Sprintf("%s [name]", name), Short: "Delete resource of kind " + name, Args: cobra.MatchAll(cobra.ExactArgs(1)), - Aliases: []string{strings.ToLower(name), strings.ToLower(name) + "s", name + "s"}, + Aliases: buildAlias(name), Run: func(cmd *cobra.Command, args []string) { parentValue := make([]string, len(parentFlagValue)) for i, v := range parentFlagValue { @@ -76,7 +75,7 @@ func initDelete(kinds schema.KindCatalog) { if isGatewayKind(kind) { err = gatewayApiClient().Delete(&kind, parentValue, args[0]) } else { - err = apiClient().Delete(&kind, parentValue, args[0]) + err = consoleApiClient().Delete(&kind, parentValue, args[0]) } if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) diff --git a/cmd/delete_gateway_specifics.go b/cmd/delete_gateway_specifics.go index ee3e371..d7d3993 100644 --- a/cmd/delete_gateway_specifics.go +++ b/cmd/delete_gateway_specifics.go @@ -3,7 +3,6 @@ package cmd import ( "fmt" "os" - "strings" "github.com/conduktor/ctl/schema" "github.com/spf13/cobra" @@ -15,11 +14,11 @@ func buildDeleteByVClusterAndNameCmd(kind schema.Kind) *cobra.Command { name := kind.GetName() var nameValue string var vClusterValue string - var aliasTopicDeleteCmd = &cobra.Command{ + var deleteCmd = &cobra.Command{ Use: fmt.Sprintf("%s [name]", name), Short: "Delete resource of kind " + name, Args: cobra.ExactArgs(0), - Aliases: []string{strings.ToLower(name), strings.ToLower(name) + "s", name + "s"}, + Aliases: buildAlias(name), Run: func(cmd *cobra.Command, args []string) { var err error queryParams := make(map[string]string) @@ -40,12 +39,12 @@ func buildDeleteByVClusterAndNameCmd(kind schema.Kind) *cobra.Command { }, } - aliasTopicDeleteCmd.Flags().StringVar(&nameValue, nameFlag, "", "name of the "+name) - aliasTopicDeleteCmd.Flags().StringVar(&vClusterValue, vClusterFlag, "", "vCluster of the "+name) + deleteCmd.Flags().StringVar(&nameValue, nameFlag, "", "name of the "+name) + deleteCmd.Flags().StringVar(&vClusterValue, vClusterFlag, "", "vCluster of the "+name) - aliasTopicDeleteCmd.MarkFlagRequired(nameFlag) + deleteCmd.MarkFlagRequired(nameFlag) - return aliasTopicDeleteCmd + return deleteCmd } func buildDeleteInterceptorsCmd(kind schema.Kind) *cobra.Command { @@ -62,7 +61,7 @@ func buildDeleteInterceptorsCmd(kind schema.Kind) *cobra.Command { Use: fmt.Sprintf("%s [name]", name), Short: "Delete resource of kind " + name, Args: cobra.ExactArgs(0), - Aliases: []string{strings.ToLower(name), strings.ToLower(name) + "s", name + "s"}, + Aliases: buildAlias(name), Run: func(cmd *cobra.Command, args []string) { var err error queryParams := make(map[string]string) diff --git a/cmd/gatewayMkKind.go b/cmd/gatewayMkKind.go new file mode 100644 index 0000000..a55a0bf --- /dev/null +++ b/cmd/gatewayMkKind.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "github.com/conduktor/ctl/schema" + "github.com/conduktor/ctl/utils" + "github.com/spf13/cobra" +) + +func initGatewayMkKind() { + var prettyPrint *bool + var nonStrict *bool + + var makeKind = &cobra.Command{ + Use: "gatewayMakeKind [file]", + Short: "Make kind json from openapi file if file not given it will read from api", + Long: ``, + Aliases: []string{"gatewayMkKind", "gwMkKind"}, + Args: cobra.RangeArgs(0, 1), + Hidden: !utils.CdkDebug(), + Run: func(cmd *cobra.Command, args []string) { + runMkKind(cmd, args, *prettyPrint, *nonStrict, func() ([]byte, error) { return gatewayApiClient().GetOpenApi() }, func(schema *schema.Schema, strict bool) (schema.KindCatalog, error) { + return schema.GetGatewayKinds(strict) + }) + }, + } + rootCmd.AddCommand(makeKind) + + prettyPrint = makeKind.Flags().BoolP("pretty", "p", false, "Pretty print the output") + nonStrict = makeKind.Flags().BoolP("non-strict", "n", false, "Don't be strict on the parsing of the schema") +} diff --git a/cmd/gateway_utils.go b/cmd/gateway_utils.go index 000b120..3e5a25e 100644 --- a/cmd/gateway_utils.go +++ b/cmd/gateway_utils.go @@ -1,13 +1,15 @@ package cmd import ( + "strings" + "github.com/conduktor/ctl/resource" "github.com/conduktor/ctl/schema" - "strings" ) func isGatewayKind(kind schema.Kind) bool { - return strings.Contains(kind.GetLatestKindVersion().ListPath, "gateway") + _, ok := kind.GetLatestKindVersion().(*schema.GatewayKindVersion) + return ok } func isGatewayResource(resource resource.Resource, kinds schema.KindCatalog) bool { diff --git a/cmd/genericMkKind.go b/cmd/genericMkKind.go new file mode 100644 index 0000000..c0c55f8 --- /dev/null +++ b/cmd/genericMkKind.go @@ -0,0 +1,52 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/conduktor/ctl/schema" + "github.com/spf13/cobra" +) + +func runMkKind(cmd *cobra.Command, args []string, prettyPrint bool, nonStrict bool, getOpenApi func() ([]byte, error), getKinds func(*schema.Schema, bool) (schema.KindCatalog, error)) { + var kinds map[string]schema.Kind + if len(args) == 1 { + data, err := os.ReadFile(args[0]) + if err != nil { + panic(err) + } + schema, err := schema.New(data) + if err != nil { + panic(err) + } + kinds, err = getKinds(schema, !nonStrict) + if err != nil { + panic(err) + } + } else { + data, err := getOpenApi() + if err != nil { + panic(err) + } + schema, err := schema.New(data) + if err != nil { + panic(err) + } + kinds, err = getKinds(schema, !nonStrict) + if err != nil { + panic(err) + } + } + var payload []byte + var err error + if prettyPrint { + payload, err = json.MarshalIndent(kinds, "", " ") + } else { + payload, err = json.Marshal(kinds) + } + if err != nil { + panic(err) + } + fmt.Print(string(payload)) +} diff --git a/cmd/get.go b/cmd/get.go index d4194bd..7d1a145 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -3,7 +3,6 @@ package cmd import ( "fmt" "os" - "strings" "github.com/conduktor/ctl/resource" "github.com/conduktor/ctl/schema" @@ -39,7 +38,7 @@ func initGet(kinds schema.KindCatalog) { Short: "Get resource of kind " + name, Args: cobra.MatchAll(cobra.MaximumNArgs(1)), Long: `If name not provided it will list all resource`, - Aliases: []string{strings.ToLower(name), strings.ToLower(name) + "s", name + "s"}, + Aliases: buildAlias(name), Run: func(cmd *cobra.Command, args []string) { parentValue := make([]string, len(parentFlagValue)) for i, v := range parentFlagValue { @@ -51,7 +50,7 @@ func initGet(kinds schema.KindCatalog) { if isGatewayKind(kind) { result, err = gatewayApiClient().Get(&kind, parentValue) } else { - result, err = apiClient().Get(&kind, parentValue) + result, err = consoleApiClient().Get(&kind, parentValue) } for _, r := range result { r.PrintPreservingOriginalFieldOrder() @@ -62,7 +61,7 @@ func initGet(kinds schema.KindCatalog) { if isGatewayKind(kind) { result, err = gatewayApiClient().Describe(&kind, parentValue, args[0]) } else { - result, err = apiClient().Describe(&kind, parentValue, args[0]) + result, err = consoleApiClient().Describe(&kind, parentValue, args[0]) } result.PrintPreservingOriginalFieldOrder() } diff --git a/cmd/get_gateway_specifics.go b/cmd/get_gateway_specifics.go index 83691d4..0d2a4e9 100644 --- a/cmd/get_gateway_specifics.go +++ b/cmd/get_gateway_specifics.go @@ -10,6 +10,14 @@ import ( "github.com/spf13/cobra" ) +func removeTrailingSIfAny(name string) string { + return strings.TrimSuffix(name, "s") +} + +func buildAlias(name string) []string { + return []string{strings.ToLower(name), removeTrailingSIfAny(strings.ToLower(name)), removeTrailingSIfAny(name)} +} + func buildListFilteredByVClusterOrNameCmd(kind schema.Kind) *cobra.Command { const nameFlag = "name" const vClusterFlag = "vcluster" @@ -18,11 +26,11 @@ func buildListFilteredByVClusterOrNameCmd(kind schema.Kind) *cobra.Command { var vClusterValue string var showDefaultsValue string name := kind.GetName() - var aliasTopicGetCmd = &cobra.Command{ + var getCmd = &cobra.Command{ Use: fmt.Sprintf("%s [name]", name), Short: "Get resource of kind " + kind.GetName(), Args: cobra.ExactArgs(0), - Aliases: []string{strings.ToLower(name), strings.ToLower(name) + "s", name + "s"}, + Aliases: buildAlias(name), Run: func(cmd *cobra.Command, args []string) { var result []resource.Resource var err error @@ -49,11 +57,11 @@ func buildListFilteredByVClusterOrNameCmd(kind schema.Kind) *cobra.Command { }, } - aliasTopicGetCmd.Flags().StringVar(&nameValue, nameFlag, "", "filter the "+name+" result list by name") - aliasTopicGetCmd.Flags().StringVar(&vClusterValue, vClusterFlag, "", "filter the "+name+" result list by vcluster") - aliasTopicGetCmd.Flags().StringVar(&showDefaultsValue, "showDefaults", "", "Toggle show defaults values (true|false, default false)") + getCmd.Flags().StringVar(&nameValue, nameFlag, "", "filter the "+name+" result list by name") + getCmd.Flags().StringVar(&vClusterValue, vClusterFlag, "", "filter the "+name+" result list by vcluster") + getCmd.Flags().StringVar(&showDefaultsValue, "showDefaults", "", "Toggle show defaults values (true|false, default false)") - return aliasTopicGetCmd + return getCmd } func buildListFilteredIntercpetorsCmd(kind schema.Kind) *cobra.Command { @@ -68,11 +76,11 @@ func buildListFilteredIntercpetorsCmd(kind schema.Kind) *cobra.Command { var usernameValue string var globalValue bool name := kind.GetName() - var aliasTopicGetCmd = &cobra.Command{ + var getCmd = &cobra.Command{ Use: fmt.Sprintf("%s [name]", name), Short: "Get resource of kind " + name, Args: cobra.ExactArgs(0), - Aliases: []string{strings.ToLower(name), strings.ToLower(name) + "s", name + "s"}, + Aliases: buildAlias(name), Run: func(cmd *cobra.Command, args []string) { var result []resource.Resource var err error @@ -93,11 +101,11 @@ func buildListFilteredIntercpetorsCmd(kind schema.Kind) *cobra.Command { }, } - aliasTopicGetCmd.Flags().StringVar(&nameValue, nameFlag, "", "filter the "+name+" result list by name") - aliasTopicGetCmd.Flags().StringVar(&vClusterValue, vClusterFlag, "", "filter the "+name+" result list by vcluster") - aliasTopicGetCmd.Flags().StringVar(&groupValue, groupFlag, "", "filter the "+name+" result list by group") - aliasTopicGetCmd.Flags().StringVar(&usernameValue, usernameFlag, "", "filter the "+name+" result list by username") - aliasTopicGetCmd.Flags().Bool(globalFlag, false, "Keep only global interceptors") + getCmd.Flags().StringVar(&nameValue, nameFlag, "", "filter the "+name+" result list by name") + getCmd.Flags().StringVar(&vClusterValue, vClusterFlag, "", "filter the "+name+" result list by vcluster") + getCmd.Flags().StringVar(&groupValue, groupFlag, "", "filter the "+name+" result list by group") + getCmd.Flags().StringVar(&usernameValue, usernameFlag, "", "filter the "+name+" result list by username") + getCmd.Flags().Bool(globalFlag, false, "Keep only global interceptors") - return aliasTopicGetCmd + return getCmd } diff --git a/cmd/login.go b/cmd/login.go index 3deca6c..7b3f961 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -3,9 +3,9 @@ package cmd import ( "fmt" "os" - "strings" "github.com/conduktor/ctl/client" + "github.com/conduktor/ctl/utils" "github.com/spf13/cobra" ) @@ -16,7 +16,7 @@ var loginCmd = &cobra.Command{ Long: `Use must use CDK_USER CDK_PASSWORD environment variables to login`, Args: cobra.RangeArgs(0, 0), Run: func(cmd *cobra.Command, args []string) { - specificApiClient, err := client.Make(client.ApiParameter{BaseUrl: os.Getenv("CDK_BASE_URL"), Debug: strings.ToLower(os.Getenv("CDK_DEBUG")) == "true"}) + specificApiClient, err := client.Make(client.ApiParameter{BaseUrl: os.Getenv("CDK_BASE_URL"), Debug: utils.CdkDebug()}) if *debug { specificApiClient.ActivateDebug() } diff --git a/cmd/mkKind.go b/cmd/mkKind.go deleted file mode 100644 index 90052dd..0000000 --- a/cmd/mkKind.go +++ /dev/null @@ -1,67 +0,0 @@ -package cmd - -import ( - "encoding/json" - "fmt" - "github.com/conduktor/ctl/schema" - "github.com/spf13/cobra" - "os" -) - -func initMkKind() { - var prettyPrint *bool - var nonStrict *bool - - var makeKind = &cobra.Command{ - Use: "makeKind [file]", - Short: "Make kind json from openapi file if file not given it will read from api", - Long: ``, - Args: cobra.RangeArgs(0, 1), - Hidden: true, - Run: func(cmd *cobra.Command, args []string) { - var kinds map[string]schema.Kind - if len(args) == 1 { - data, err := os.ReadFile(args[0]) - if err != nil { - panic(err) - } - schema, err := schema.New(data) - if err != nil { - panic(err) - } - kinds, err = schema.GetKinds(!*nonStrict) - if err != nil { - panic(err) - } - } else { - data, err := apiClient().GetOpenApi() - if err != nil { - panic(err) - } - schema, err := schema.New(data) - if err != nil { - panic(err) - } - kinds, err = schema.GetKinds(!*nonStrict) - if err != nil { - panic(err) - } - } - var payload []byte - var err error - if *prettyPrint { - payload, err = json.MarshalIndent(kinds, "", " ") - } else { - payload, err = json.Marshal(kinds) - } - if err != nil { - panic(err) - } - fmt.Print(string(payload)) - }, - } - rootCmd.AddCommand(makeKind) - - prettyPrint = makeKind.Flags().BoolP("pretty", "p", false, "Pretty print the output") - nonStrict = makeKind.Flags().BoolP("non-strict", "n", false, "Don't be strict on the parsing of the schema") -} diff --git a/cmd/printKind.go b/cmd/printKind.go index c34962c..f866aca 100644 --- a/cmd/printKind.go +++ b/cmd/printKind.go @@ -3,7 +3,9 @@ package cmd import ( "encoding/json" "fmt" + "github.com/conduktor/ctl/schema" + "github.com/conduktor/ctl/utils" "github.com/spf13/cobra" ) @@ -16,7 +18,7 @@ func initPrintKind(kinds schema.KindCatalog) { Short: "Print kind catalog used", Long: ``, Args: cobra.NoArgs, - Hidden: true, + Hidden: !utils.CdkDebug(), Run: func(cmd *cobra.Command, args []string) { var payload []byte var err error diff --git a/cmd/root.go b/cmd/root.go index 0db9c26..a973475 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -15,7 +15,7 @@ var apiClientError error var gatewayApiClient_ *client.GatewayClient var gatewayApiClientError error -func apiClient() *client.Client { +func consoleApiClient() *client.Client { if apiClientError != nil { fmt.Fprintf(os.Stderr, "Cannot create client: %s", apiClientError) os.Exit(1) @@ -40,7 +40,8 @@ Additionally, you can configure client TLS authentication by providing your cert For server TLS authentication, you can ignore the certificate by setting CDK_INSECURE=true, or provide a certificate authority using CDK_CACERT.`, PersistentPreRun: func(cmd *cobra.Command, args []string) { if *debug { - apiClient().ActivateDebug() + consoleApiClient().ActivateDebug() + gatewayApiClient().ActivateDebug() } }, Run: func(cmd *cobra.Command, args []string) { @@ -80,6 +81,7 @@ func init() { initGet(kinds) initDelete(kinds) initApply(kinds) - initMkKind() + initConsoleMkKind() + initGatewayMkKind() initPrintKind(kinds) } diff --git a/cmd/token.go b/cmd/token.go index 7e0edb3..4324042 100644 --- a/cmd/token.go +++ b/cmd/token.go @@ -30,7 +30,7 @@ var listAdminCmd = &cobra.Command{ Use: "admin", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - result, err := apiClient().ListAdminToken() + result, err := consoleApiClient().ListAdminToken() if err != nil { fmt.Fprintf(os.Stderr, "Could not list admin token: %s\n", err) os.Exit(1) @@ -48,7 +48,7 @@ var listApplicationInstanceTokenCmd = &cobra.Command{ Use: "application-instance", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - result, err := apiClient().ListApplicationInstanceToken(*applicationInstanceNameForList) + result, err := consoleApiClient().ListApplicationInstanceToken(*applicationInstanceNameForList) if err != nil { fmt.Fprintf(os.Stderr, "Could not list application-instance token: %s\n", err) os.Exit(1) @@ -86,18 +86,18 @@ var createAdminTokenCmd = &cobra.Command{ fmt.Fprintln(os.Stderr, "Please set CDK_USER if you set CDK_PASSWORD") os.Exit(3) } else if username != "" && password != "" { - jwtToken, err := apiClient().Login(username, password) + jwtToken, err := consoleApiClient().Login(username, password) if err != nil { fmt.Fprintf(os.Stderr, "Could not login: %s\n", err) os.Exit(4) } - apiClient().SetApiKey(jwtToken.AccessToken) + consoleApiClient().SetApiKey(jwtToken.AccessToken) } else if os.Getenv("CDK_API_KEY") == "" { fmt.Fprintln(os.Stderr, "Please set CDK_API_KEY or CDK_USER and CDK_PASSWORD") os.Exit(5) } - result, err := apiClient().CreateAdminToken(args[0]) + result, err := consoleApiClient().CreateAdminToken(args[0]) if err != nil { fmt.Fprintf(os.Stderr, "Could not create admin token: %s\n", err) os.Exit(4) @@ -110,7 +110,7 @@ var createApplicationInstanceTokenCmd = &cobra.Command{ Use: "application-instance --application-instance=myappinstance ", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { - result, err := apiClient().CreateApplicationInstanceToken(*applicationInstanceNameForCreate, args[0]) + result, err := consoleApiClient().CreateApplicationInstanceToken(*applicationInstanceNameForCreate, args[0]) if err != nil { fmt.Fprintf(os.Stderr, "Could not create application-instance token: %s\n", err) os.Exit(1) @@ -123,7 +123,7 @@ var deleteTokenCmd = &cobra.Command{ Use: "delete ", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { - err := apiClient().DeleteToken(args[0]) + err := consoleApiClient().DeleteToken(args[0]) if err != nil { fmt.Fprintf(os.Stderr, "Could not delete token: %s\n", err) os.Exit(1) diff --git a/schema/schema_test.go b/schema/console_schema_test.go similarity index 90% rename from schema/schema_test.go rename to schema/console_schema_test.go index 43fd2af..34224ea 100644 --- a/schema/schema_test.go +++ b/schema/console_schema_test.go @@ -21,7 +21,7 @@ func TestGetKindWithYamlFromOldConsolePlusWithoutOrder(t *testing.T) { t.Fatalf("failed creating new schema: %s", err) } - kinds, err := schema.GetKinds(false) + kinds, err := schema.GetConsoleKinds(false) if err != nil { t.Fatalf("failed getting kinds: %s", err) } @@ -29,7 +29,7 @@ func TestGetKindWithYamlFromOldConsolePlusWithoutOrder(t *testing.T) { expected := KindCatalog{ "Application": { Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ Name: "Application", ListPath: "/public/self-serve/v1/application", ParentPathParam: make([]string, 0), @@ -39,7 +39,7 @@ func TestGetKindWithYamlFromOldConsolePlusWithoutOrder(t *testing.T) { }, "ApplicationInstance": { Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ Name: "ApplicationInstance", ListPath: "/public/self-serve/v1/application-instance", ParentPathParam: make([]string, 0), @@ -49,7 +49,7 @@ func TestGetKindWithYamlFromOldConsolePlusWithoutOrder(t *testing.T) { }, "ApplicationInstancePermission": { Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ Name: "ApplicationInstancePermission", ListPath: "/public/self-serve/v1/application-instance-permission", ParentPathParam: make([]string, 0), @@ -59,7 +59,7 @@ func TestGetKindWithYamlFromOldConsolePlusWithoutOrder(t *testing.T) { }, "TopicPolicy": { Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ Name: "TopicPolicy", ListPath: "/public/self-serve/v1/topic-policy", ParentPathParam: make([]string, 0), @@ -69,7 +69,7 @@ func TestGetKindWithYamlFromOldConsolePlusWithoutOrder(t *testing.T) { }, "Topic": { Versions: map[int]KindVersion{ - 2: { + 2: &ConsoleKindVersion{ Name: "Topic", ListPath: "/public/kafka/v2/cluster/{cluster}/topic", ParentPathParam: []string{"cluster"}, @@ -96,7 +96,7 @@ func TestGetKindWithYamlFromConsolePlus(t *testing.T) { t.Fatalf("failed creating new schema: %s", err) } - kinds, err := schema.GetKinds(true) + kinds, err := schema.GetConsoleKinds(true) if err != nil { t.Fatalf("failed getting kinds: %s", err) } @@ -104,7 +104,7 @@ func TestGetKindWithYamlFromConsolePlus(t *testing.T) { expected := KindCatalog{ "Application": { Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ Name: "Application", ListPath: "/public/self-serve/v1/application", ParentPathParam: []string{}, @@ -114,7 +114,7 @@ func TestGetKindWithYamlFromConsolePlus(t *testing.T) { }, "ApplicationInstance": { Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ Name: "ApplicationInstance", ListPath: "/public/self-serve/v1/application-instance", ParentPathParam: []string{}, @@ -124,7 +124,7 @@ func TestGetKindWithYamlFromConsolePlus(t *testing.T) { }, "ApplicationInstancePermission": { Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ Name: "ApplicationInstancePermission", ListPath: "/public/self-serve/v1/application-instance-permission", ParentPathParam: []string{}, @@ -134,7 +134,7 @@ func TestGetKindWithYamlFromConsolePlus(t *testing.T) { }, "ApplicationGroup": { Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ Name: "ApplicationGroup", ListPath: "/public/self-serve/v1/application-group", ParentPathParam: []string{}, @@ -144,7 +144,7 @@ func TestGetKindWithYamlFromConsolePlus(t *testing.T) { }, "TopicPolicy": { Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ Name: "TopicPolicy", ListPath: "/public/self-serve/v1/topic-policy", ParentPathParam: []string{}, @@ -154,7 +154,7 @@ func TestGetKindWithYamlFromConsolePlus(t *testing.T) { }, "Topic": { Versions: map[int]KindVersion{ - 2: { + 2: &ConsoleKindVersion{ Name: "Topic", ListPath: "/public/kafka/v2/cluster/{cluster}/topic", ParentPathParam: []string{"cluster"}, @@ -164,7 +164,7 @@ func TestGetKindWithYamlFromConsolePlus(t *testing.T) { }, "Subject": { Versions: map[int]KindVersion{ - 2: { + 2: &ConsoleKindVersion{ Name: "Subject", ListPath: "/public/kafka/v2/cluster/{cluster}/subject", ParentPathParam: []string{"cluster"}, @@ -174,7 +174,7 @@ func TestGetKindWithYamlFromConsolePlus(t *testing.T) { }, "User": { Versions: map[int]KindVersion{ - 2: { + 2: &ConsoleKindVersion{ Name: "User", ListPath: "/public/iam/v2/user", ParentPathParam: []string{}, @@ -184,7 +184,7 @@ func TestGetKindWithYamlFromConsolePlus(t *testing.T) { }, "Group": { Versions: map[int]KindVersion{ - 2: { + 2: &ConsoleKindVersion{ Name: "Group", ListPath: "/public/iam/v2/group", ParentPathParam: []string{}, @@ -194,7 +194,7 @@ func TestGetKindWithYamlFromConsolePlus(t *testing.T) { }, "KafkaCluster": { Versions: map[int]KindVersion{ - 2: { + 2: &ConsoleKindVersion{ Name: "KafkaCluster", ListPath: "/public/console/v2/kafka-cluster", ParentPathParam: []string{}, @@ -221,7 +221,7 @@ func TestGetKindWithMultipleVersion(t *testing.T) { t.Fatalf("failed creating new schema: %s", err) } - kinds, err := schema.GetKinds(false) + kinds, err := schema.GetConsoleKinds(false) if err != nil { t.Fatalf("failed getting kinds: %s", err) } @@ -229,13 +229,13 @@ func TestGetKindWithMultipleVersion(t *testing.T) { expected := KindCatalog{ "Topic": { Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ Name: "Topic", ListPath: "/public/v1/cluster/{cluster}/topic", ParentPathParam: []string{"cluster"}, Order: DefaultPriority, }, - 2: { + 2: &ConsoleKindVersion{ Name: "Topic", ListPath: "/public/v2/cluster/{cluster}/sa/{sa}/topic", ParentPathParam: []string{"cluster", "sa"}, @@ -261,7 +261,7 @@ func TestKindWithMissingMetadataField(t *testing.T) { t.Fatalf("failed creating new schema: %s", err) } - _, err = schema.GetKinds(true) + _, err = schema.GetConsoleKinds(true) if !strings.Contains(err.Error(), "Parent path param sa not found in metadata for kind Topic") { t.Fatalf("Not expected error: %s", err) } @@ -279,7 +279,7 @@ func TestKindNotRequiredMetadataField(t *testing.T) { t.Fatalf("failed creating new schema: %s", err) } - _, err = schema.GetKinds(true) + _, err = schema.GetConsoleKinds(true) if !strings.Contains(err.Error(), "Parent path param sa in metadata for kind Topic not required") { t.Fatalf("Not expected error: %s", err) } diff --git a/schema/gateway.yaml b/schema/gateway.yaml new file mode 100644 index 0000000..08af72f --- /dev/null +++ b/schema/gateway.yaml @@ -0,0 +1,3166 @@ +openapi: 3.1.0 +info: + title: Conduktor API + version: v2 + summary: The API to interact with Conduktor Gateway programmatically + contact: + email: contact@conduktor.io + url: https://docs.conduktor.io + x-logo: + url: https://avatars.githubusercontent.com/u/60062294?s=200&v=4 + backgroundColor: '#FFFFFF' + altText: Conduktor logo +tags: +- name: Introduction + description: | + The Conduktor Gateway REST API 's aim is to help you configure your Gateway. + + Get started with Conduktor Gateway [self-hosted](https://docs.conduktor.io/gateway/installation/) today. Setup takes only a few minutes. +- name: Authentication + description: |- + Authentication to the API requires a basic authentication. + + To get a token and use it you must go through the following steps: + + * Configure the admin password in the Gateway YAML configuration file. + + * Use the password in the **authorization** header of your requests. + + Example: + + ```shell + curl -X GET "https://your.gateway-api.host/gateway/v2/vclusters" \ + -H "accept: application/json" \ + --user "admin:password" + ``` +- name: Kinds + description: | + ### Definition + + Kinds the resource types of the Conduktor gateway. + + ### Conduktor Gateway Kinds + + The following kinds are available in the Conduktor Gateway API: + + * `VCluster` + * `AliasTopic` + * `ConcentratedTopic` + * `ConcentrationRule` + * `Interceptor` + * `Plugin` + * `ServiceAccount` + * `Token` + * `Group` +- name: Api Groups + description: |+ + ### Definition + + API groups a set of resources that share the same API path prefix. They are used to organize the API endpoints and the + resources they manage. + The versioning is set at this level, so all the resources in the same group share the same version. + Kinds of a same group can be nested paths in the API, for example, the `vcluster` kind can have an `alias-topic` kind + nested in it. + + ### Conduktor Api Groups + + The Gateway API consist of a single API group right now (`gateway`), and it manages the following resources: + + | Api Group | Kinds | + |-----------|----------------------------------------------------------------------------------------------------------------------------------------------------| + | `gateway` | `vclusters`, `alias-topics`, `concentrated-topics`, `concentration-rules`,
`interceptors`, `plugins`, `service-accounts`, `groups`, `tokens` | + + + +- name: Versioning + description: |+ + * __The version is set at the api group level__. It is incremented when a breaking change happens in the schema of an endpoint of the group (that has been marked `stable`). The n-1 version is still available for a while to allow users to migrate. The version is part of the endpoint path. + * The API version (v2) is the global version of the Conduktor Gateway API, it should not change unless there is a complete overhaul of the API. + + + Endpoint also have a status to manage their API lifecycle, following the order below: + * __preview__: this is an early-stage feature, really likely to change + * __beta__: early access feature, breaking change + * __stable__: Production-ready endpoint, no breaking change + * __deprecated__: This endpoint isn't supported anymore and the user should migrate + + +- name: Conventions + description: |+ + ### Path conventions + + The API follows as much as possible the endpoints structure below for each kind of resource: + + * `GET /{api-group}/{version}/{kind}/{name}` to read a resource + * `GET /{api-group}/{version}/{kind}` to list resources of a kind + * `PUT /{api-group}/{version}/{kind}` to update or create a resource + * `DELETE /{api-group}/{version}/{kind}/{name}` to delete a resource (returns 204 No Content) + * `POST` is used for specific operations that don't fit this CRUD model. PUT is the default verb for updates and + creations. + * Important principle: the result of a GET can be reused as the body of a PUT to update the resource. + + __Non-unique names__: + When a `name` is not enough to uniquely identify a resource, the GET and DELETE endpoint are different + The GET by name is replaced by query parameters (returning lists or the searched item if the criterias are the elements of the key), and the DELETE by name is replaced by a DELETE with a body. + For example, an `alias-topic` is identified by its `name` and the `vCluster` gives the following endpoints: + + * `GET /gateway/v2/vclusters/{name}/alias-topics?name={name}&vcluster={vcluster}` + * `PUT /gateway/v2/vclusters/{name}/alias-topics` + * `DELETE /gateway/v2/vclusters/{name}/alias-topics` with a body containing the `name` and the `vCluster` + + ### Other conventions + + * All requests and responses are in JSON and should have their `content-type` header set to `application/json` + * Every kind has a pluralized name (e.g. `vclusters` for the `VCluster` kind) that is used in the endpoint path. + * Errors have a standard format: + * The HTTP status code is used to indicate the type of error. + * The response body contains a common JSON object for every error: + * `title`: a unique error code for the error. + * `message`: a human-readable message for the error. + * `cause`: additional information about the error. + * All timestamps are in ISO 8601 format. + +- name: tokens + description: |+ + ### Definition + + The token group contains just a utility endpoint to generate a token for a given Local ServiceAccount (on a given + vCluster, or `passthrough` if omitted). + + This token can be used to authenticate against the Gateway API in the `sasl_plaintext` authentication mode. + More information on this case here : https://docs.conduktor.io/gateway/concepts/Clients/#sasl_plaintext + + ### Available operations + + * Generate a token + + ### Identity + + N/A. + +- name: cli_vclusters_gateway_v2_7 + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/Virtual%20Cluster/ + + ### Available operations + + * List virtual clusters + * Get a virtual cluster + * Upsert a virtual cluster + * Delete a virtual cluster + + ### Identity + + A virtual cluster is identified by the `name`. + + ### Schema + + + + + x-displayName: vclusters +- name: cli_alias-topics_gateway_v2_8 + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/Logical_topics/Alias%20topics/ + + ### Available operations + + * List alias topics + * Upsert an alias topic + * Delete an alias topic + + ### Identity + + An alias topic is identified by the `name` inside a `vcluster` (`passthrough` if omitted). + + ### Schema + + + + + x-displayName: alias-topics +- name: cli_concentrated-topics_gateway_v2_0 + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/Logical_topics/Concentration/ + + ### Available operations + + * List concentrated topics + + Concentrated topics are created when a concentration rule is applied. They are not created or deleted directly by the API. + + ### Identity + + A concentrated topic is identified by the `name` and the `vCluster` (`passthrough` if omitted). + + ### Schema + + + + + x-displayName: concentrated-topics +- name: cli_concentration-rules_gateway_v2_9 + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/Logical_topics/Concentration/ + + ### Available operations + + * List concentration rules + * Get a concentration rule + * Upsert a concentration rule + * Delete a concentration rule + + + ### Identity + + A concentration rule is identified by its name inside a vCluster. + + ### Schema + + + + + x-displayName: concentration-rules +- name: cli_interceptors_gateway_v2_12 + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/Interceptors-and-plugins/ + + ### Available operations + + * List the interceptors + * Get an interceptor + * Upsert an interceptor + * Delete an interceptor + + ### Identity + + An interceptor is identified by its `name` and its `scope`. + The `scope` is itself composed of 3 optional fields `vCluster`, `group`, `username`. + A __global__ interceptor is an interceptor whose `scope` has its fields empty. + + ### Schema + + + + + x-displayName: interceptors +- name: plugins + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/Interceptors-and-plugins/ + + ### Available operations + + * List the available plugins of the Gateway + + ### Identity + + A plugin is identified by its `pluginId`. + + The list of plugins is fixed for a given Gateway instance. + The list is fixed and cannot be modified. + + ### Schema + + + + +- name: cli_service-accounts_gateway_v2_11 + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/GatewayUser/ + + ### Available operations + + * List service accounts + * Upsert a service account + * Delete a service account + + ### Identity + + The service account is identified by the `name` and the `vClusterName`. + + The vCluster name is not mandatory, but if omitted, the `passthrough` vCluster will be used. + Thus, a service account is always associated with one and only one vCluster. + + ### Local and external service accounts + + A service account can be `Local` or `External`. + + * A `Local` service account is just a local user that allows to generate Gateway tokens for your Kafka client + applications (SASL). + * An `External` service account is a user that is authenticated by an external system (OIDC). In such a + scenario you will only need to create external service accounts in 2 cases : either to rename the OIDC principal + for you Kafka applications OR to gather service accounts into `Groups`. Gateway Tokens are not issued for external + service accounts since the authentication must be done by the external system. + + To create an external service account you must provide have an `principal` that is the name of the user in the + external system. + It can be equal or not to the service account `name` (up to you), but note that __the `principal` must be unique + accross all the vClusters__. + + ### Schema + + + + + x-displayName: service-accounts +- name: cli_gateway-groups_gateway_v2_10 + description: |+ + ### Definition + + Groups a defined by name a allow to regroup Gateway users in order to apply interceptors rules. + + ### Available operations + + * List groups + * Get a group + * Upsert a group + * Delete a group + + ### Identity + + The group is identified by its name (unique across all the vClusters). + + A group can be added external groups (coming from LDAP, OIDC claims etc.) which will allow the Gateway to bind them on the Gateway Group. + + ### Schema + + + + + x-displayName: gateway-groups +paths: + /gateway/v2/tokens: + post: + tags: + - tokens + description: |2+ + + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Create a new token for given service account and a given vcluster. + + If the vcluster is not provided, the token will be created for the passthrough vcluster. + + operationId: Generate a token for a service account on a vcluster + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TokenRequest' + example: + vClusterName: vcluster1 + username: user1 + lifeTimeSeconds: 3600 + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/TokenResponse' + example: + token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsIm5hbWUiOiJ1c2VyMSIsImlhdCI6MTUxNjIzOTAyMn0.1Q2JjNz + '400': + description: 'Invalid value for: body' + content: + text/plain: + schema: + type: string + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The searched entity was not found + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The searched entity was not found + '409': + description: Requesting a JWT token when no user pool service is configured + in the Gateway or requesting a token for an external service account. + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: Requesting a JWT token when no user pool service is configured + in the Gateway or requesting a token for an external service account. + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request POST \ + --url 'http://localhost:8888/gateway/v2/tokens' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{"vClusterName":"vcluster1","username":"user1","lifeTimeSeconds":3600000}' + /gateway/v2/vclusters: + get: + tags: + - cli_vclusters_gateway_v2_7 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the vclusters + + operationId: List the vclusters + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + uniqueItems: true + items: + $ref: '#/components/schemas/VCluster' + example: + - kind: VClusters + apiVersion: gateway/v2 + metadata: + name: vcluster1 + spec: + prefix: vcluster1 + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/vclusters' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' + put: + tags: + - cli_vclusters_gateway_v2_7 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Upsert a vcluster + + operationId: Upsert a vcluster + parameters: + - name: dryMode + in: query + description: If true, the operation will be simulated and no changes will + be made + required: false + schema: + default: false + type: boolean + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/VCluster' + example: + kind: VClusters + apiVersion: gateway/v2 + metadata: + name: vcluster1 + spec: + prefix: vcluster1 + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplyResult_VCluster' + example: + resource: + kind: VClusters + apiVersion: gateway/v2 + metadata: + name: vcluster1 + spec: + prefix: vcluster1 + upsertResult: Updated + '400': + description: Wrong format or usage of reserved keywords (e.g. passthrough) + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: Wrong format or usage of reserved keywords (e.g. passthrough) + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '409': + description: The given prefix is already used by another vcluster + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: The given prefix is already used by another vcluster + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request PUT \ + --url 'http://localhost:8888/gateway/v2/vclusters?dryMode=false' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "kind" : "VClusters", + "apiVersion" : "gateway/v2", + "metadata" : { + "name" : "vcluster1" + }, + "spec" : { + "prefix" : "vcluster1" + } + }' + /gateway/v2/vclusters/{vClusterName}: + get: + tags: + - cli_vclusters_gateway_v2_7 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Get a vcluster + + operationId: Get a vcluster + parameters: + - name: vClusterName + in: path + description: The name of the vcluster + required: true + schema: + type: string + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/VCluster' + example: + kind: VClusters + apiVersion: gateway/v2 + metadata: + name: vcluster1 + spec: + prefix: vcluster1 + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given vcluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given vcluster does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/vclusters/vcluster1' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' + delete: + tags: + - cli_vclusters_gateway_v2_7 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Delete a vcluster + + operationId: Delete a vcluster + parameters: + - name: vClusterName + in: path + description: The name of the vcluster + required: true + schema: + type: string + responses: + '204': + description: '' + '400': + description: Default passthrough vcluster cannot be deleted + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: Default passthrough vcluster cannot be deleted + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given vcluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given vcluster does not exist + '409': + description: The given vcluster has references (logical topics, interceptors, + concentration rules, service accounts) and cannot be deleted + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: The given vcluster has references (logical topics, interceptors, + concentration rules, service accounts) and cannot be deleted + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request DELETE \ + --url 'http://localhost:8888/gateway/v2/vclusters/vcluster1' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' + /gateway/v2/alias-topics: + get: + tags: + - cli_alias-topics_gateway_v2_8 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the alias topics of a vcluster + + operationId: List the alias topics + parameters: + - name: vcluster + in: query + description: The vCluster filter + required: false + schema: + type: string + - name: name + in: query + description: The name filter + required: false + schema: + type: string + - name: showDefaults + in: query + description: Whether to show default values or not + required: false + schema: + default: false + type: boolean + example: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/AliasTopic' + example: + - kind: AliasTopics + apiVersion: gateway/v2 + metadata: + name: name1 + vCluster: vCluster1 + spec: + physicalName: physicalName1 + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given vCluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given vCluster does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/alias-topics?showDefaults=false' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' + put: + tags: + - cli_alias-topics_gateway_v2_8 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Upsert an alias topic in a vcluster + + operationId: Upsert an alias topic + parameters: + - name: dryMode + in: query + description: Whether to simulate the operation or not + required: false + schema: + default: false + type: boolean + example: true + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AliasTopic' + example: + kind: AliasTopics + apiVersion: gateway/v2 + metadata: + name: name1 + vCluster: vCluster1 + spec: + physicalName: physicalName1 + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplyResult_AliasTopic' + example: + resource: + kind: AliasTopics + apiVersion: gateway/v2 + metadata: + name: name1 + vCluster: vCluster1 + spec: + physicalName: physicalName1 + upsertResult: Updated + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given vCluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given vCluster does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request PUT \ + --url 'http://localhost:8888/gateway/v2/alias-topics?dryMode=false' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "kind" : "AliasTopics", + "apiVersion" : "gateway/v2", + "metadata" : { + "name" : "name1", + "vCluster" : "vCluster1" + }, + "spec" : { + "physicalName" : "physicalName1" + } + }' + delete: + tags: + - cli_alias-topics_gateway_v2_8 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Delete an alias topic in a vcluster + + operationId: Delete an alias topic + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AliasTopicId' + required: true + responses: + '204': + description: '' + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given name does not exist in the given vCluster + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given name does not exist in the given vCluster + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request DELETE \ + --url 'http://localhost:8888/gateway/v2/alias-topics' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "name" : "name1", + "vCluster" : "vCluster1" + }' + /gateway/v2/concentrated-topics: + get: + tags: + - cli_concentrated-topics_gateway_v2_0 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the concentrated topics of a vcluster + + operationId: List the concentrated topics + parameters: + - name: vcluster + in: query + description: The vCluster filter + required: false + schema: + type: string + - name: name + in: query + description: The name filter + required: false + schema: + type: string + - name: showDefaults + in: query + description: Whether to show default values or not + required: false + schema: + default: false + type: boolean + example: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ConcentratedTopic' + example: + - kind: concentrated-topics + apiVersion: gateway/v2 + metadata: + name: name1 + vCluster: vCluster1 + spec: + physicalName: physicalName1 + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given vCluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given vCluster does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/concentrated-topics?showDefaults=false' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' + /gateway/v2/concentration-rules: + get: + tags: + - cli_concentration-rules_gateway_v2_9 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the concentration rules of a vcluster + + operationId: List the concentration rules + parameters: + - name: vcluster + in: query + description: The vCluster filter + required: false + schema: + type: string + - name: name + in: query + description: The name filter + required: false + schema: + type: string + - name: showDefaults + in: query + description: Whether to show default values or not + required: false + schema: + default: false + type: boolean + example: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ConcentrationRule' + example: + - kind: ConcentrationRules + apiVersion: gateway/v2 + metadata: + name: concentrationRule1 + vCluster: vCluster1 + spec: + logicalTopicNamePattern: topic.* + physicalTopicName: physicalTopicName + physicalTopicNameCompacted: physicalTopicNameCompacted + physicalTopicNameCompactedDeleted: physicalTopicNameCompactedDeleted + autoManaged: false + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given vCluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given vCluster does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/concentration-rules?showDefaults=false' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' + put: + tags: + - cli_concentration-rules_gateway_v2_9 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Upsert a concentration rule in a vcluster + + operationId: Upsert a concentration rule + parameters: + - name: dryMode + in: query + description: Whether to simulate the operation or not + required: false + schema: + default: false + type: boolean + example: true + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ConcentrationRule' + example: + kind: ConcentrationRules + apiVersion: gateway/v2 + metadata: + name: concentrationRule1 + vCluster: vCluster1 + spec: + logicalTopicNamePattern: topic.* + physicalTopicName: physicalTopicName + physicalTopicNameCompacted: physicalTopicNameCompacted + physicalTopicNameCompactedDeleted: physicalTopicNameCompactedDeleted + autoManaged: false + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplyResult_ConcentrationRule' + example: + resource: + kind: ConcentrationRules + apiVersion: gateway/v2 + metadata: + name: concentrationRule1 + vCluster: vCluster1 + spec: + logicalTopicNamePattern: topic.* + physicalTopicName: physicalTopicName + physicalTopicNameCompacted: physicalTopicNameCompacted + physicalTopicNameCompactedDeleted: physicalTopicNameCompactedDeleted + autoManaged: false + upsertResult: Created + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given vCluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given vCluster does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request PUT \ + --url 'http://localhost:8888/gateway/v2/concentration-rules?dryMode=false' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "kind" : "ConcentrationRules", + "apiVersion" : "gateway/v2", + "metadata" : { + "name" : "concentrationRule1", + "vCluster" : "vCluster1" + }, + "spec" : { + "logicalTopicNamePattern" : "topic.*", + "physicalTopicName" : "physicalTopicName", + "physicalTopicNameCompacted" : "physicalTopicNameCompacted", + "physicalTopicNameCompactedDeleted" : "physicalTopicNameCompactedDeleted", + "autoManaged" : false + } + }' + delete: + tags: + - cli_concentration-rules_gateway_v2_9 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Delete a concentration rule in a vcluster + + operationId: Delete a concentration rule + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ConcentrationRuleId' + example: + name: concentrationRule1 + vCluster: vCluster1 + required: true + responses: + '204': + description: '' + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given vCluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given vCluster does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request DELETE \ + --url 'http://localhost:8888/gateway/v2/concentration-rules' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "name" : "concentrationRule1", + "vCluster" : "vCluster1" + }' + /gateway/v2/interceptors: + get: + tags: + - cli_interceptors_gateway_v2_12 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the interceptors + + operationId: List the interceptors + parameters: + - name: name + in: query + description: Filter by name + required: false + schema: + type: string + - name: global + in: query + description: Keep only global interceptors + required: false + schema: + type: boolean + - name: vcluster + in: query + description: Filter by vCluster + required: false + schema: + type: string + - name: group + in: query + description: Filter by group + required: false + schema: + type: string + - name: username + in: query + description: Filter by service-account + required: false + schema: + type: string + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + uniqueItems: true + items: + $ref: '#/components/schemas/Interceptor' + example: + - kind: interceptors + apiVersion: gateway/v2 + metadata: + name: yellow_cars_filter + scope: + vCluster: vCluster1 + spec: + comment: Filter yellow cars + pluginClass: io.conduktor.gateway.interceptor.VirtualSqlTopicPlugin + priority: 1 + config: + virtualTopic: yellow_cars + statement: SELECT '$.type' as type, '$.price' as price FROM cars + WHERE '$.color' = 'yellow' + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/interceptors?name=interceptor-name&global=true&vcluster=passthrough&group=group1&username=user1' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' + put: + tags: + - cli_interceptors_gateway_v2_12 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Upsert an interceptor + + operationId: Upsert an interceptor + parameters: + - name: dryMode + in: query + description: Whether to simulate the operation or not + required: false + schema: + default: false + type: boolean + example: true + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Interceptor' + example: + kind: interceptors + apiVersion: gateway/v2 + metadata: + name: yellow_cars_filter + scope: + vCluster: vCluster1 + spec: + comment: Filter yellow cars + pluginClass: io.conduktor.gateway.interceptor.VirtualSqlTopicPlugin + priority: 1 + config: + virtualTopic: yellow_cars + statement: SELECT '$.type' as type, '$.price' as price FROM cars + WHERE '$.color' = 'yellow' + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplyResult_Interceptor' + example: + resource: + kind: interceptors + apiVersion: gateway/v2 + metadata: + name: yellow_cars_filter + scope: + vCluster: vCluster1 + spec: + comment: Filter yellow cars + pluginClass: io.conduktor.gateway.interceptor.VirtualSqlTopicPlugin + priority: 1 + config: + virtualTopic: yellow_cars + statement: SELECT '$.type' as type, '$.price' as price FROM + cars WHERE '$.color' = 'yellow' + upsertResult: Created + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The vCluster or the group specified in the scope does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The vCluster or the group specified in the scope does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request PUT \ + --url 'http://localhost:8888/gateway/v2/interceptors?dryMode=false' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "kind" : "interceptors", + "apiVersion" : "gateway/v2", + "metadata" : { + "name" : "yellow_cars_filter", + "scope" : { + "vCluster" : "vCluster1", + "group" : null, + "username" : null + } + }, + "spec" : { + "comment" : "Filter yellow cars", + "pluginClass" : "io.conduktor.gateway.interceptor.VirtualSqlTopicPlugin", + "priority" : 1, + "config" : { + "virtualTopic" : "yellow_cars", + "statement" : "SELECT \'$.type\' as type, \'$.price\' as price FROM cars WHERE \'$.color\' = \'yellow\'" + } + } + }' + /gateway/v2/interceptors/{name}: + delete: + tags: + - cli_interceptors_gateway_v2_12 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Delete an interceptor + + operationId: Delete an interceptor + parameters: + - name: name + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InterceptorScope' + required: true + responses: + '204': + description: '' + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given interceptor does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given interceptor does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request DELETE \ + --url 'http://localhost:8888/gateway/v2/interceptors/yellow_cars_filter' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "vCluster" : "vCluster1", + "group" : null, + "username" : null + }' + /gateway/v2/interceptors/resolve: + post: + tags: + - cli_interceptors_gateway_v2_12 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + A utility endpoint to resolve the interceptors for a given vCluster, groups and username. + Helps to understand which interceptors will be applied for a given request. + + operationId: Resolve interceptors + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InterceptorResolverRequest' + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + uniqueItems: true + items: + $ref: '#/components/schemas/Interceptor' + example: + - kind: interceptors + apiVersion: gateway/v2 + metadata: + name: yellow_cars_filter + scope: + vCluster: vCluster1 + spec: + comment: Filter yellow cars + pluginClass: io.conduktor.gateway.interceptor.VirtualSqlTopicPlugin + priority: 1 + config: + virtualTopic: yellow_cars + statement: SELECT '$.type' as type, '$.price' as price FROM cars + WHERE '$.color' = 'yellow' + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request POST \ + --url 'http://localhost:8888/gateway/v2/interceptors/resolve' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "vCluster" : "passthrough", + "groups" : [ + "group1", + "group2" + ], + "username" : "user1" + }' + /gateway/v2/plugins: + get: + tags: + - plugins + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the available plugins of the gateway + + operationId: List the Plugins of the Gateway + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + uniqueItems: true + items: + $ref: '#/components/schemas/Plugin' + example: + - plugin: io.conduktor.gateway.interceptor.chaos.SimulateSlowProducersConsumersPlugin + pluginId: io.conduktor.gateway.interceptor.chaos.SimulateSlowProducersConsumersPlugin + readme: |2+ + + --- + version: ${project.version} + title: Simulate slow producers and consumers + description: Validate your application behaves correctly when there are delays in responses from the Kafka cluster. + parent: console + license: enterprise + --- + + ## Introduction + + This interceptor slows responses from the brokers. + + It will operate only on a set of topics rather than all traffic. + + This interceptor only works on Produce requests and Fetch requests. + + ## Configuration + + | key | type | default | description | + |:--------------|:--------|:--------|:----------------------------------------------------------------| + | topic | String | `.*` | Topics that match this regex will have the interceptor applied. | + | rateInPercent | int | | The percentage of requests that will apply this interceptor | + | minLatencyMs | int | | Minimum for the random response latency in milliseconds | + | maxLatencyMs | int | | Maximum for the random response latency in milliseconds | + + ## Example + + ```json + { + "name": "mySimulateSlowProducersConsumersInterceptor", + "pluginClass": "io.conduktor.gateway.interceptor.chaos.SimulateSlowProducersConsumersPlugin", + "priority": 100, + "config": { + "rateInPercent": 100, + "minLatencyMs": 50, + "maxLatencyMs": 1200 + } + } + ``` + + parent: Console + license: enterprise + description: Validate your application behaves correctly when broker + errors occur. + title: Broker errors + version: 3.0.1 + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/plugins' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' + /gateway/v2/service-accounts: + get: + tags: + - cli_service-accounts_gateway_v2_11 + description: |2+ + + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the service accounts + + operationId: List the service accounts + parameters: + - name: vcluster + in: query + description: Filter by vCluster + required: false + schema: + type: string + - name: name + in: query + description: Filter by name + required: false + schema: + type: string + - name: type + in: query + description: Filter by type (External or Local) + required: false + schema: + type: string + - name: showDefaults + in: query + description: Whether to show default values or not + required: false + schema: + default: false + type: boolean + example: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ServiceAccount' + example: + - kind: service-accounts + apiVersion: gateway/v2 + metadata: + name: user1 + vCluster: vcluster1 + spec: + type: External + principal: aliasUser1 + - kind: service-accounts + apiVersion: gateway/v2 + metadata: + name: user1 + vCluster: vcluster1 + spec: + type: Local + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/service-accounts?vcluster=vCluster1&name=user1&type=External&showDefaults=false' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' + put: + tags: + - cli_service-accounts_gateway_v2_11 + description: |2+ + + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Upsert a service account + + operationId: Upsert a service account + parameters: + - name: dryMode + in: query + description: Whether to simulate the operation or not + required: false + schema: + default: false + type: boolean + example: true + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceAccount' + example: + kind: service-accounts + apiVersion: gateway/v2 + metadata: + name: user1 + vCluster: vcluster1 + spec: + type: External + principal: aliasUser1 + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplyResult_ServiceAccount' + example: + resource: + kind: service-accounts + apiVersion: gateway/v2 + metadata: + name: user1 + vCluster: vcluster1 + spec: + type: External + principal: aliasUser1 + upsertResult: Created + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given service account references a non-existing vCluster + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given service account references a non-existing vCluster + '409': + description: The service account already exist + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: The service account already exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request PUT \ + --url 'http://localhost:8888/gateway/v2/service-accounts?dryMode=false' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "kind" : "service-accounts", + "apiVersion" : "gateway/v2", + "metadata" : { + "name" : "user1", + "vCluster" : "vcluster1" + }, + "spec" : { + "type" : "External", + "principal" : "aliasUser1" + } + }' + delete: + tags: + - cli_service-accounts_gateway_v2_11 + description: |2+ + + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Delete a service account + + operationId: Delete a service account + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceAccountId' + required: true + responses: + '204': + description: '' + '400': + description: 'Invalid value for: body' + content: + text/plain: + schema: + type: string + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given service account does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given service account does not exist + '409': + description: The service account is still used by groups + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: The service account is still used by groups + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request DELETE \ + --url 'http://localhost:8888/gateway/v2/service-accounts' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "name" : "user1" + }' + /gateway/v2/gateway-groups: + get: + tags: + - cli_gateway-groups_gateway_v2_10 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the groups + + operationId: List the groups + parameters: + - name: showDefaults + in: query + description: Whether to show default values or not + required: false + schema: + default: false + type: boolean + example: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/GatewayGroup' + example: + - kind: GatewayGroups + apiVersion: gateway/v2 + metadata: + name: group1 + spec: + members: + - vCluster: vCluster1 + name: serviceAccount1 + - vCluster: vCluster2 + name: serviceAccount2 + - vCluster: vCluster3 + name: serviceAccount3 + externalGroups: + - GROUP_READER + - GROUP_WRITER + '400': + description: 'Invalid value for: query parameter showDefaults' + content: + text/plain: + schema: + type: string + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/gateway-groups?showDefaults=false' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' + put: + tags: + - cli_gateway-groups_gateway_v2_10 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Upsert a group + + operationId: Upsert a group + parameters: + - name: dryMode + in: query + description: Whether to simulate the operation or not + required: false + schema: + default: false + type: boolean + example: true + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayGroup' + example: + kind: GatewayGroups + apiVersion: gateway/v2 + metadata: + name: group1 + spec: + members: + - vCluster: vCluster1 + name: serviceAccount1 + - vCluster: vCluster2 + name: serviceAccount2 + - vCluster: vCluster3 + name: serviceAccount3 + externalGroups: + - GROUP_READER + - GROUP_WRITER + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplyResult_GatewayGroup' + example: + resource: + kind: GatewayGroups + apiVersion: gateway/v2 + metadata: + name: group1 + spec: + members: + - vCluster: vCluster1 + name: serviceAccount1 + - vCluster: vCluster2 + name: serviceAccount2 + - vCluster: vCluster3 + name: serviceAccount3 + externalGroups: + - GROUP_READER + - GROUP_WRITER + upsertResult: Updated + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The group contains a service account that does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The group contains a service account that does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request PUT \ + --url 'http://localhost:8888/gateway/v2/gateway-groups?dryMode=false' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "kind" : "GatewayGroups", + "apiVersion" : "gateway/v2", + "metadata" : { + "name" : "group1" + }, + "spec" : { + "members" : [ + { + "vCluster" : "vCluster1", + "name" : "serviceAccount1" + }, + { + "vCluster" : "vCluster2", + "name" : "serviceAccount2" + }, + { + "vCluster" : "vCluster3", + "name" : "serviceAccount3" + } + ], + "externalGroups" : [ + "GROUP_READER", + "GROUP_WRITER" + ] + } + }' + /gateway/v2/gateway-groups/{name}: + get: + tags: + - cli_gateway-groups_gateway_v2_10 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Get a group + + operationId: Get a group + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: showDefaults + in: query + description: Whether to show default values or not + required: false + schema: + default: false + type: boolean + example: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayGroup' + example: + kind: GatewayGroups + apiVersion: gateway/v2 + metadata: + name: group1 + spec: + members: + - vCluster: vCluster1 + name: serviceAccount1 + - vCluster: vCluster2 + name: serviceAccount2 + - vCluster: vCluster3 + name: serviceAccount3 + externalGroups: + - GROUP_READER + - GROUP_WRITER + '400': + description: 'Invalid value for: query parameter showDefaults' + content: + text/plain: + schema: + type: string + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given group does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given group does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/gateway-groups/group1?showDefaults=false' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' + delete: + tags: + - cli_gateway-groups_gateway_v2_10 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Delete a group + + operationId: Delete a group + parameters: + - name: name + in: path + required: true + schema: + type: string + responses: + '204': + description: '' + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given group does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given group does not exist + '409': + description: The group is still referenced by interceptors + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: The group is still referenced by interceptors + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request DELETE \ + --url 'http://localhost:8888/gateway/v2/gateway-groups/group1' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' +components: + schemas: + AliasTopic: + title: AliasTopic + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the alias topic + type: string + apiVersion: + description: The api version of the alias topic + type: string + metadata: + $ref: '#/components/schemas/AliasTopicMetadata' + spec: + $ref: '#/components/schemas/AliasTopicSpec' + AliasTopicId: + title: AliasTopicId + type: object + required: + - name + properties: + name: + description: The name of the alias topic + type: string + vCluster: + description: 'The vCluster name of the alias topic (default: passthrough)' + type: string + AliasTopicMetadata: + title: AliasTopicMetadata + description: The metadata of the alias topic + type: object + required: + - name + properties: + name: + description: The name of the alias topic + type: string + format: ^[a-zA-Z0-9._-]+$ + vCluster: + description: 'The vCluster name of the alias topic (default: passthrough)' + type: string + format: ^[a-zA-Z0-9_-]+$ + AliasTopicSpec: + title: AliasTopicSpec + description: The specification of the alias topic + type: object + required: + - physicalName + properties: + physicalName: + description: The physical name of the alias topic + type: string + format: ^[a-zA-Z0-9._-]+$ + ApplyResult_AliasTopic: + title: ApplyResult_AliasTopic + type: object + required: + - resource + - upsertResult + properties: + resource: + $ref: '#/components/schemas/AliasTopic' + description: The resource that was upserted + upsertResult: + $ref: '#/components/schemas/UpsertResult' + ApplyResult_ConcentrationRule: + title: ApplyResult_ConcentrationRule + type: object + required: + - resource + - upsertResult + properties: + resource: + $ref: '#/components/schemas/ConcentrationRule' + description: The resource that was upserted + upsertResult: + $ref: '#/components/schemas/UpsertResult' + ApplyResult_GatewayGroup: + title: ApplyResult_GatewayGroup + type: object + required: + - resource + - upsertResult + properties: + resource: + $ref: '#/components/schemas/GatewayGroup' + description: The resource that was upserted + upsertResult: + $ref: '#/components/schemas/UpsertResult' + ApplyResult_Interceptor: + title: ApplyResult_Interceptor + type: object + required: + - resource + - upsertResult + properties: + resource: + $ref: '#/components/schemas/Interceptor' + description: The resource that was upserted + upsertResult: + $ref: '#/components/schemas/UpsertResult' + ApplyResult_ServiceAccount: + title: ApplyResult_ServiceAccount + type: object + required: + - resource + - upsertResult + properties: + resource: + $ref: '#/components/schemas/ServiceAccount' + description: The resource that was upserted + upsertResult: + $ref: '#/components/schemas/UpsertResult' + ApplyResult_VCluster: + title: ApplyResult_VCluster + type: object + required: + - resource + - upsertResult + properties: + resource: + $ref: '#/components/schemas/VCluster' + description: The resource that was upserted + upsertResult: + $ref: '#/components/schemas/UpsertResult' + BadRequest: + title: BadRequest + type: object + required: + - title + properties: + title: + type: string + msg: + type: string + cause: + type: string + ConcentratedTopic: + title: ConcentratedTopic + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the concentrated topic + type: string + apiVersion: + description: The api version of the concentrated topic + type: string + metadata: + $ref: '#/components/schemas/ConcentratedTopicMetadata' + spec: + $ref: '#/components/schemas/ConcentratedTopicSpec' + ConcentratedTopicMetadata: + title: ConcentratedTopicMetadata + description: The metadata of the concentrated topic + type: object + required: + - name + properties: + name: + description: The name of the concentrated topic + type: string + vCluster: + description: The vCluster of the concentrated topic. If not provided, defaulted + to passthrough vCluster. + type: string + ConcentratedTopicSpec: + title: ConcentratedTopicSpec + description: The specification of the concentrated topic + type: object + required: + - physicalName + properties: + physicalName: + description: The physical name of the concentrated topic + type: string + ConcentrationRule: + title: ConcentrationRule + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the concentration rule + type: string + apiVersion: + description: The api version of the concentration rule + type: string + metadata: + $ref: '#/components/schemas/ConcentrationRuleMetadata' + spec: + $ref: '#/components/schemas/ConcentrationRuleSpec' + ConcentrationRuleId: + title: ConcentrationRuleId + type: object + required: + - name + properties: + name: + description: The name of the concentration rule (identifier in a vCluster) + type: string + vCluster: + description: The vCluster of the concentration rule. Default to passthrough + if not provided. + type: string + ConcentrationRuleMetadata: + title: ConcentrationRuleMetadata + description: The metadata of the concentration rule + type: object + required: + - name + properties: + name: + description: The name of the concentration rule (identifier in a vCluster) + type: string + format: ^[a-zA-Z0-9._-]+$ + vCluster: + description: The vCluster of the concentration rule. Default to passthrough + if not provided. + type: string + ConcentrationRuleSpec: + title: ConcentrationRuleSpec + description: The specification of the concentration rule + type: object + required: + - logicalTopicNamePattern + - physicalTopicName + properties: + logicalTopicNamePattern: + description: The pattern of the concentration rule + type: string + physicalTopicName: + description: The physical name of the concentrated topic + type: string + format: ^[a-zA-Z0-9._-]+$ + physicalTopicNameCompacted: + description: The physical name of the topic for compact policy + type: string + format: ^[a-zA-Z0-9._-]+$ + physicalTopicNameCompactedDeleted: + description: The pattern of the topic for delete, compact policy + type: string + format: ^[a-zA-Z0-9._-]+$ + autoManaged: + description: Whether the concentration rule is auto managed + type: boolean + Conflict: + title: Conflict + type: object + required: + - title + properties: + title: + type: string + msg: + type: string + cause: + type: string + Created: + title: Created + type: object + External: + title: External + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the service account + type: string + apiVersion: + description: The api version of the service account + type: string + metadata: + $ref: '#/components/schemas/ExternalMetadata' + spec: + $ref: '#/components/schemas/ExternalSpec' + ExternalMetadata: + title: ExternalMetadata + description: Metadata of the service account + type: object + required: + - name + properties: + name: + description: The name of the service account (identifier) + type: string + format: ^[a-zA-Z0-9_-]{3,64}$ + vCluster: + description: |2 + + The name of the vcluster the service account belongs to. + + If not provided, the service account will be created in the default `passthrough` vcluster. + type: string + format: ^[a-zA-Z0-9_-]+$ + ExternalSpec: + title: ExternalSpec + description: Spec of the service account + type: object + required: + - principal + properties: + principal: + description: |2 + + An optional field to override the principal of the service account from an external user DB (OIDC) + type: string + format: ^[a-zA-Z0-9_-]{3,64}$ + GatewayGroup: + title: GatewayGroup + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the group + type: string + apiVersion: + description: The api version of the group + type: string + metadata: + $ref: '#/components/schemas/GroupMetadata' + spec: + $ref: '#/components/schemas/GroupSpec' + GroupMetadata: + title: GroupMetadata + description: The metadata of the group + type: object + required: + - name + properties: + name: + description: The name of the group + type: string + format: ^[a-zA-Z0-9_-]{1,100}$ + GroupSpec: + title: GroupSpec + description: The specification of the group + type: object + properties: + members: + description: The service accounts belonging to the group + type: array + uniqueItems: true + items: + $ref: '#/components/schemas/ServiceAccountId' + externalGroups: + description: The external groups (LDAP, OIDC...) mapped on the group + type: array + uniqueItems: true + items: + type: string + Interceptor: + title: Interceptor + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the interceptor + type: string + apiVersion: + description: The api version of the interceptor + type: string + metadata: + $ref: '#/components/schemas/InterceptorMetadata' + spec: + $ref: '#/components/schemas/InterceptorSpec' + InterceptorMetadata: + title: InterceptorMetadata + description: Metadata of the interceptor + type: object + required: + - name + properties: + name: + description: The name of the interceptor + type: string + scope: + $ref: '#/components/schemas/InterceptorScope' + description: |2 + + The scope of the interceptor. + It can be applied to a specific vCluster or group or username. + If none of them is set, it will be applied Globally to the gateway. + InterceptorResolverRequest: + title: InterceptorResolverRequest + type: object + properties: + vCluster: + description: The vCluster to test the interceptors resolution + type: string + groups: + description: The groups to test the interceptors resolution + type: array + items: + type: string + username: + description: The username to test the interceptors resolution + type: string + InterceptorScope: + title: InterceptorScope + type: object + properties: + vCluster: + description: An optional vCluster to filter the interceptors + type: string + group: + description: An optional group to filter the interceptors + type: string + username: + description: An optional username to filter the interceptors + type: string + InterceptorSpec: + title: InterceptorSpec + description: Spec of the interceptor + type: object + required: + - pluginClass + - priority + - config + properties: + comment: + description: An optional comment for the interceptor + type: string + pluginClass: + description: The class of the plugin + type: string + priority: + description: The priority of the interceptor + type: integer + format: int32 + config: + $ref: '#/components/schemas/Map_Json' + Local: + title: Local + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the service account + type: string + apiVersion: + description: The api version of the service account + type: string + metadata: + $ref: '#/components/schemas/LocalMetadata' + spec: + $ref: '#/components/schemas/LocalSpec' + LocalMetadata: + title: LocalMetadata + description: Metadata of the service account + type: object + required: + - name + properties: + name: + description: The name of the service account (identifier) + type: string + format: ^[a-zA-Z0-9_-]{3,64}$ + vCluster: + description: |2 + + The name of the vcluster the service account belongs to. + + If not provided, the service account will be created in the default `passthrough` vcluster. + type: string + format: ^[a-zA-Z0-9_-]+$ + LocalSpec: + title: LocalSpec + description: Spec of the service account + type: object + Map_Json: + title: Map_Json + description: The configuration of the interceptor + type: object + additionalProperties: {} + NotChanged: + title: NotChanged + type: object + NotFound: + title: NotFound + type: object + required: + - title + properties: + title: + type: string + msg: + type: string + cause: + type: string + Plugin: + title: Plugin + type: object + required: + - plugin + - pluginId + - readme + properties: + plugin: + description: The name of the plugin + type: string + pluginId: + description: The id of the plugin + type: string + readme: + description: The readme of the plugin + type: string + parent: + description: The parent of the plugin + type: string + license: + description: The license of the plugin + type: string + description: + description: The description of the plugin + type: string + title: + description: The title of the plugin + type: string + version: + description: The version of the plugin + type: string + ServerError: + title: ServerError + type: object + required: + - title + properties: + title: + type: string + msg: + type: string + cause: + type: string + ServiceAccount: + title: ServiceAccount + oneOf: + - $ref: '#/components/schemas/External' + - $ref: '#/components/schemas/Local' + ServiceAccountId: + title: ServiceAccountId + type: object + required: + - name + properties: + vCluster: + description: |2 + + The name of the vcluster the service account belongs to. + + If not provided, the service account will be created in the default `passthrough` vcluster. + type: string + name: + description: The name of the service account (identifier) + type: string + TokenRequest: + title: TokenRequest + type: object + required: + - username + - lifeTimeSeconds + properties: + vClusterName: + description: 'The name of the vcluster to create the token for. "passthrough + if omitted" ' + type: string + username: + description: The username of the local service account to create the token + for. + type: string + lifeTimeSeconds: + description: The life time of the token in milliseconds. + type: integer + format: int64 + TokenResponse: + title: TokenResponse + type: object + required: + - token + properties: + token: + description: The token created for the given username on the given vcluster. + type: string + Unauthorized: + title: Unauthorized + type: object + required: + - title + properties: + title: + type: string + msg: + type: string + cause: + type: string + Updated: + title: Updated + type: object + UpsertResult: + title: UpsertResult + description: The result of the upsert operation (created, updated, not changed) + oneOf: + - $ref: '#/components/schemas/Created' + - $ref: '#/components/schemas/NotChanged' + - $ref: '#/components/schemas/Updated' + VCluster: + title: VCluster + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the vCluster + type: string + apiVersion: + description: The api version of the vCluster + type: string + metadata: + $ref: '#/components/schemas/VClusterMetadata' + spec: + $ref: '#/components/schemas/VClusterSpec' + VClusterMetadata: + title: VClusterMetadata + description: Metadata of the vCluster + type: object + required: + - name + properties: + name: + description: The name of the vcluster + type: string + format: ^[a-zA-Z0-9_-]+$ + VClusterSpec: + title: VClusterSpec + description: Spec of the vCluster + type: object + required: + - prefix + properties: + prefix: + description: The prefix that will be used to namespace kafka resources in + the physical cluster + type: string + format: ^[a-zA-Z0-9_-]*$ + securitySchemes: + httpAuth: + type: http + scheme: basic +x-tagGroups: +- name: 🐺 Conduktor Gateway API + tags: + - Introduction + - Authentication + - Kinds + - Api Groups + - Versioning + - Conventions +- name: 🌉 gateway/v2 + tags: + - cli_vclusters_gateway_v2_7 + - cli_alias-topics_gateway_v2_8 + - cli_concentrated-topics_gateway_v2_0 + - cli_concentration-rules_gateway_v2_9 + - cli_interceptors_gateway_v2_12 + - plugins + - cli_service-accounts_gateway_v2_11 + - cli_gateway-groups_gateway_v2_10 + - tokens diff --git a/schema/gateway_schema_test.go b/schema/gateway_schema_test.go new file mode 100644 index 0000000..7ccb668 --- /dev/null +++ b/schema/gateway_schema_test.go @@ -0,0 +1,94 @@ +package schema + +import ( + "os" + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" +) + +func TestGetKindWithYamlFromGateway(t *testing.T) { + t.Run("gets kinds from schema", func(t *testing.T) { + schemaContent, err := os.ReadFile("gateway.yaml") + if err != nil { + t.Fatalf("failed reading file: %s", err) + } + + schema, err := New(schemaContent) + if err != nil { + t.Fatalf("failed creating new schema: %s", err) + } + + kinds, err := schema.GetGatewayKinds(true) + if err != nil { + t.Fatalf("failed getting kinds: %s", err) + } + + expected := KindCatalog{ + "Vclusters": { + Versions: map[int]KindVersion{ + 2: &GatewayKindVersion{ + Name: "Vclusters", + ListPath: "/gateway/v2/vclusters", + ParentPathParam: []string{}, + Order: 7, + }, + }, + }, + "AliasTopics": { + Versions: map[int]KindVersion{ + 2: &GatewayKindVersion{ + Name: "AliasTopics", + ListPath: "/gateway/v2/alias-topics", + ParentPathParam: []string{}, + Order: 8, + }, + }, + }, + "ConcentrationRules": { + Versions: map[int]KindVersion{ + 2: &GatewayKindVersion{ + Name: "ConcentrationRules", + ListPath: "/gateway/v2/concentration-rules", + ParentPathParam: []string{}, + Order: 9, + }, + }, + }, + "GatewayGroups": { + Versions: map[int]KindVersion{ + 2: &GatewayKindVersion{ + Name: "GatewayGroups", + ListPath: "/gateway/v2/gateway-groups", + ParentPathParam: []string{}, + Order: 10, + }, + }, + }, + "ServiceAccounts": { + Versions: map[int]KindVersion{ + 2: &GatewayKindVersion{ + Name: "ServiceAccounts", + ListPath: "/gateway/v2/service-accounts", + ParentPathParam: []string{}, + Order: 11, + }, + }, + }, + "Interceptors": { + Versions: map[int]KindVersion{ + 2: &GatewayKindVersion{ + Name: "Interceptors", + ListPath: "/gateway/v2/interceptors", + ParentPathParam: []string{}, + Order: 12, + }, + }, + }, + } + if !reflect.DeepEqual(kinds, expected) { + t.Error(spew.Printf("got kinds %v, want %v", kinds, expected)) + } + }) +} diff --git a/schema/kind.go b/schema/kind.go index 9c30c8b..f2c080d 100644 --- a/schema/kind.go +++ b/schema/kind.go @@ -12,13 +12,59 @@ import ( "github.com/conduktor/ctl/resource" ) -type KindVersion struct { +type KindVersion interface { + GetListPath() string + GetName() string + GetParentPathParam() []string + GetOrder() int +} + +type ConsoleKindVersion struct { ListPath string Name string ParentPathParam []string Order int `json:1000` //same value DefaultPriority } +func (c *ConsoleKindVersion) GetListPath() string { + return c.ListPath +} + +func (c *ConsoleKindVersion) GetName() string { + return c.Name +} + +func (c *ConsoleKindVersion) GetParentPathParam() []string { + return c.ParentPathParam +} + +func (c *ConsoleKindVersion) GetOrder() int { + return c.Order +} + +type GatewayKindVersion struct { + ListPath string + Name string + ParentPathParam []string + Order int `json:1000` //same value DefaultPriority +} + +func (c *GatewayKindVersion) GetListPath() string { + return c.ListPath +} + +func (c *GatewayKindVersion) GetName() string { + return c.Name +} + +func (c *GatewayKindVersion) GetParentPathParam() []string { + return c.ParentPathParam +} + +func (c *GatewayKindVersion) GetOrder() int { + return c.Order +} + const DefaultPriority = 1000 //update json annotation for Order when changing this value type Kind struct { @@ -33,26 +79,40 @@ var consoleDefaultByteSchema []byte //go:embed gateway-default-schema.json var gatewayDefaultByteSchema []byte -func buildKindCatalogFromByteSchema(byteSchema []byte) KindCatalog { - var result KindCatalog - err := json.Unmarshal(byteSchema, &result) +type KindGeneric[T KindVersion] struct { + Versions map[int]T +} + +func buildKindCatalogFromByteSchema[T KindVersion](byteSchema []byte) KindCatalog { + var jsonResult map[string]KindGeneric[T] + err := json.Unmarshal(byteSchema, &jsonResult) if err != nil { panic(err) } + var result KindCatalog = make(map[string]Kind) + for kindName, kindGeneric := range jsonResult { + kind := Kind{ + Versions: make(map[int]KindVersion), + } + for version, kindVersion := range kindGeneric.Versions { + kind.Versions[version] = kindVersion + } + result[kindName] = kind + } return result } func ConsoleDefaultKind() KindCatalog { - return buildKindCatalogFromByteSchema(consoleDefaultByteSchema) + return buildKindCatalogFromByteSchema[*ConsoleKindVersion](consoleDefaultByteSchema) } func GatewayDefaultKind() KindCatalog { - return buildKindCatalogFromByteSchema(gatewayDefaultByteSchema) + return buildKindCatalogFromByteSchema[*GatewayKindVersion](gatewayDefaultByteSchema) } -func NewKind(version int, kindVersion *KindVersion) Kind { +func NewKind(version int, kindVersion KindVersion) Kind { return Kind{ - Versions: map[int]KindVersion{version: *kindVersion}, + Versions: map[int]KindVersion{version: kindVersion}, } } @@ -77,19 +137,19 @@ func extractVersionFromApiVersion(apiVersion string) int { return version } -func (kind *Kind) AddVersion(version int, kindVersion *KindVersion) error { +func (kind *Kind) AddVersion(version int, kindVersion KindVersion) error { name := kind.GetName() - if name != kindVersion.Name { - return fmt.Errorf("Adding kind version of kind %s to different kind %s", kindVersion.Name, name) + if name != kindVersion.GetName() { + return fmt.Errorf("Adding kind version of kind %s to different kind %s", kindVersion.GetName(), name) } - kind.Versions[version] = *kindVersion + kind.Versions[version] = kindVersion return nil } func (kind *Kind) GetFlag() []string { kindVersion := kind.GetLatestKindVersion() - result := make([]string, len(kindVersion.ParentPathParam)) - copy(result, kindVersion.ParentPathParam) + result := make([]string, len(kindVersion.GetParentPathParam())) + copy(result, kindVersion.GetParentPathParam()) return result } @@ -103,29 +163,29 @@ func (kind *Kind) MaxVersion() int { return maxVersion } -func (kind *Kind) GetLatestKindVersion() *KindVersion { +func (kind *Kind) GetLatestKindVersion() KindVersion { kindVersion, ok := kind.Versions[kind.MaxVersion()] if !ok { panic("Max numVersion on kind return a numVersion that does not exist") } - return &kindVersion + return kindVersion } func (Kind *Kind) GetName() string { for _, kindVersion := range Kind.Versions { - return kindVersion.Name + return kindVersion.GetName() } panic("No kindVersion in kind") //should never happen } func (kind *Kind) ListPath(parentPathValues []string) string { kindVersion := kind.GetLatestKindVersion() - if len(parentPathValues) != len(kindVersion.ParentPathParam) { - panic(fmt.Sprintf("For kind %s expected %d parent apiVersion values, got %d", kindVersion.Name, len(kindVersion.ParentPathParam), len(parentPathValues))) + if len(parentPathValues) != len(kindVersion.GetParentPathParam()) { + panic(fmt.Sprintf("For kind %s expected %d parent apiVersion values, got %d", kindVersion.GetName(), len(kindVersion.GetParentPathParam()), len(parentPathValues))) } - path := kindVersion.ListPath + path := kindVersion.GetListPath() for i, pathValue := range parentPathValues { - path = strings.Replace(path, fmt.Sprintf("{%s}", kindVersion.ParentPathParam[i]), pathValue, 1) + path = strings.Replace(path, fmt.Sprintf("{%s}", kindVersion.GetParentPathParam()[i]), pathValue, 1) } return path } @@ -139,9 +199,9 @@ func (kind *Kind) ApplyPath(resource *resource.Resource) (string, error) { if !ok { return "", fmt.Errorf("Could not find version %s for kind %s", resource.Version, resource.Kind) } - parentPathValues := make([]string, len(kindVersion.ParentPathParam)) + parentPathValues := make([]string, len(kindVersion.GetParentPathParam())) var err error - for i, param := range kindVersion.ParentPathParam { + for i, param := range kindVersion.GetParentPathParam() { parentPathValues[i], err = resource.StringFromMetadata(param) if err != nil { return "", err diff --git a/schema/kind_test.go b/schema/kind_test.go index 19820f2..723f9e0 100644 --- a/schema/kind_test.go +++ b/schema/kind_test.go @@ -9,7 +9,7 @@ func TestKindGetFlag(t *testing.T) { kind := Kind{ Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ ParentPathParam: []string{"param-1", "param-2", "param-3"}, }, }, @@ -34,7 +34,7 @@ func TestKindGetFlagWhenNoFlag(t *testing.T) { t.Run("converts parent parameters to flags", func(t *testing.T) { kind := Kind{ Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ ParentPathParam: []string{}, }, }, @@ -52,7 +52,7 @@ func TestKindListPath(t *testing.T) { t.Run("replaces parent parameters in ListPath", func(t *testing.T) { kind := Kind{ Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ ListPath: "/ListPath/{param-1}/{param-2}", ParentPathParam: []string{"param-1", "param-2"}, }, @@ -70,7 +70,7 @@ func TestKindListPath(t *testing.T) { t.Run("panics when parent paths and parameters length mismatch", func(t *testing.T) { kind := Kind{ Versions: map[int]KindVersion{ - 1: { + 1: &ConsoleKindVersion{ ListPath: "/ListPath/{param1}/{param2}", ParentPathParam: []string{"param1", "param2"}, }, diff --git a/schema/schema.go b/schema/schema.go index a079a0d..1d4de81 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -31,7 +31,7 @@ func New(schema []byte) (*Schema, error) { }, nil } -func (s *Schema) GetKinds(strict bool) (map[string]Kind, error) { +func getKinds[T KindVersion](s *Schema, strict bool, buildKindVersion func(path, kind string, order int, put *v3high.Operation, strict bool) (T, error)) (map[string]Kind, error) { result := make(map[string]Kind, 0) for path := s.doc.Model.Paths.PathItems.First(); path != nil; path = path.Next() { put := path.Value().Put @@ -61,8 +61,16 @@ func (s *Schema) GetKinds(strict bool) (map[string]Kind, error) { return result, nil } -func buildKindVersion(path, kind string, order int, put *v3high.Operation, strict bool) (*KindVersion, error) { - newKind := &KindVersion{ +func (s *Schema) GetConsoleKinds(strict bool) (map[string]Kind, error) { + return getKinds(s, strict, buildConsoleKindVersion) +} + +func (s *Schema) GetGatewayKinds(strict bool) (map[string]Kind, error) { + return getKinds(s, strict, buildGatewayKindVersion) +} + +func buildConsoleKindVersion(path, kind string, order int, put *v3high.Operation, strict bool) (*ConsoleKindVersion, error) { + newKind := &ConsoleKindVersion{ Name: kind, ListPath: path, ParentPathParam: make([]string, 0, len(put.Parameters)), @@ -88,6 +96,20 @@ func buildKindVersion(path, kind string, order int, put *v3high.Operation, stric return newKind, nil } +func buildGatewayKindVersion(path, kind string, order int, put *v3high.Operation, strict bool) (*GatewayKindVersion, error) { + //for the moment there is the same but this might evolve latter + consokeKind, err := buildConsoleKindVersion(path, kind, order, put, strict) + if err != nil { + return nil, err + } + return &GatewayKindVersion{ + Name: consokeKind.Name, + ListPath: consokeKind.ListPath, + ParentPathParam: consokeKind.ParentPathParam, + Order: consokeKind.Order, + }, nil +} + type tagParseResult struct { kind string version int @@ -124,7 +146,10 @@ func parseTag(tag string) (tagParseResult, error) { return tagParseResult{kind: utils.KebabToUpperCamel(kind), version: version, order: order}, nil } -func checkThatPathParamAreInSpec(kind *KindVersion, requestBody *v3high.RequestBody) error { +func checkThatPathParamAreInSpec(kind *ConsoleKindVersion, requestBody *v3high.RequestBody) error { + if len(kind.ParentPathParam) == 0 { + return nil + } jsonContent, ok := requestBody.Content.Get("application/json") if !ok { return fmt.Errorf("No application/json content for kind %s", kind.Name) @@ -148,7 +173,7 @@ func checkThatPathParamAreInSpec(kind *KindVersion, requestBody *v3high.RequestB return nil } -func checkThatOrderArePresent(kind *KindVersion) error { +func checkThatOrderArePresent(kind *ConsoleKindVersion) error { if kind.Order == DefaultPriority { return fmt.Errorf("No priority set in schema for kind %s", kind.Name) } diff --git a/schema/sort.go b/schema/sort.go index c18a9db..48dfa0b 100644 --- a/schema/sort.go +++ b/schema/sort.go @@ -25,7 +25,7 @@ func resourcePriority(catalog KindCatalog, resource resource.Resource, debug, fa } return DefaultPriority } else { - order := kindVersion.Order + order := kindVersion.GetOrder() if order == DefaultPriority && fallbackToDefaultCatalog { defaultCatalog := ConsoleDefaultKind() //TODO: handle gateway orderFromDefaultCatalog := resourcePriority(defaultCatalog, resource, false, false) @@ -34,7 +34,7 @@ func resourcePriority(catalog KindCatalog, resource resource.Resource, debug, fa } return orderFromDefaultCatalog } else { - return kindVersion.Order + return kindVersion.GetOrder() } } } diff --git a/schema/sort_test.go b/schema/sort_test.go index ba19f1e..41601b6 100644 --- a/schema/sort_test.go +++ b/schema/sort_test.go @@ -11,18 +11,18 @@ func TestSortResources(t *testing.T) { catalog := KindCatalog{ "kind1": Kind{ Versions: map[int]KindVersion{ - 1: {Order: 1}, + 1: &ConsoleKindVersion{Order: 1}, }, }, "kind2": Kind{ Versions: map[int]KindVersion{ - 1: {Order: 2}, + 1: &ConsoleKindVersion{Order: 2}, }, }, "kind3": Kind{ Versions: map[int]KindVersion{ - 1: {Order: 3}, - 2: {Order: 4}, + 1: &ConsoleKindVersion{Order: 3}, + 2: &ConsoleKindVersion{Order: 4}, }, }, } diff --git a/utils/cdk_debug.go b/utils/cdk_debug.go new file mode 100644 index 0000000..6b1e881 --- /dev/null +++ b/utils/cdk_debug.go @@ -0,0 +1,10 @@ +package utils + +import ( + "os" + "strings" +) + +func CdkDebug() bool { + return strings.ToLower(os.Getenv("CDK_DEBUG")) == "true" +}