diff --git a/wizard/flow/ambassador.go b/wizard/flow/ambassador.go index d5d5ef2..d5cbe8a 100644 --- a/wizard/flow/ambassador.go +++ b/wizard/flow/ambassador.go @@ -13,24 +13,36 @@ type ambassadorFlow struct { baseFlow } -func (a ambassadorFlow) Start() (Response, error) { +func (a ambassadorFlow) getBasePath() string { var basePathSuggestions []string for _, server := range a.apiSpec.Servers { basePathSuggestions = append(basePathSuggestions, server.URL) } - basePath := a.prompt.SelectOneOf("Base path prefix", basePathSuggestions, true) - trimPrefix := a.prompt.InputNonEmpty("Prefix to trim from the URL (rewrite)", basePath) + if len(basePathSuggestions) == 0 && a.opts.Path.Base != "" { + basePathSuggestions = append(basePathSuggestions, a.opts.Path.Base) + } - separateMappings := false + return a.prompt.SelectOneOf("Base path prefix", basePathSuggestions, true) +} - if basePath != "" { - separateMappings = a.prompt.Confirm("Generate mapping for each endpoint separately?") +func (a ambassadorFlow) getTrimPrefix(basePath string) string { + trimPrefixDefault := basePath + if a.opts.Path.TrimPrefix != "" { + trimPrefixDefault = a.opts.Path.TrimPrefix } + return a.prompt.InputNonEmpty("Prefix to trim from the URL (rewrite)", trimPrefixDefault) +} + +func (a ambassadorFlow) shouldSeparateMappings(basePath string) bool { + return basePath != "" && a.prompt.Confirm("Generate mapping for each endpoint separately?") +} + +func (a ambassadorFlow) getTimeoutOpts() options.TimeoutOptions { var timeoutOptions options.TimeoutOptions - if requestTimeout := a.prompt.Input("Request timeout, leave empty to skip", ""); requestTimeout != "" { + if requestTimeout := a.prompt.Input("Request timeout, leave empty to skip", strconv.Itoa(int(a.opts.Timeouts.RequestTimeout))); requestTimeout != "" { if rTimeout, err := strconv.Atoi(requestTimeout); err != nil { log.Printf("WARN: %s is not a valid request timeout value. Skipping\n", requestTimeout) } else { @@ -38,7 +50,7 @@ func (a ambassadorFlow) Start() (Response, error) { } } - if idleTimeout := a.prompt.Input("Idle timeout, leave empty to skip", ""); idleTimeout != "" { + if idleTimeout := a.prompt.Input("Idle timeout, leave empty to skip", strconv.Itoa(int(a.opts.Timeouts.IdleTimeout))); idleTimeout != "" { if iTimeout, err := strconv.Atoi(idleTimeout); err != nil { log.Printf("WARN: %s is not a valid idle timeout value. Skipping\n", idleTimeout) } else { @@ -46,34 +58,99 @@ func (a ambassadorFlow) Start() (Response, error) { } } + return timeoutOptions +} + +func (a ambassadorFlow) getCORSOpts() options.CORSOptions { var corsOpts options.CORSOptions - if setCORS := a.prompt.Confirm("Set CORS options?"); setCORS { - // Origins + + // return if user doesn't want to set CORS options + if setCORS := a.prompt.Confirm("Set CORS options?"); !setCORS { + return corsOpts + } + + // Origins + // If apispec has some origins set, prompt to use them, else prompt for input + if len(a.opts.CORS.Origins) > 0 && a.prompt.Confirm(fmt.Sprintf("add the following CORS origins? %s", a.opts.CORS.Origins)) { + corsOpts.Origins = a.opts.CORS.Origins + } else { corsOpts.Origins = a.prompt.InputMany("add CORS origin") + } - // Methods + // Methods + // If apispec has some methods set, prompt to use them, else prompt for input + if len(a.opts.CORS.Methods) > 0 && a.prompt.Confirm(fmt.Sprintf("add the following CORS methods? %s", a.opts.CORS.Methods)) { + corsOpts.Methods = a.opts.CORS.Methods + } else { corsOpts.Methods = a.prompt.InputMany("add CORS method") + } - // Headers + // Headers + // If apispec has some headers set, prompt to use them, else prompt for input + if len(a.opts.CORS.Headers) > 0 && a.prompt.Confirm(fmt.Sprintf("add the following CORS headers? %s", a.opts.CORS.Headers)) { + corsOpts.Headers = a.opts.CORS.Headers + } else { corsOpts.Headers = a.prompt.InputMany("add CORS header") + } - // ExposeHeaders + // ExposeHeaders + // If apispec has some expose headers set, prompt to use them, else prompt for input + if len(a.opts.CORS.ExposeHeaders) > 0 && a.prompt.Confirm(fmt.Sprintf("add the following CORS expose headers? %s", a.opts.CORS.ExposeHeaders)) { + corsOpts.ExposeHeaders = a.opts.CORS.ExposeHeaders + } else { corsOpts.ExposeHeaders = a.prompt.InputMany("add CORS headers you want to expose") + } - // Credentials - credentials := a.prompt.Confirm("enable CORS credentials") - corsOpts.Credentials = &credentials + // Credentials + credentials := a.prompt.Confirm("enable CORS credentials") + corsOpts.Credentials = &credentials - // Max age - maxAgeStr := a.prompt.Input("set CORS max age", "0") - maxAge, err := strconv.Atoi(maxAgeStr) - if err != nil { - log.Printf("WARN: %s is not a valid max age value. Skipping\n", maxAgeStr) - maxAge = 0 - } - corsOpts.MaxAge = maxAge + // Max age + // default is what is set in apisec, or 0 if not set + maxAgeStr := a.prompt.Input("set CORS max age", strconv.Itoa(a.opts.CORS.MaxAge)) + maxAge, err := strconv.Atoi(maxAgeStr) + if err != nil { + log.Printf("WARN: %s is not a valid max age value. Skipping\n", maxAgeStr) + maxAge = 0 } + corsOpts.MaxAge = maxAge + + return corsOpts +} + +func (a ambassadorFlow) getCmdFromOpts(opts *options.Options) string { + cmd := fmt.Sprintf("kusk ambassador -i %s ", a.apiSpecPath) + cmd = cmd + fmt.Sprintf("--namespace=%s ", a.targetNamespace) + cmd = cmd + fmt.Sprintf("--service.namespace=%s ", a.targetNamespace) + cmd = cmd + fmt.Sprintf("--service.name=%s ", a.targetService) + cmd = cmd + fmt.Sprintf("--path.base=%s ", opts.Path.Base) + + if opts.Path.TrimPrefix != "" { + cmd = cmd + fmt.Sprintf("--path.trim_prefix=%s ", opts.Path.TrimPrefix) + } + if opts.Path.Split { + cmd = cmd + fmt.Sprintf("--path.split ") + } + + if opts.Timeouts.RequestTimeout > 0 { + cmd = cmd + fmt.Sprintf("--timeouts.request_timeout=%d", opts.Timeouts.RequestTimeout) + } + if opts.Timeouts.IdleTimeout > 0 { + cmd = cmd + fmt.Sprintf("--timeouts.idle_timeout=%d", opts.Timeouts.IdleTimeout) + } + + return cmd +} + +func (a ambassadorFlow) Start() (Response, error) { + basePath := a.getBasePath() + trimPrefix := a.getTrimPrefix(basePath) + separateMappings := a.shouldSeparateMappings(basePath) + + timeoutOptions := a.getTimeoutOpts() + corsOpts := a.getCORSOpts() + opts := &options.Options{ Namespace: a.targetNamespace, Service: options.ServiceOptions{ @@ -89,23 +166,7 @@ func (a ambassadorFlow) Start() (Response, error) { CORS: corsOpts, } - cmd := fmt.Sprintf("kusk ambassador -i %s ", a.apiSpecPath) - cmd = cmd + fmt.Sprintf("--namespace=%s ", a.targetNamespace) - cmd = cmd + fmt.Sprintf("--service.namespace=%s ", a.targetNamespace) - cmd = cmd + fmt.Sprintf("--service.name=%s ", a.targetService) - cmd = cmd + fmt.Sprintf("--path.base=%s ", basePath) - if trimPrefix != "" { - cmd = cmd + fmt.Sprintf("--path.trim_prefix=%s ", trimPrefix) - } - if separateMappings { - cmd = cmd + fmt.Sprintf("--path.split ") - } - if timeoutOptions.RequestTimeout > 0 { - cmd = cmd + fmt.Sprintf("--timeouts.request_timeout=%d", timeoutOptions.RequestTimeout) - } - if timeoutOptions.IdleTimeout > 0 { - cmd = cmd + fmt.Sprintf("--timeouts.idle_timeout=%d", timeoutOptions.IdleTimeout) - } + cmd := a.getCmdFromOpts(opts) var ag ambassador.Generator diff --git a/wizard/flow/flow.go b/wizard/flow/flow.go index 32b08dd..e1608d9 100644 --- a/wizard/flow/flow.go +++ b/wizard/flow/flow.go @@ -5,6 +5,7 @@ import ( "github.com/getkin/kin-openapi/openapi3" + "github.com/kubeshop/kusk/options" "github.com/kubeshop/kusk/wizard/prompt" ) @@ -24,6 +25,8 @@ type baseFlow struct { targetNamespace string targetService string + opts *options.Options + prompt prompt.Prompter } @@ -35,6 +38,8 @@ type Args struct { TargetNamespace string TargetService string + Opts *options.Options + Prompt prompt.Prompter } @@ -46,6 +51,7 @@ func New(args *Args) (Interface, error) { apiSpec: args.ApiSpec, targetNamespace: args.TargetNamespace, targetService: args.TargetService, + opts: args.Opts, prompt: args.Prompt, } diff --git a/wizard/flow/linkerd.go b/wizard/flow/linkerd.go index 3fdaf32..6654903 100644 --- a/wizard/flow/linkerd.go +++ b/wizard/flow/linkerd.go @@ -13,20 +13,33 @@ type linkerdFlow struct { baseFlow } -func (l linkerdFlow) Start() (Response, error) { - clusterDomain := l.prompt.InputNonEmpty("Cluster domain", "cluster.local") - +func (l linkerdFlow) getBasePath() string { var basePathSuggestions []string for _, server := range l.apiSpec.Servers { basePathSuggestions = append(basePathSuggestions, server.URL) } - basePath := l.prompt.SelectOneOf("Base path prefix", basePathSuggestions, true) + if len(basePathSuggestions) == 0 && l.opts.Path.Base != "" { + basePathSuggestions = append(basePathSuggestions, l.opts.Path.Base) + } + + return l.prompt.SelectOneOf("Base path prefix", basePathSuggestions, true) +} + +func (l linkerdFlow) getClusterDomain() string { + defaultClusterDomain := "cluster.local" + if l.opts.Cluster.ClusterDomain != "" { + defaultClusterDomain = l.opts.Cluster.ClusterDomain + } + return l.prompt.InputNonEmpty("Cluster domain", defaultClusterDomain) +} + +func (l linkerdFlow) getTimeoutOpts() options.TimeoutOptions { var timeoutOptions options.TimeoutOptions // Support only request timeout as linkerd generator doesn't support idle timeout - if requestTimeout := l.prompt.Input("Request timeout, leave empty to skip", ""); requestTimeout != "" { + if requestTimeout := l.prompt.Input("Request timeout, leave empty to skip", strconv.Itoa(int(l.opts.Timeouts.RequestTimeout))); requestTimeout != "" { if rTimeout, err := strconv.Atoi(requestTimeout); err != nil { log.Printf("WARN: %s is not a valid request timeout value. Skipping\n", requestTimeout) } else { @@ -34,6 +47,30 @@ func (l linkerdFlow) Start() (Response, error) { } } + return timeoutOptions +} + +func (l linkerdFlow) getCmdFromOpts(opts *options.Options) string { + cmd := fmt.Sprintf("kusk linkerd -i %s ", l.apiSpecPath) + cmd = cmd + fmt.Sprintf("--namespace=%s ", l.targetNamespace) + cmd = cmd + fmt.Sprintf("--service.namespace=%s ", l.targetNamespace) + cmd = cmd + fmt.Sprintf("--service.name=%s ", l.targetService) + cmd = cmd + fmt.Sprintf("--path.base=%s ", opts.Path.Base) + cmd = cmd + fmt.Sprintf("--cluster.cluster_domain=%s ", opts.Cluster.ClusterDomain) + + if opts.Timeouts.RequestTimeout > 0 { + cmd = cmd + fmt.Sprintf("--timeouts.request_timeout=%d", opts.Timeouts.RequestTimeout) + } + + return cmd +} + +func (l linkerdFlow) Start() (Response, error) { + clusterDomain := l.getClusterDomain() + + basePath := l.getBasePath() + timeoutOptions := l.getTimeoutOpts() + opts := &options.Options{ Namespace: l.targetNamespace, Path: options.PathOptions{ @@ -49,16 +86,7 @@ func (l linkerdFlow) Start() (Response, error) { Timeouts: timeoutOptions, } - cmd := fmt.Sprintf("kusk linkerd -i %s ", l.apiSpecPath) - cmd = cmd + fmt.Sprintf("--namespace=%s ", l.targetNamespace) - cmd = cmd + fmt.Sprintf("--service.namespace=%s ", l.targetNamespace) - cmd = cmd + fmt.Sprintf("--service.name=%s ", l.targetService) - cmd = cmd + fmt.Sprintf("--path.base=%s ", basePath) - cmd = cmd + fmt.Sprintf("--cluster.cluster_domain=%s ", clusterDomain) - - if timeoutOptions.RequestTimeout > 0 { - cmd = cmd + fmt.Sprintf("--timeouts.request_timeout=%d", timeoutOptions.RequestTimeout) - } + cmd := l.getCmdFromOpts(opts) var ld linkerd.Generator diff --git a/wizard/flow/nginx_ingress.go b/wizard/flow/nginx_ingress.go index 0ba3972..ef949c3 100644 --- a/wizard/flow/nginx_ingress.go +++ b/wizard/flow/nginx_ingress.go @@ -14,24 +14,37 @@ type nginxIngressFlow struct { baseFlow } -func (n nginxIngressFlow) Start() (Response, error) { +func (n nginxIngressFlow) getBasePath() string { var basePathSuggestions []string for _, server := range n.apiSpec.Servers { basePathSuggestions = append(basePathSuggestions, server.URL) } - basePath := n.prompt.SelectOneOf("Base path prefix", basePathSuggestions, true) - trimPrefix := n.prompt.Input("Prefix to trim from the URL (rewrite)", "") + if len(basePathSuggestions) == 0 && n.opts.Path.Base != "" { + basePathSuggestions = append(basePathSuggestions, n.opts.Path.Base) + } + + return n.prompt.SelectOneOf("Base path prefix", basePathSuggestions, true) +} - separateMappings := false - if basePath != "" { - separateMappings = n.prompt.Confirm("Generate ingress resource for each endpoint separately?") +func (n nginxIngressFlow) getTrimPrefix(basePath string) string { + trimPrefixDefault := basePath + if n.opts.Path.TrimPrefix != "" { + trimPrefixDefault = n.opts.Path.TrimPrefix } + return n.prompt.InputNonEmpty("Prefix to trim from the URL (rewrite)", trimPrefixDefault) +} + +func (n nginxIngressFlow) shouldSeparateMappings(basePath string) bool { + return basePath != "" && n.prompt.Confirm("Generate ingress resource for each endpoint separately?") +} + +func (n nginxIngressFlow) getTimeoutOpts() options.TimeoutOptions { var timeoutOptions options.TimeoutOptions // Support only request timeout as nginx-ingress generator doesn't support idle timeout - if requestTimeout := n.prompt.Input("Request timeout, leave empty to skip", ""); requestTimeout != "" { + if requestTimeout := n.prompt.Input("Request timeout, leave empty to skip", strconv.Itoa(int(n.opts.Timeouts.RequestTimeout))); requestTimeout != "" { if rTimeout, err := strconv.Atoi(requestTimeout); err != nil { log.Printf("WARN: %s is not a valid request timeout value. Skipping\n", requestTimeout) } else { @@ -39,34 +52,94 @@ func (n nginxIngressFlow) Start() (Response, error) { } } + return timeoutOptions +} + +func (n nginxIngressFlow) getCORSOpts() options.CORSOptions { var corsOpts options.CORSOptions - if setCORS := n.prompt.Confirm("Set CORS options?"); setCORS { - // Origins - corsOpts.Origins = []string{n.prompt.Input("add CORS origin", "")} - // Methods + // return if user doesn't want to set CORS options + if setCORS := n.prompt.Confirm("Set CORS options?"); !setCORS { + return corsOpts + } + + // Origins + // If apispec has some origins set, prompt to use them, else prompt for input + if len(n.opts.CORS.Origins) > 0 && n.prompt.Confirm(fmt.Sprintf("add the following CORS origins? %s", n.opts.CORS.Origins)) { + corsOpts.Origins = n.opts.CORS.Origins + } else { + corsOpts.Origins = n.prompt.InputMany("add CORS origin") + } + + // Methods + // If apispec has some methods set, prompt to use them, else prompt for input + if len(n.opts.CORS.Methods) > 0 && n.prompt.Confirm(fmt.Sprintf("add the following CORS methods? %s", n.opts.CORS.Methods)) { + corsOpts.Methods = n.opts.CORS.Methods + } else { corsOpts.Methods = n.prompt.InputMany("add CORS method") + } - // Headers + // Headers + // If apispec has some headers set, prompt to use them, else prompt for input + if len(n.opts.CORS.Headers) > 0 && n.prompt.Confirm(fmt.Sprintf("add the following CORS headers? %s", n.opts.CORS.Headers)) { + corsOpts.Headers = n.opts.CORS.Headers + } else { corsOpts.Headers = n.prompt.InputMany("add CORS header") + } - // ExposeHeaders + // ExposeHeaders + // If apispec has some expose headers set, prompt to use them, else prompt for input + if len(n.opts.CORS.ExposeHeaders) > 0 && n.prompt.Confirm(fmt.Sprintf("add the following CORS expose headers? %s", n.opts.CORS.ExposeHeaders)) { + corsOpts.ExposeHeaders = n.opts.CORS.ExposeHeaders + } else { corsOpts.ExposeHeaders = n.prompt.InputMany("add CORS headers you want to expose") + } - // Credentials - credentials := n.prompt.Confirm("enable CORS credentials") - corsOpts.Credentials = &credentials + // Credentials + credentials := n.prompt.Confirm("enable CORS credentials") + corsOpts.Credentials = &credentials - // Max age - maxAgeStr := n.prompt.Input("set CORS max age", "0") - maxAge, err := strconv.Atoi(maxAgeStr) - if err != nil { - log.Printf("WARN: %s is not a valid max age value. Skipping\n", maxAgeStr) - maxAge = 0 - } - corsOpts.MaxAge = maxAge + // Max age + // default is what is set in apisec, or 0 if not set + maxAgeStr := n.prompt.Input("set CORS max age", strconv.Itoa(n.opts.CORS.MaxAge)) + maxAge, err := strconv.Atoi(maxAgeStr) + if err != nil { + log.Printf("WARN: %s is not a valid max age value. Skipping\n", maxAgeStr) + maxAge = 0 + } + + corsOpts.MaxAge = maxAge + + return corsOpts +} + +func (n nginxIngressFlow) getCmdFromOpts(opts *options.Options) string { + var sb strings.Builder + sb.WriteString(fmt.Sprintf("kusk nginx-ingress -i %s ", n.apiSpecPath)) + sb.WriteString(fmt.Sprintf("--namespace=%s ", n.targetNamespace)) + sb.WriteString(fmt.Sprintf("--service.namespace=%s ", n.targetNamespace)) + sb.WriteString(fmt.Sprintf("--service.name=%s ", n.targetService)) + sb.WriteString(fmt.Sprintf("--path.base=%s ", opts.Path.Base)) + + if opts.Path.TrimPrefix != "" { + sb.WriteString(fmt.Sprintf("--path.trim_prefix=%s ", opts.Path.TrimPrefix)) } + if opts.Path.Split { + sb.WriteString("--path.split ") + } + + return sb.String() +} + +func (n nginxIngressFlow) Start() (Response, error) { + basePath := n.getBasePath() + trimPrefix := n.getTrimPrefix(basePath) + separateMappings := n.shouldSeparateMappings(basePath) + + timeoutOptions := n.getTimeoutOpts() + corsOpts := n.getCORSOpts() + opts := &options.Options{ Namespace: n.targetNamespace, Service: options.ServiceOptions{ @@ -82,20 +155,7 @@ func (n nginxIngressFlow) Start() (Response, error) { CORS: corsOpts, } - var sb strings.Builder - sb.WriteString(fmt.Sprintf("kusk ambassador -i %s ", n.apiSpecPath)) - sb.WriteString(fmt.Sprintf("--namespace=%s ", n.targetNamespace)) - sb.WriteString(fmt.Sprintf("--service.namespace=%s ", n.targetNamespace)) - sb.WriteString(fmt.Sprintf("--service.name=%s ", n.targetService)) - sb.WriteString(fmt.Sprintf("--path.base=%s ", basePath)) - - if trimPrefix != "" { - sb.WriteString(fmt.Sprintf("--path.trim_prefix=%s ", trimPrefix)) - } - - if separateMappings { - sb.WriteString("--path.split ") - } + cmd := n.getCmdFromOpts(opts) var ingressGenerator nginx_ingress.Generator ingresses, err := ingressGenerator.Generate(opts, n.apiSpec) @@ -104,7 +164,7 @@ func (n nginxIngressFlow) Start() (Response, error) { } return Response{ - EquivalentCmd: sb.String(), + EquivalentCmd: cmd, Manifests: ingresses, }, nil } diff --git a/wizard/wizard.go b/wizard/wizard.go index 451a70b..e473e9c 100644 --- a/wizard/wizard.go +++ b/wizard/wizard.go @@ -10,6 +10,7 @@ import ( "k8s.io/client-go/util/homedir" "github.com/kubeshop/kusk/cluster" + "github.com/kubeshop/kusk/spec" "github.com/kubeshop/kusk/wizard/flow" "github.com/kubeshop/kusk/wizard/prompt" ) @@ -26,13 +27,24 @@ func Start(apiSpecPath string, apiSpec *openapi3.T, prompt prompt.Prompter) { ) } + opts, err := spec.GetOptions(apiSpec) + if err != nil { + log.Fatal(fmt.Errorf("failed to read options from apispec: %w", err)) + } + + args := &flow.Args{ + ApiSpecPath: apiSpecPath, + ApiSpec: apiSpec, + Prompt: prompt, + Opts: opts, + } + var mappings string - var err error if canConnectToCluster { - mappings, err = flowWithCluster(apiSpecPath, apiSpec, kubeConfigPath, prompt) + mappings, err = flowWithCluster(args, kubeConfigPath) } else { - mappings, err = flowWithoutCluster(apiSpecPath, apiSpec, prompt) + mappings, err = flowWithoutCluster(args) } if err != nil { @@ -55,7 +67,7 @@ func Start(apiSpecPath string, apiSpec *openapi3.T, prompt prompt.Prompter) { fmt.Println(mappings) } -func flowWithCluster(apiSpecPath string, apiSpec *openapi3.T, kubeConfigPath string, prompt prompt.Prompter) (string, error) { +func flowWithCluster(args *flow.Args, kubeConfigPath string) (string, error) { var servicesToSuggest []string client, err := cluster.NewClient(kubeConfigPath) @@ -103,36 +115,29 @@ func flowWithCluster(apiSpecPath string, apiSpec *openapi3.T, kubeConfigPath str return "", fmt.Errorf("failed to list namespaces: %w", err) } - targetServiceNamespace = prompt.SelectOneOf("Choose namespace with your service", targetServiceNamespaceSuggestions, true) + targetServiceNamespace = args.Prompt.SelectOneOf("Choose namespace with your service", targetServiceNamespaceSuggestions, true) targetServiceSuggestions, err := client.ListServices(targetServiceNamespace) if err != nil { return "", fmt.Errorf("failed to list namespaces: %w", err) } - args := &flow.Args{ - ApiSpecPath: apiSpecPath, - ApiSpec: apiSpec, - Prompt: prompt, - } - - args.TargetService = prompt.SelectOneOf("Choose your service", targetServiceSuggestions, true) + args.TargetService = args.Prompt.SelectOneOf("Choose your service", targetServiceSuggestions, true) - args.Service = prompt.SelectOneOf("Choose a service you want Kusk generate manifests for", servicesToSuggest, false) + args.Service = args.Prompt.SelectOneOf("Choose a service you want Kusk generate manifests for", servicesToSuggest, false) return executeFlow(args) } -func flowWithoutCluster(apiSpecPath string, apiSpec *openapi3.T, prompt prompt.Prompter) (string, error) { - args := &flow.Args{ - ApiSpecPath: apiSpecPath, - ApiSpec: apiSpec, - Prompt: prompt, +func flowWithoutCluster(args *flow.Args) (string, error) { + defaultNamespace := "default" + if args.Opts.Service.Namespace != "" { + defaultNamespace = args.Opts.Service.Namespace } - args.TargetNamespace = prompt.InputNonEmpty("Enter namespace with your service", "default") - args.TargetService = prompt.InputNonEmpty("Enter your service name", "") + args.TargetNamespace = args.Prompt.InputNonEmpty("Enter namespace with your service", defaultNamespace) + args.TargetService = args.Prompt.InputNonEmpty("Enter your service name", args.Opts.Service.Name) - args.Service = prompt.SelectOneOf( + args.Service = args.Prompt.SelectOneOf( "Choose a service you want Kusk generate manifests for", []string{ "ambassador",