Skip to content

Commit

Permalink
Wizard: Load option defaults from apispec extensions (#145)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle Hodgetts committed Aug 26, 2021
1 parent 312c2ea commit 6a3e525
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 116 deletions.
143 changes: 102 additions & 41 deletions wizard/flow/ambassador.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,67 +13,144 @@ 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 {
timeoutOptions.RequestTimeout = uint32(rTimeout)
}
}

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 {
timeoutOptions.RequestTimeout = uint32(iTimeout)
}
}

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{
Expand All @@ -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

Expand Down
6 changes: 6 additions & 0 deletions wizard/flow/flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/getkin/kin-openapi/openapi3"

"github.com/kubeshop/kusk/options"
"github.com/kubeshop/kusk/wizard/prompt"
)

Expand All @@ -24,6 +25,8 @@ type baseFlow struct {
targetNamespace string
targetService string

opts *options.Options

prompt prompt.Prompter
}

Expand All @@ -35,6 +38,8 @@ type Args struct {
TargetNamespace string
TargetService string

Opts *options.Options

Prompt prompt.Prompter
}

Expand All @@ -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,
}

Expand Down
58 changes: 43 additions & 15 deletions wizard/flow/linkerd.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,64 @@ 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 {
timeoutOptions.RequestTimeout = uint32(rTimeout)
}
}

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{
Expand All @@ -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

Expand Down
Loading

0 comments on commit 6a3e525

Please sign in to comment.