diff --git a/cmd/profiles.go b/cmd/profiles.go index 957301ecb..82b4174b6 100644 --- a/cmd/profiles.go +++ b/cmd/profiles.go @@ -16,7 +16,7 @@ import ( "github.com/elastic/elastic-package/internal/cobraext" "github.com/elastic/elastic-package/internal/configuration/locations" - "github.com/elastic/elastic-package/internal/environment" + "github.com/elastic/elastic-package/internal/install" "github.com/elastic/elastic-package/internal/profile" ) @@ -26,9 +26,6 @@ const jsonFormat = "json" // tableFormat is the format for table output const tableFormat = "table" -// profileNameEnvVar is the name of the environment variable to set the default profile -var profileNameEnvVar = environment.WithElasticPackagePrefix("PROFILE") - func setupProfilesCommand() *cobraext.Command { profilesLongDescription := `Use this command to add, remove, and manage multiple config profiles. @@ -65,12 +62,16 @@ User profiles are not overwritten on upgrade of elastic-stack, and can be freely return errors.Wrapf(err, "error creating profile %s from profile %s", newProfileName, fromName) } - fmt.Printf("Created profile %s from %s.\n", newProfileName, fromName) + if fromName == "" { + fmt.Printf("Created profile %s.\n", newProfileName) + } else { + fmt.Printf("Created profile %s from %s.\n", newProfileName, fromName) + } return nil }, } - profileNewCommand.Flags().String(cobraext.ProfileFromFlagName, "default", cobraext.ProfileFromFlagDescription) + profileNewCommand.Flags().String(cobraext.ProfileFromFlagName, "", cobraext.ProfileFromFlagDescription) profileDeleteCommand := &cobra.Command{ Use: "delete", @@ -104,10 +105,14 @@ User profiles are not overwritten on upgrade of elastic-stack, and can be freely if err != nil { return errors.Wrap(err, "error listing all profiles") } + if len(profileList) == 0 { + fmt.Println("There are no profiles yet.") + return nil + } format, err := cmd.Flags().GetString(cobraext.ProfileFormatFlagName) if err != nil { - return cobraext.FlagParsingError(err, cobraext.ProfileFromFlagName) + return cobraext.FlagParsingError(err, cobraext.ProfileFormatFlagName) } switch format { @@ -122,7 +127,45 @@ User profiles are not overwritten on upgrade of elastic-stack, and can be freely } profileListCommand.Flags().String(cobraext.ProfileFormatFlagName, tableFormat, cobraext.ProfileFormatFlagDescription) - profileCommand.AddCommand(profileNewCommand, profileDeleteCommand, profileListCommand) + profileUseCommand := &cobra.Command{ + Use: "use", + Short: "Sets the profile to use when no other is specified", + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("use requires an argument") + } + profileName := args[0] + + _, err := profile.LoadProfile(profileName) + if err != nil { + return fmt.Errorf("cannot use profile %q: %v", profileName, err) + } + + location, err := locations.NewLocationManager() + if err != nil { + return fmt.Errorf("error fetching profile: %w", err) + } + + config, err := install.Configuration() + if err != nil { + return fmt.Errorf("failed to load current configuration: %w", err) + } + config.SetCurrentProfile(profileName) + + err = install.WriteConfigFile(location, config) + if err != nil { + return fmt.Errorf("failed to store configuration: %w", err) + } + return nil + }, + } + + profileCommand.AddCommand( + profileNewCommand, + profileDeleteCommand, + profileListCommand, + profileUseCommand, + ) return cobraext.NewCommand(profileCommand, cobraext.ContextGlobal) } @@ -175,15 +218,6 @@ func profileToList(profiles []profile.Metadata) [][]string { return profileList } -func lookupEnv() string { - env := os.Getenv(profileNameEnvVar) - if env == "" { - return profile.DefaultProfile - } - return env - -} - func availableProfilesAsAList() ([]string, error) { loc, err := locations.NewLocationManager() if err != nil { diff --git a/cmd/stack.go b/cmd/stack.go index c96193111..4039ad32b 100644 --- a/cmd/stack.go +++ b/cmd/stack.go @@ -39,7 +39,7 @@ By default the latest released version of the stack is spun up but it is possibl Be aware that a common issue while trying to boot up the stack is that your Docker environments settings are too low in terms of memory threshold. -To ęxpose local packages in the Package Registry, build them first and boot up the stack from inside of the Git repository containing the package (e.g. elastic/integrations). They will be copied to the development stack (~/.elastic-package/stack/development) and used to build a custom Docker image of the Package Registry. +To expose local packages in the Package Registry, build them first and boot up the stack from inside of the Git repository containing the package (e.g. elastic/integrations). They will be copied to the development stack (~/.elastic-package/stack/development) and used to build a custom Docker image of the Package Registry. For details on how to connect the service with the Elastic stack, see the [service command](https://github.com/elastic/elastic-package/blob/main/README.md#elastic-package-service).` @@ -73,37 +73,24 @@ func setupStackCommand() *cobraext.Command { return cobraext.FlagParsingError(err, cobraext.StackVersionFlagName) } - profileName, err := cmd.Flags().GetString(cobraext.ProfileFlagName) + profile, err := getProfileFlag(cmd) if err != nil { - return cobraext.FlagParsingError(err, cobraext.ProfileFlagName) - } - - userProfile, err := profile.LoadProfile(profileName) - if errors.Is(err, profile.ErrNotAProfile) { - pList, err := availableProfilesAsAList() - if err != nil { - return errors.Wrap(err, "error listing known profiles") - } - return fmt.Errorf("%s is not a valid profile, known profiles are: %s", profileName, pList) - } - if err != nil { - return errors.Wrap(err, "error loading profile") + return err } - // Print information before starting the stack, for cases where - // this is executed in the foreground, without daemon mode. - cmd.Printf("Using profile %s.\n", userProfile.ProfilePath) - cmd.Println(`Remember to load stack environment variables using 'eval "$(elastic-package stack shellinit)"'.`) - err = printInitConfig(cmd, userProfile) + provider, err := getProviderFromProfile(cmd, profile, true) if err != nil { return err } - err = stack.BootUp(stack.Options{ + cmd.Printf("Using profile %s.\n", profile.ProfilePath) + cmd.Println(`Remember to load stack environment variables using 'eval "$(elastic-package stack shellinit)"'.`) + err = provider.BootUp(stack.Options{ DaemonMode: daemonMode, StackVersion: stackVersion, Services: services, - Profile: userProfile, + Profile: profile, + Printer: cmd, }) if err != nil { return errors.Wrap(err, "booting up the stack failed") @@ -117,6 +104,7 @@ func setupStackCommand() *cobraext.Command { upCommand.Flags().StringSliceP(cobraext.StackServicesFlagName, "s", nil, fmt.Sprintf(cobraext.StackServicesFlagDescription, strings.Join(availableServicesAsList(), ","))) upCommand.Flags().StringP(cobraext.StackVersionFlagName, "", install.DefaultStackVersion, cobraext.StackVersionFlagDescription) + upCommand.Flags().String(cobraext.StackProviderFlagName, "", fmt.Sprintf(cobraext.StackProviderFlagDescription, strings.Join(stack.SupportedProviders, ", "))) downCommand := &cobra.Command{ Use: "down", @@ -124,26 +112,19 @@ func setupStackCommand() *cobraext.Command { RunE: func(cmd *cobra.Command, args []string) error { cmd.Println("Take down the Elastic stack") - profileName, err := cmd.Flags().GetString(cobraext.ProfileFlagName) + profile, err := getProfileFlag(cmd) if err != nil { - return cobraext.FlagParsingError(err, cobraext.ProfileFlagName) - } - - userProfile, err := profile.LoadProfile(profileName) - if errors.Is(err, profile.ErrNotAProfile) { - pList, err := availableProfilesAsAList() - if err != nil { - return errors.Wrap(err, "error listing known profiles") - } - return fmt.Errorf("%s is not a valid profile, known profiles are: %s", profileName, pList) + return err } + provider, err := getProviderFromProfile(cmd, profile, false) if err != nil { - return errors.Wrap(err, "error loading profile") + return err } - err = stack.TearDown(stack.Options{ - Profile: userProfile, + err = provider.TearDown(stack.Options{ + Profile: profile, + Printer: cmd, }) if err != nil { return errors.Wrap(err, "tearing down the stack failed") @@ -160,14 +141,14 @@ func setupStackCommand() *cobraext.Command { RunE: func(cmd *cobra.Command, args []string) error { cmd.Println("Update the Elastic stack") - profileName, err := cmd.Flags().GetString(cobraext.ProfileFlagName) + profile, err := getProfileFlag(cmd) if err != nil { - return cobraext.FlagParsingError(err, cobraext.ProfileFlagName) + return err } - profile, err := profile.LoadProfile(profileName) + provider, err := getProviderFromProfile(cmd, profile, false) if err != nil { - return errors.Wrap(err, "error loading profile") + return err } stackVersion, err := cmd.Flags().GetString(cobraext.StackVersionFlagName) @@ -175,9 +156,10 @@ func setupStackCommand() *cobraext.Command { return cobraext.FlagParsingError(err, cobraext.StackVersionFlagName) } - err = stack.Update(stack.Options{ + err = provider.Update(stack.Options{ StackVersion: stackVersion, Profile: profile, + Printer: cmd, }) if err != nil { return errors.Wrap(err, "failed updating the stack images") @@ -193,11 +175,6 @@ func setupStackCommand() *cobraext.Command { Use: "shellinit", Short: "Export environment variables", RunE: func(cmd *cobra.Command, args []string) error { - profileName, err := cmd.Flags().GetString(cobraext.ProfileFlagName) - if err != nil { - return cobraext.FlagParsingError(err, cobraext.ProfileFlagName) - } - shellName, err := cmd.Flags().GetString(cobraext.ShellInitShellFlagName) if err != nil { return cobraext.FlagParsingError(err, cobraext.ShellInitShellFlagName) @@ -207,9 +184,9 @@ func setupStackCommand() *cobraext.Command { fmt.Fprintf(cmd.OutOrStderr(), "Detected shell: %s\n", shellName) } - profile, err := profile.LoadProfile(profileName) + profile, err := getProfileFlag(cmd) if err != nil { - return errors.Wrap(err, "error loading profile") + return err } shellCode, err := stack.ShellInit(profile, shellName) @@ -232,17 +209,17 @@ func setupStackCommand() *cobraext.Command { return cobraext.FlagParsingError(err, cobraext.StackDumpOutputFlagName) } - profileName, err := cmd.Flags().GetString(cobraext.ProfileFlagName) + profile, err := getProfileFlag(cmd) if err != nil { - return cobraext.FlagParsingError(err, cobraext.ProfileFlagName) + return err } - profile, err := profile.LoadProfile(profileName) + provider, err := getProviderFromProfile(cmd, profile, false) if err != nil { - return errors.Wrap(err, "error loading profile") + return err } - target, err := stack.Dump(stack.DumpOptions{ + target, err := provider.Dump(stack.DumpOptions{ Output: output, Profile: profile, }) @@ -262,7 +239,20 @@ func setupStackCommand() *cobraext.Command { Use: "status", Short: "Show status of the stack services", RunE: func(cmd *cobra.Command, args []string) error { - servicesStatus, err := stack.Status() + profile, err := getProfileFlag(cmd) + if err != nil { + return err + } + + provider, err := getProviderFromProfile(cmd, profile, false) + if err != nil { + return err + } + + servicesStatus, err := provider.Status(stack.Options{ + Profile: profile, + Printer: cmd, + }) if err != nil { return errors.Wrap(err, "failed getting stack status") } @@ -278,7 +268,7 @@ func setupStackCommand() *cobraext.Command { Short: "Manage the Elastic stack", Long: stackLongDescription, } - cmd.PersistentFlags().StringP(cobraext.ProfileFlagName, "p", lookupEnv(), fmt.Sprintf(cobraext.ProfileFlagDescription, profileNameEnvVar)) + cmd.PersistentFlags().StringP(cobraext.ProfileFlagName, "p", "", fmt.Sprintf(cobraext.ProfileFlagDescription, install.ProfileNameEnvVar)) cmd.AddCommand( upCommand, downCommand, @@ -317,18 +307,6 @@ func validateServicesFlag(services []string) error { return nil } -func printInitConfig(cmd *cobra.Command, profile *profile.Profile) error { - initConfig, err := stack.StackInitConfig(profile) - if err != nil { - return nil - } - cmd.Printf("Elasticsearch host: %s\n", initConfig.ElasticsearchHostPort) - cmd.Printf("Kibana host: %s\n", initConfig.KibanaHostPort) - cmd.Printf("Username: %s\n", initConfig.ElasticsearchUsername) - cmd.Printf("Password: %s\n", initConfig.ElasticsearchPassword) - return nil -} - func printStatus(cmd *cobra.Command, servicesStatus []stack.ServiceStatus) { if len(servicesStatus) == 0 { cmd.Printf(" - No service running\n") @@ -343,3 +321,57 @@ func printStatus(cmd *cobra.Command, servicesStatus []stack.ServiceStatus) { t.SetStyle(table.StyleRounded) cmd.Println(t.Render()) } + +func getProfileFlag(cmd *cobra.Command) (*profile.Profile, error) { + profileName, err := cmd.Flags().GetString(cobraext.ProfileFlagName) + if err != nil { + return nil, cobraext.FlagParsingError(err, cobraext.ProfileFlagName) + } + if profileName == "" { + config, err := install.Configuration() + if err != nil { + return nil, fmt.Errorf("cannot read configuration: %w", err) + } + profileName = config.CurrentProfile() + } + + p, err := profile.LoadProfile(profileName) + if errors.Is(err, profile.ErrNotAProfile) { + list, err := availableProfilesAsAList() + if err != nil { + return nil, errors.Wrap(err, "error listing known profiles") + } + if len(list) == 0 { + return nil, fmt.Errorf("%s is not a valid profile", profileName) + } + return nil, fmt.Errorf("%s is not a valid profile, known profiles are: %s", profileName, strings.Join(list, ", ")) + } + if err != nil { + return nil, errors.Wrap(err, "error loading profile") + } + + return p, nil +} + +func getProviderFromProfile(cmd *cobra.Command, profile *profile.Profile, checkFlag bool) (stack.Provider, error) { + var providerName = stack.DefaultProvider + stackConfig, err := stack.LoadConfig(profile) + if err != nil { + return nil, err + } + if stackConfig.Provider != "" { + providerName = stackConfig.Provider + } + + if checkFlag { + providerFlag, err := cmd.Flags().GetString(cobraext.StackProviderFlagName) + if err != nil { + return nil, cobraext.FlagParsingError(err, cobraext.StackProviderFlagName) + } + if providerFlag != "" { + providerName = providerFlag + } + } + + return stack.BuildProvider(providerName, profile) +} diff --git a/go.mod b/go.mod index b8996d5bb..de6f1e56b 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,10 @@ require ( github.com/aymerick/raymond v2.0.2+incompatible github.com/boumenot/gocover-cobertura v1.2.0 github.com/cespare/xxhash/v2 v2.2.0 + github.com/elastic/cloud-sdk-go v1.11.0 github.com/elastic/go-elasticsearch/v7 v7.17.7 github.com/elastic/go-licenser v0.4.1 + github.com/elastic/go-resource v0.1.1 github.com/elastic/go-ucfg v0.8.6 github.com/elastic/package-spec/v2 v2.5.0 github.com/fatih/color v1.14.1 @@ -51,6 +53,7 @@ require ( github.com/acomagu/bufpipe v1.0.3 // indirect github.com/andybalholm/brotli v1.0.4 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect + github.com/blang/semver/v4 v4.0.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cloudflare/circl v1.3.1 // indirect github.com/creasty/defaults v1.6.0 // indirect @@ -68,11 +71,16 @@ require ( github.com/go-git/gcfg v1.5.0 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-openapi/analysis v0.21.1 // indirect github.com/go-openapi/errors v0.20.3 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/loads v0.21.0 // indirect + github.com/go-openapi/runtime v0.23.0 // indirect + github.com/go-openapi/spec v0.20.4 // indirect github.com/go-openapi/strfmt v0.21.3 // indirect github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/validate v0.20.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -82,6 +90,8 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect @@ -110,6 +120,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nwaples/rardecode v1.1.3 // indirect github.com/oklog/ulid v1.3.1 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4/v4 v4.1.17 // indirect github.com/pjbgf/sha1cd v0.2.3 // indirect @@ -159,3 +170,5 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) + +replace github.com/gobuffalo/packr/v2 => github.com/gobuffalo/packr/v2 v2.3.2 diff --git a/go.sum b/go.sum index 8322c6d11..d2d1d672a 100644 --- a/go.sum +++ b/go.sum @@ -31,21 +31,34 @@ github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08 h1:dS7r5z4iGS0q github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM= github.com/ProtonMail/gopenpgp/v2 v2.5.2 h1:97SjlWNAxXl9P22lgwgrZRshQdiEfAht0g3ZoiA1GCw= github.com/ProtonMail/gopenpgp/v2 v2.5.2/go.mod h1:52qDaCnto6r+CoWbuU50T77XQt99lIs46HtHtvgFO3o= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0= github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/boumenot/gocover-cobertura v1.2.0 h1:g+VROIASoEHBrEilIyaCmgo7HGm+AV5yKEPLk0qIY+s= github.com/boumenot/gocover-cobertura v1.2.0/go.mod h1:fz7ly8dslE42VRR5ZWLt2OHGDHjkTiA2oNvKgJEjLT0= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= @@ -66,6 +79,10 @@ github.com/cloudflare/circl v1.3.1/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSb github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -77,14 +94,20 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk= github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= +github.com/elastic/cloud-sdk-go v1.11.0 h1:rJRL6EScg4uJXRFlvWB6GdEBvJ7hvbZZ3NjMdH7ak/k= +github.com/elastic/cloud-sdk-go v1.11.0/go.mod h1:YJfcOSFF/MS+o9dWZAUs+JYWfoeRWzAKmtUm27cbUzA= github.com/elastic/go-elasticsearch/v7 v7.17.7 h1:pcYNfITNPusl+cLwLN6OLmVT+F73Els0nbaWOmYachs= github.com/elastic/go-elasticsearch/v7 v7.17.7/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4= github.com/elastic/go-licenser v0.4.1 h1:1xDURsc8pL5zYT9R29425J3vkHdt4RT5TNEMeRN48x4= github.com/elastic/go-licenser v0.4.1/go.mod h1:V56wHMpmdURfibNBggaSBfqgPxyT1Tldns1i87iTEvU= +github.com/elastic/go-resource v0.1.1 h1:vM173uhPoaZ7C64rKrYbbBn5kxOhstE1+YcGFteAKh8= +github.com/elastic/go-resource v0.1.1/go.mod h1:7F1Wjs6eSFX0i/235yAK/x9bvPNd9/ML92AiULa4XYA= github.com/elastic/go-ucfg v0.8.6 h1:stUeyh2goTgGX+/wb9gzKvTv0YB0231LTpKUgCKj4U0= github.com/elastic/go-ucfg v0.8.6/go.mod h1:4E8mPOLSUV9hQ7sgLEJ4bvt0KhMuDJa8joDT2QGAEKA= github.com/elastic/gojsonschema v1.2.1 h1:cUMbgsz0wyEB4x7xf3zUEvUVDl6WCz2RKcQPul8OsQc= @@ -112,12 +135,15 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= @@ -135,19 +161,133 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ= +github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk= +github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og= +github.com/go-openapi/analysis v0.20.1/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og= +github.com/go-openapi/analysis v0.21.1 h1:krcNCEvCttpSUFBPOrfvn7nniejvrOkoNYRlZwQFpEs= +github.com/go-openapi/analysis v0.21.1/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.1/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI= +github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY= +github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= +github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= +github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4= +github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o= +github.com/go-openapi/loads v0.21.0 h1:jYtUO4wwP7psAweisP/MDoOpdzsYEESdoPcsWjHDR68= +github.com/go-openapi/loads v0.21.0/go.mod h1:rHYve9nZrQ4CJhyeIIFJINGCg1tQpx2yJrrNo8sf1ws= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo= +github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98= +github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk= +github.com/go-openapi/runtime v0.23.0 h1:HX6ET2sHCIvaKeDDQoU01CtO1ekg5EkekHSkLTtWXH0= +github.com/go-openapi/runtime v0.23.0/go.mod h1:aQg+kaIQEn+A2CRSY1TxbM8+sT9g2V3aLc1FbIAnbbs= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= +github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= +github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ= +github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= +github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= +github.com/go-openapi/strfmt v0.20.2/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk= +github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= +github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M= +github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= +github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8= +github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4= +github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI= +github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= +github.com/go-openapi/validate v0.20.3 h1:GZPPhhKSZrE8HjB4eEkoYAZmoWA4+tCemSgINH1/vKw= +github.com/go-openapi/validate v0.20.3/go.mod h1:goDdqVGiigM3jChcrYJxD2joalke3ZXeftD16byIjA4= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.3.2/go.mod h1:93elRVdDhpUgox9GnXswWK5dzpVBQsnlQjnnncSLoiU= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -197,6 +337,7 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -205,10 +346,17 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -218,19 +366,25 @@ github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PH github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexDMMOV0= @@ -238,12 +392,15 @@ github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrD github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -253,10 +410,17 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo= github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -278,9 +442,14 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -307,6 +476,12 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= @@ -314,6 +489,7 @@ github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI= github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -326,10 +502,14 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -337,17 +517,27 @@ github.com/shirou/gopsutil/v3 v3.23.1 h1:a9KKO+kGLKEvcPIs4W62v0nu3sciVDOOOPUD0Hz github.com/shirou/gopsutil/v3 v3.23.1/go.mod h1:NN6mnm5/0k8jw4cBfCnJtr5L7ErOTg18tMNpgFkn0hA= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0= github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.4/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -367,15 +557,21 @@ github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+Kd github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -386,6 +582,7 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofm github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -394,16 +591,35 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= +go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.mongodb.org/mongo-driver v1.8.2/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8= go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20221205180719-3fd0dac74452 h1:JZtNuL6LPB+scU5yaQ6hqRlJFRiddZm2FwRt2AQqtHA= go.starlark.net v0.0.0-20221205180719-3fd0dac74452/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -434,20 +650,32 @@ golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= @@ -470,9 +698,17 @@ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -486,6 +722,7 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -509,6 +746,7 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -520,6 +758,7 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -529,11 +768,18 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190603152906-08e0b306e832/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200526224456-8b020aee10d2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -591,15 +837,18 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/cobraext/flags.go b/internal/cobraext/flags.go index 6d41722dd..4707f019b 100644 --- a/internal/cobraext/flags.go +++ b/internal/cobraext/flags.go @@ -129,6 +129,9 @@ const ( TLSSkipVerifyFlagName = "tls-skip-verify" TLSSkipVerifyFlagDescription = "skip TLS verify" + StackProviderFlagName = "provider" + StackProviderFlagDescription = "service provider to start a stack (%s)" + StackServicesFlagName = "services" StackServicesFlagDescription = "component services (comma-separated values: \"%s\")" diff --git a/internal/configuration/locations/locations.go b/internal/configuration/locations/locations.go index 6c912019e..e6252e75a 100644 --- a/internal/configuration/locations/locations.go +++ b/internal/configuration/locations/locations.go @@ -24,20 +24,14 @@ const ( deployerDir = "deployer" fieldsCachedDir = "cache/fields" - - terraformDeployerYmlFile = "terraform-deployer.yml" - - dockerCustomAgentDeployerYmlFile = "docker-custom-agent-base.yml" ) var ( // elasticPackageDataHome is the name of the environment variable used to override data folder for elastic-package elasticPackageDataHome = environment.WithElasticPackagePrefix("DATA_HOME") - serviceLogsDir = filepath.Join(temporaryDir, "service_logs") - kubernetesDeployerDir = filepath.Join(deployerDir, "kubernetes") - terraformDeployerDir = filepath.Join(deployerDir, "terraform") - dockerCustomAgentDeployerDir = filepath.Join(deployerDir, "docker_custom_agent") + serviceLogsDir = filepath.Join(temporaryDir, "service_logs") + kubernetesDeployerDir = filepath.Join(deployerDir, "kubernetes") ) // LocationManager maintains an instance of a config path location @@ -91,26 +85,6 @@ func (loc LocationManager) KubernetesDeployerDir() string { return filepath.Join(loc.stackPath, kubernetesDeployerDir) } -// TerraformDeployerDir returns the Terraform Directory -func (loc LocationManager) TerraformDeployerDir() string { - return filepath.Join(loc.stackPath, terraformDeployerDir) -} - -// TerraformDeployerYml returns the Terraform deployer yml file -func (loc LocationManager) TerraformDeployerYml() string { - return filepath.Join(loc.stackPath, terraformDeployerDir, terraformDeployerYmlFile) -} - -// DockerCustomAgentDeployerDir returns the DockerCustomAgent Directory -func (loc LocationManager) DockerCustomAgentDeployerDir() string { - return filepath.Join(loc.stackPath, dockerCustomAgentDeployerDir) -} - -// DockerCustomAgentDeployerYml returns the DockerCustomAgent deployer yml file -func (loc LocationManager) DockerCustomAgentDeployerYml() string { - return filepath.Join(loc.stackPath, dockerCustomAgentDeployerDir, dockerCustomAgentDeployerYmlFile) -} - // ServiceLogDir returns the log directory func (loc LocationManager) ServiceLogDir() string { return filepath.Join(loc.stackPath, serviceLogsDir) diff --git a/internal/install/application_configuration.go b/internal/install/application_configuration.go index e850c35f0..5213204aa 100644 --- a/internal/install/application_configuration.go +++ b/internal/install/application_configuration.go @@ -14,26 +14,61 @@ import ( "gopkg.in/yaml.v3" "github.com/elastic/elastic-package/internal/configuration/locations" + "github.com/elastic/elastic-package/internal/environment" "github.com/elastic/elastic-package/internal/logger" + "github.com/elastic/elastic-package/internal/profile" ) const ( stackVersion715 = "7.15.0-SNAPSHOT" stackVersion820 = "8.2.0-SNAPSHOT" + + elasticAgentImageName = "docker.elastic.co/beats/elastic-agent" + elasticAgentCompleteLegacyImageName = "docker.elastic.co/beats/elastic-agent-complete" + elasticAgentCompleteImageName = "docker.elastic.co/elastic-agent/elastic-agent-complete" + elasticsearchImageName = "docker.elastic.co/elasticsearch/elasticsearch" + kibanaImageName = "docker.elastic.co/kibana/kibana" + + applicationConfigurationYmlFile = "config.yml" ) var ( elasticAgentCompleteFirstSupportedVersion = semver.MustParse(stackVersion715) elasticAgentCompleteOwnNamespaceVersion = semver.MustParse(stackVersion820) + + // ProfileNameEnvVar is the name of the environment variable to set the default profile + ProfileNameEnvVar = environment.WithElasticPackagePrefix("PROFILE") ) +func DefaultConfiguration() *ApplicationConfiguration { + config := ApplicationConfiguration{} + config.c.Profile.Current = profile.DefaultProfile + + // Uncomment and use the commented definition of "stack" in case of emergency + // to define Docker image overrides (stack.image_ref_overrides). + // The following sample defines overrides for the Elastic stack ver. 7.13.0-SNAPSHOT. + // It's advised to use latest stable snapshots for the stack snapshot. + // + // config.c.Stack.ImageRefOverrides = map[string]ImageRefs{ + // "7.13.0-SNAPSHOT": ImageRefs{ + // ElasticAgent: elasticAgentImageName + `@sha256:76c294cf55654bc28dde72ce936032f34ad5f40c345f3df964924778b249e581`, + // Kibana: kibanaImageName + `@sha256:78ae3b1ca09efee242d2c77597dfab18670e984adb96c2407ec03fe07ceca4f6`, + // }, + // } + + return &config +} + // ApplicationConfiguration represents the configuration of the elastic-package. type ApplicationConfiguration struct { c configFile } type configFile struct { - Stack stack `yaml:"stack"` + Stack stack `yaml:"stack"` + Profile struct { + Current string `yaml:"current"` + } `yaml:"profile"` } type stack struct { @@ -79,6 +114,24 @@ func (ac *ApplicationConfiguration) StackImageRefs(version string) ImageRefs { return refs } +// CurrentProfile returns the current profile, or the default one if not set. +func (ac *ApplicationConfiguration) CurrentProfile() string { + fromEnv := os.Getenv(ProfileNameEnvVar) + if fromEnv != "" { + return fromEnv + } + current := ac.c.Profile.Current + if current == "" { + return profile.DefaultProfile + } + return current +} + +// SetCurrentProfile sets the current profile. +func (ac *ApplicationConfiguration) SetCurrentProfile(name string) { + ac.c.Profile.Current = name +} + // selectElasticAgentImageName function returns the appropriate image name for Elastic-Agent depending on the stack version. // This is mandatory as "elastic-agent-complete" is available since 7.15.0-SNAPSHOT. func selectElasticAgentImageName(version string) string { @@ -108,6 +161,9 @@ func Configuration() (*ApplicationConfiguration, error) { } cfg, err := os.ReadFile(filepath.Join(configPath.RootDir(), applicationConfigurationYmlFile)) + if errors.Is(err, os.ErrNotExist) { + return DefaultConfiguration(), nil + } if err != nil { return nil, errors.Wrap(err, "can't read configuration file") } diff --git a/internal/install/application_configuration_yml.go b/internal/install/application_configuration_yml.go deleted file mode 100644 index dbc6811ac..000000000 --- a/internal/install/application_configuration_yml.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -// or more contributor license agreements. Licensed under the Elastic License; -// you may not use this file except in compliance with the Elastic License. - -package install - -const ( - elasticAgentImageName = "docker.elastic.co/beats/elastic-agent" - elasticAgentCompleteLegacyImageName = "docker.elastic.co/beats/elastic-agent-complete" - elasticAgentCompleteImageName = "docker.elastic.co/elastic-agent/elastic-agent-complete" - elasticsearchImageName = "docker.elastic.co/elasticsearch/elasticsearch" - kibanaImageName = "docker.elastic.co/kibana/kibana" -) - -const applicationConfigurationYmlFile = "config.yml" - -/* - -Uncomment and use the commented definition of "stack" in case of emergency to define Docker image overrides -(stack.image_ref_overrides). The following sample defines overrides for the Elastic stack ver. 7.13.0-SNAPSHOT. -It's advised to use latest stable snapshots for the stack snapshot. - -const applicationConfigurationYml = `stack: - image_ref_overrides: - 7.13.0-SNAPSHOT: - # Use stable image versions for Agent and Kibana - elastic-agent: ` + elasticAgentImageName + `@sha256:76c294cf55654bc28dde72ce936032f34ad5f40c345f3df964924778b249e581 - kibana: ` + kibanaImageName + `@sha256:78ae3b1ca09efee242d2c77597dfab18670e984adb96c2407ec03fe07ceca4f6` -*/ - -const applicationConfigurationYml = `stack: - image_ref_overrides: -` diff --git a/internal/install/install.go b/internal/install/install.go index 23eddf83e..814ba072b 100644 --- a/internal/install/install.go +++ b/internal/install/install.go @@ -8,18 +8,17 @@ import ( "fmt" "os" "path/filepath" - "time" "github.com/pkg/errors" + "gopkg.in/yaml.v3" "github.com/elastic/elastic-package/internal/configuration/locations" - "github.com/elastic/elastic-package/internal/logger" "github.com/elastic/elastic-package/internal/profile" ) const versionFilename = "version" -// EnsureInstalled method installs once static resources for the testing Docker stack. +// EnsureInstalled method installs once the required configuration files. func EnsureInstalled() error { elasticPackagePath, err := locations.NewLocationManager() if err != nil { @@ -31,45 +30,43 @@ func EnsureInstalled() error { return errors.Wrap(err, "failed to check if there is an elastic-package installation") } if installed { - return nil - } - - err = migrateIfNeeded(elasticPackagePath) - if err != nil { - return errors.Wrap(err, "error migrating old install") + latestInstalled, err := checkIfLatestVersionInstalled(elasticPackagePath) + if err != nil { + return errors.Wrap(err, "failed to check if latest version is installed") + } + if latestInstalled { + return nil + } + return migrateConfigDirectory(elasticPackagePath) } - // Create the root .elastic-package path + // Create the root .elastic-package path. err = createElasticPackageDirectory(elasticPackagePath) if err != nil { return errors.Wrap(err, "creating elastic package directory failed") } - // write the root config.yml file - err = writeConfigFile(elasticPackagePath) + // Write the root config.yml file. + err = WriteConfigFile(elasticPackagePath, DefaultConfiguration()) if err != nil { return errors.Wrap(err, "writing configuration file failed") } - // write root version file + // Write root version file. err = writeVersionFile(elasticPackagePath) if err != nil { return errors.Wrap(err, "writing version file failed") } - err = writeStackResources(elasticPackagePath) - if err != nil { - return errors.Wrap(err, "writing stack resources failed") - } - - err = writeTerraformDeployerResources(elasticPackagePath) - if err != nil { - return errors.Wrap(err, "writing Terraform deployer resources failed") + // Create initial profile: + options := profile.Options{ + PackagePath: elasticPackagePath.ProfileDir(), + Name: profile.DefaultProfile, + OverwriteExisting: false, } - - err = writeDockerCustomAgentResources(elasticPackagePath) + err = profile.CreateProfile(options) if err != nil { - return errors.Wrap(err, "writing Terraform deployer resources failed") + return errors.Wrap(err, "creation of initial profile failed") } if err := createServiceLogsDir(elasticPackagePath); err != nil { @@ -81,55 +78,14 @@ func EnsureInstalled() error { } func checkIfAlreadyInstalled(elasticPackagePath *locations.LocationManager) (bool, error) { - _, err := os.Stat(elasticPackagePath.StackDir()) + _, err := os.Stat(elasticPackagePath.RootDir()) if errors.Is(err, os.ErrNotExist) { return false, nil } if err != nil { return false, errors.Wrapf(err, "stat file failed (path: %s)", elasticPackagePath) } - return checkIfLatestVersionInstalled(elasticPackagePath) -} - -// checkIfUnmigrated checks to see if we have a pre-profile config that needs to be migrated -func migrateIfNeeded(elasticPackagePath *locations.LocationManager) error { - // use the snapshot.yml file as a canary to see if we have a pre-profile install - _, err := os.Stat(filepath.Join(elasticPackagePath.StackDir(), string(profile.SnapshotFile))) - if errors.Is(err, os.ErrNotExist) { - return nil - } - if err != nil { - return errors.Wrapf(err, "stat file failed (path: %s)", elasticPackagePath) - } - - profileName := fmt.Sprintf("default_migrated_%d", time.Now().Unix()) - logger.Warnf("Pre-profiles elastic-package detected. Existing config will be migrated to %s", profileName) - // Depending on how old the install is, not all the files will be available to migrate, - // So treat any errors from missing files as "soft" - oldFiles := []string{ - filepath.Join(elasticPackagePath.StackDir(), string(profile.SnapshotFile)), - filepath.Join(elasticPackagePath.StackDir(), string(profile.PackageRegistryDockerfileFile)), - filepath.Join(elasticPackagePath.StackDir(), string(profile.KibanaConfigDefaultFile)), - filepath.Join(elasticPackagePath.StackDir(), string(profile.PackageRegistryConfigFile)), - } - - opts := profile.Options{ - PackagePath: elasticPackagePath.StackDir(), - Name: profileName, - } - err = profile.MigrateProfileFiles(opts, oldFiles) - if err != nil { - return errors.Wrap(err, "error migrating profile config") - } - - // delete the old files - for _, file := range oldFiles { - err = os.Remove(file) - if err != nil { - return errors.Wrapf(err, "error removing config file %s", file) - } - } - return nil + return true, nil } func createElasticPackageDirectory(elasticPackagePath *locations.LocationManager) error { @@ -151,107 +107,37 @@ func createElasticPackageDirectory(elasticPackagePath *locations.LocationManager return nil } -func writeStackResources(elasticPackagePath *locations.LocationManager) error { - err := os.MkdirAll(elasticPackagePath.PackagesDir(), 0755) - if err != nil { - return errors.Wrapf(err, "creating directory failed (path: %s)", elasticPackagePath.PackagesDir()) - } - - err = os.MkdirAll(elasticPackagePath.ProfileDir(), 0755) - if err != nil { - return errors.Wrapf(err, "creating directory failed (path: %s)", elasticPackagePath.PackagesDir()) - } - - kibanaHealthcheckPath := filepath.Join(elasticPackagePath.StackDir(), "healthcheck.sh") - err = writeStaticResource(err, kibanaHealthcheckPath, kibanaHealthcheckSh) - if err != nil { - return errors.Wrapf(err, "copying healthcheck script failed (%s)", kibanaHealthcheckPath) - } - - // Install GeoIP database - ingestGeoIPDir := filepath.Join(elasticPackagePath.StackDir(), "ingest-geoip") - err = os.MkdirAll(ingestGeoIPDir, 0755) - if err != nil { - return errors.Wrapf(err, "creating directory failed (path: %s)", ingestGeoIPDir) - } - - geoIpAsnMmdbPath := filepath.Join(ingestGeoIPDir, "GeoLite2-ASN.mmdb") - err = writeStaticResource(err, geoIpAsnMmdbPath, geoIpAsnMmdb) - if err != nil { - return errors.Wrapf(err, "copying GeoIP ASN database failed (%s)", geoIpAsnMmdbPath) - } - - geoIpCityMmdbPath := filepath.Join(ingestGeoIPDir, "GeoLite2-City.mmdb") - err = writeStaticResource(err, geoIpCityMmdbPath, geoIpCityMmdb) - if err != nil { - return errors.Wrapf(err, "copying GeoIP city database failed (%s)", geoIpCityMmdbPath) - } - - geoIpCountryMmdbPath := filepath.Join(ingestGeoIPDir, "GeoLite2-Country.mmdb") - err = writeStaticResource(err, geoIpCountryMmdbPath, geoIpCountryMmdb) - if err != nil { - return errors.Wrapf(err, "copying GeoIP country database failed (%s)", geoIpCountryMmdbPath) - } - - serviceTokensPath := filepath.Join(elasticPackagePath.StackDir(), "service_tokens") - err = writeStaticResource(err, serviceTokensPath, serviceTokens) - if err != nil { - return errors.Wrapf(err, "copying service_tokens failed (%s)", serviceTokensPath) - } - - options := profile.Options{ - PackagePath: elasticPackagePath.ProfileDir(), - Name: profile.DefaultProfile, - OverwriteExisting: false, - } - return profile.CreateProfile(options) -} - -func writeTerraformDeployerResources(elasticPackagePath *locations.LocationManager) error { - terraformDeployer := elasticPackagePath.TerraformDeployerDir() - err := os.MkdirAll(terraformDeployer, 0755) +func WriteConfigFile(elasticPackagePath *locations.LocationManager, configuration *ApplicationConfiguration) error { + d, err := yaml.Marshal(configuration.c) if err != nil { - return errors.Wrapf(err, "creating directory failed (path: %s)", terraformDeployer) + return errors.Wrap(err, "failed to encode configuration") } - err = writeStaticResource(err, elasticPackagePath.TerraformDeployerYml(), terraformDeployerYml) - err = writeStaticResource(err, filepath.Join(terraformDeployer, "Dockerfile"), terraformDeployerDockerfile) - err = writeStaticResource(err, filepath.Join(terraformDeployer, "run.sh"), terraformDeployerRun) + err = writeStaticResource(err, filepath.Join(elasticPackagePath.RootDir(), applicationConfigurationYmlFile), string(d)) if err != nil { return errors.Wrap(err, "writing static resource failed") } return nil } -func writeDockerCustomAgentResources(elasticPackagePath *locations.LocationManager) error { - dir := elasticPackagePath.DockerCustomAgentDeployerDir() - if err := os.MkdirAll(dir, 0755); err != nil { - return errors.Wrapf(err, "creating directory failed (path: %s)", dir) - } - if err := writeStaticResource(nil, elasticPackagePath.DockerCustomAgentDeployerYml(), dockerCustomAgentBaseYml); err != nil { - return errors.Wrap(err, "writing static resource failed") +func writeStaticResource(err error, path, content string) error { + if err != nil { + return err } - return nil -} -func writeConfigFile(elasticPackagePath *locations.LocationManager) error { - var err error - err = writeStaticResource(err, filepath.Join(elasticPackagePath.RootDir(), applicationConfigurationYmlFile), applicationConfigurationYml) + err = os.WriteFile(path, []byte(content), 0644) if err != nil { - return errors.Wrap(err, "writing static resource failed") + return errors.Wrapf(err, "writing file failed (path: %s)", path) } return nil } -func writeStaticResource(err error, path, content string) error { +func migrateConfigDirectory(elasticPackagePath *locations.LocationManager) error { + err := writeVersionFile(elasticPackagePath) if err != nil { - return err + return errors.Wrap(err, "writing version file failed") } - err = os.WriteFile(path, []byte(content), 0644) - if err != nil { - return errors.Wrapf(err, "writing file failed (path: %s)", path) - } return nil } diff --git a/internal/install/static.go b/internal/install/static.go deleted file mode 100644 index fb6b6d40c..000000000 --- a/internal/install/static.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -// or more contributor license agreements. Licensed under the Elastic License; -// you may not use this file except in compliance with the Elastic License. - -package install - -import _ "embed" - -//go:embed _static/kibana_healthcheck.sh -var kibanaHealthcheckSh string - -//go:embed _static/Dockerfile.terraform_deployer -var terraformDeployerDockerfile string - -//go:embed _static/terraform_deployer.yml -var terraformDeployerYml string - -//go:embed _static/terraform_deployer_run.sh -var terraformDeployerRun string - -//go:embed _static/GeoLite2-ASN.mmdb -var geoIpAsnMmdb string - -//go:embed _static/GeoLite2-City.mmdb -var geoIpCityMmdb string - -//go:embed _static/GeoLite2-Country.mmdb -var geoIpCountryMmdb string - -//go:embed _static/service_tokens -var serviceTokens string - -//go:embed _static/docker-custom-agent-base.yml -var dockerCustomAgentBaseYml string diff --git a/internal/profile/_static/elastic-agent_default.env b/internal/profile/_static/elastic-agent_default.env deleted file mode 100644 index f0375c615..000000000 --- a/internal/profile/_static/elastic-agent_default.env +++ /dev/null @@ -1,4 +0,0 @@ -FLEET_ENROLL=1 -FLEET_URL=https://fleet-server:8220 -KIBANA_FLEET_HOST=https://kibana:5601 -KIBANA_HOST=https://kibana:5601 diff --git a/internal/profile/_static/elasticsearch_config_8x.yml b/internal/profile/_static/elasticsearch_config_8x.yml deleted file mode 100644 index f36f2b248..000000000 --- a/internal/profile/_static/elasticsearch_config_8x.yml +++ /dev/null @@ -1,15 +0,0 @@ -network.host: "" -transport.host: "127.0.0.1" -http.host: "0.0.0.0" - -indices.id_field_data.enabled: true - -xpack.license.self_generated.type: "trial" -xpack.security.enabled: true -xpack.security.authc.api_key.enabled: true - -xpack.security.http.ssl.enabled: true -xpack.security.http.ssl.key: "certs/key.pem" -xpack.security.http.ssl.certificate: "certs/cert.pem" - -ingest.geoip.downloader.enabled: false diff --git a/internal/profile/_static/kibana_config_80.yml b/internal/profile/_static/kibana_config_80.yml deleted file mode 100644 index 4e3c72ec0..000000000 --- a/internal/profile/_static/kibana_config_80.yml +++ /dev/null @@ -1,59 +0,0 @@ -server.name: kibana -server.host: "0.0.0.0" -server.ssl.enabled: true -server.ssl.certificate: "/usr/share/kibana/config/certs/cert.pem" -server.ssl.key: "/usr/share/kibana/config/certs/key.pem" -server.ssl.certificateAuthorities: ["/usr/share/kibana/config/certs/ca-cert.pem"] - -elasticsearch.hosts: [ "https://elasticsearch:9200" ] -elasticsearch.ssl.certificateAuthorities: "/usr/share/kibana/config/certs/ca-cert.pem" -elasticsearch.serviceAccountToken: "AAEAAWVsYXN0aWMva2liYW5hL2VsYXN0aWMtcGFja2FnZS1raWJhbmEtdG9rZW46b2x4b051SWNRa0tYMHdXazdLWmFBdw" - -monitoring.ui.container.elasticsearch.enabled: true - -xpack.fleet.registryUrl: "https://package-registry:8080" -xpack.fleet.agents.enabled: true -xpack.fleet.agents.elasticsearch.hosts: ["https://elasticsearch:9200"] -xpack.fleet.agents.fleet_server.hosts: ["https://fleet-server:8220"] - -xpack.encryptedSavedObjects.encryptionKey: "12345678901234567890123456789012" - -xpack.fleet.packages: - - name: system - version: latest - - name: elastic_agent - version: latest - - name: fleet_server - version: latest -xpack.fleet.agentPolicies: - - name: Elastic-Agent (elastic-package) - id: elastic-agent-managed-ep - is_default: true - is_managed: false - namespace: default - monitoring_enabled: - - logs - - metrics - package_policies: - - name: system-1 - id: default-system - package: - name: system - - name: Fleet Server (elastic-package) - id: fleet-server-policy - is_default_fleet_server: true - is_managed: false - namespace: default - package_policies: - - name: fleet_server-1 - id: default-fleet-server - package: - name: fleet_server -xpack.fleet.outputs: - - id: fleet-default-output - name: default - type: elasticsearch - hosts: [ https://elasticsearch:9200 ] - ca_trusted_fingerprint: "${ELASTIC_PACKAGE_CA_TRUSTED_FINGERPRINT}" - is_default: true - is_default_monitoring: true diff --git a/internal/profile/_static/kibana_config_86.yml b/internal/profile/_static/kibana_config_86.yml deleted file mode 100644 index 6d9564c00..000000000 --- a/internal/profile/_static/kibana_config_86.yml +++ /dev/null @@ -1,61 +0,0 @@ -server.name: kibana -server.host: "0.0.0.0" -server.ssl.enabled: true -server.ssl.certificate: "/usr/share/kibana/config/certs/cert.pem" -server.ssl.key: "/usr/share/kibana/config/certs/key.pem" -server.ssl.certificateAuthorities: ["/usr/share/kibana/config/certs/ca-cert.pem"] - -elasticsearch.hosts: [ "https://elasticsearch:9200" ] -elasticsearch.ssl.certificateAuthorities: "/usr/share/kibana/config/certs/ca-cert.pem" -elasticsearch.serviceAccountToken: "AAEAAWVsYXN0aWMva2liYW5hL2VsYXN0aWMtcGFja2FnZS1raWJhbmEtdG9rZW46b2x4b051SWNRa0tYMHdXazdLWmFBdw" - -monitoring.ui.container.elasticsearch.enabled: true - -xpack.fleet.registryUrl: "https://package-registry:8080" -xpack.fleet.agents.enabled: true -xpack.fleet.agents.elasticsearch.hosts: ["https://elasticsearch:9200"] -xpack.fleet.agents.fleet_server.hosts: ["https://fleet-server:8220"] - -xpack.encryptedSavedObjects.encryptionKey: "12345678901234567890123456789012" - -xpack.cloudSecurityPosture.enabled: true - -xpack.fleet.packages: - - name: system - version: latest - - name: elastic_agent - version: latest - - name: fleet_server - version: latest -xpack.fleet.agentPolicies: - - name: Elastic-Agent (elastic-package) - id: elastic-agent-managed-ep - is_default: true - is_managed: false - namespace: default - monitoring_enabled: - - logs - - metrics - package_policies: - - name: system-1 - id: default-system - package: - name: system - - name: Fleet Server (elastic-package) - id: fleet-server-policy - is_default_fleet_server: true - is_managed: false - namespace: default - package_policies: - - name: fleet_server-1 - id: default-fleet-server - package: - name: fleet_server -xpack.fleet.outputs: - - id: fleet-default-output - name: default - type: elasticsearch - hosts: [ https://elasticsearch:9200 ] - ca_trusted_fingerprint: "${ELASTIC_PACKAGE_CA_TRUSTED_FINGERPRINT}" - is_default: true - is_default_monitoring: true diff --git a/internal/profile/_static/kibana_config_default.yml b/internal/profile/_static/kibana_config_default.yml deleted file mode 100644 index 124f0ff74..000000000 --- a/internal/profile/_static/kibana_config_default.yml +++ /dev/null @@ -1,21 +0,0 @@ -server.name: kibana -server.host: "0.0.0.0" -server.ssl.enabled: true -server.ssl.certificate: "/usr/share/kibana/config/certs/cert.pem" -server.ssl.key: "/usr/share/kibana/config/certs/key.pem" -server.ssl.certificateAuthorities: ["/usr/share/kibana/config/certs/ca-cert.pem"] - -elasticsearch.hosts: [ "https://elasticsearch:9200" ] -elasticsearch.ssl.certificateAuthorities: "/usr/share/kibana/config/certs/ca-cert.pem" -elasticsearch.username: elastic -elasticsearch.password: changeme - -xpack.monitoring.ui.container.elasticsearch.enabled: true - -xpack.fleet.enabled: true -xpack.fleet.registryUrl: "https://package-registry:8080" -xpack.fleet.agents.enabled: true -xpack.fleet.agents.elasticsearch.host: "https://elasticsearch:9200" -xpack.fleet.agents.fleet_server.hosts: ["https://fleet-server:8220"] - -xpack.encryptedSavedObjects.encryptionKey: "12345678901234567890123456789012" diff --git a/internal/profile/config_profile_test.go b/internal/profile/config_profile_test.go deleted file mode 100644 index c1f00d967..000000000 --- a/internal/profile/config_profile_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -// or more contributor license agreements. Licensed under the Elastic License; -// you may not use this file except in compliance with the Elastic License. -package profile - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" -) - -const ( - profileName = "test_profile" -) - -func TestNewProfile(t *testing.T) { - elasticPackageDir, err := os.MkdirTemp("", "package") - defer cleanupProfile(t, elasticPackageDir) - assert.NoError(t, err, "error creating tempdir") - t.Logf("writing to directory %s", elasticPackageDir) - - options := Options{ - PackagePath: elasticPackageDir, - Name: profileName, - OverwriteExisting: false, - } - err = createProfile(options) - assert.NoErrorf(t, err, "error creating profile %s", err) - -} - -func TestNewProfileFrom(t *testing.T) { - elasticPackageDir, err := os.MkdirTemp("", "package") - defer cleanupProfile(t, elasticPackageDir) - assert.NoError(t, err, "error creating tempdir") - t.Logf("writing to directory %s", elasticPackageDir) - - options := Options{ - PackagePath: elasticPackageDir, - Name: profileName, - OverwriteExisting: false, - } - err = createProfile(options) - assert.NoErrorf(t, err, "error creating profile %s", err) - - //update the profile to make sure we're properly copying everything - - testProfile, err := NewConfigProfile(elasticPackageDir, profileName) - assert.NoErrorf(t, err, "error creating profile %s", err) - - pkgRegUpdated := &simpleFile{ - path: filepath.Join(testProfile.ProfilePath, string(PackageRegistryConfigFile)), - body: `package_paths: - - /packages/testing - - /packages/development - - /packages/production - - /packages/staging - - /packages/snapshot - `, - } - t.Logf("updating profile %s", testProfile.ProfilePath) - testProfile.configFiles[PackageRegistryConfigFile] = pkgRegUpdated - err = testProfile.writeProfileResources() - assert.NoErrorf(t, err, "error updating profile %s", err) - - // actually create & check the new profile - option := Options{ - PackagePath: elasticPackageDir, - Name: "test_from", - FromProfile: profileName, - } - err = createProfileFrom(option) - assert.NoErrorf(t, err, "error copying updating profile %s", err) - -} - -func cleanupProfile(t *testing.T, dir string) { - err := os.RemoveAll(dir) - assert.NoErrorf(t, err, "Error cleaning up tempdir %s", dir) -} diff --git a/internal/profile/files.go b/internal/profile/files.go deleted file mode 100644 index c115fd3e9..000000000 --- a/internal/profile/files.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -// or more contributor license agreements. Licensed under the Elastic License; -// you may not use this file except in compliance with the Elastic License. - -package profile - -import ( - "os" - "path/filepath" - - "github.com/pkg/errors" -) - -// NewConfig is a generic function type to return a new Managed config -type NewConfig = func(profileName string, profilePath string) (*simpleFile, error) - -// simpleFile defines a file that's managed by the profile system -// and doesn't require any rendering -type simpleFile struct { - name string - path string - body string -} - -const profileStackPath = "stack" - -// configFilesDiffer checks to see if a local configItem differs from the one it knows. -func (cfg simpleFile) configFilesDiffer() (bool, error) { - changes, err := os.ReadFile(cfg.path) - if err != nil && errors.Is(err, os.ErrNotExist) { - return false, nil - } - if err != nil { - return false, errors.Wrapf(err, "error reading %s", cfg.path) - } - if string(changes) == cfg.body { - return false, nil - } - return true, nil -} - -// writeConfig writes the config item -func (cfg simpleFile) writeConfig() error { - err := os.MkdirAll(filepath.Dir(cfg.path), 0755) - if err != nil { - return errors.Wrapf(err, "creating parent directories for file failed (path: %s)", cfg.path) - } - err = os.WriteFile(cfg.path, []byte(cfg.body), 0644) - if err != nil { - return errors.Wrapf(err, "writing file failed (path: %s)", cfg.path) - } - return nil -} - -// readConfig reads the config item, overwriting whatever exists in the fileBody. -func (cfg *simpleFile) readConfig() error { - body, err := os.ReadFile(cfg.path) - if err != nil { - return errors.Wrapf(err, "reading file failed (path: %s)", cfg.path) - } - cfg.body = string(body) - return nil -} diff --git a/internal/profile/install_profile.go b/internal/profile/install_profile.go deleted file mode 100644 index eb13dfc16..000000000 --- a/internal/profile/install_profile.go +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -// or more contributor license agreements. Licensed under the Elastic License; -// you may not use this file except in compliance with the Elastic License. - -package profile - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/pkg/errors" - - "github.com/elastic/elastic-package/internal/configuration/locations" - "github.com/elastic/elastic-package/internal/logger" -) - -// CreateProfile creates an existing profile from the default elastic-package config dir -// if option.Package has a value it'll be used, if not, the default location will be used -// if option.From does not have a supplied value, it'll create a default profile. -func CreateProfile(options Options) error { - if options.PackagePath == "" { - loc, err := locations.NewLocationManager() - if err != nil { - return errors.Wrap(err, "error finding stack dir location") - } - options.PackagePath = loc.ProfileDir() - } - - // If they're creating from Default, assume they want the actual default, and - // not whatever is currently inside default. - if options.FromProfile == "" || options.FromProfile == DefaultProfile { - return createProfile(options) - } - - return createProfileFrom(options) -} - -// MigrateProfileFiles creates a new profile based on existing filepaths -// that are stored elsewhere outside the profile system. -func MigrateProfileFiles(options Options, files []string) error { - profile, err := newProfileFromExistingFiles(options.PackagePath, options.Name, files, true) - if err != nil { - return errors.Wrap(err, "error creating new profile from files") - } - err = os.Mkdir(profile.ProfilePath, 0755) - if err != nil { - return errors.Wrapf(err, "error crating profile directory %s", profile.ProfilePath) - } - err = profile.writeProfileResources() - if err != nil { - return errors.Wrap(err, "error writing out new profile config") - } - return nil -} - -// LoadProfile loads an existing profile from the default elastic-package config dir -func LoadProfile(profileName string) (*Profile, error) { - loc, err := locations.NewLocationManager() - if err != nil { - return nil, errors.Wrap(err, "error finding stack dir location") - } - - return loadProfile(loc.ProfileDir(), profileName) -} - -// DeleteProfile deletes a profile from the default elastic-package config dir -func DeleteProfile(profileName string) error { - loc, err := locations.NewLocationManager() - if err != nil { - return errors.Wrap(err, "error finding stack dir location") - } - return deleteProfile(loc.ProfileDir(), profileName) -} - -// FetchAllProfiles returns a list of profile values -func FetchAllProfiles(elasticPackagePath string) ([]Metadata, error) { - dirList, err := os.ReadDir(elasticPackagePath) - if err != nil { - return []Metadata{}, errors.Wrapf(err, "error reading from directory %s", elasticPackagePath) - } - - var profiles []Metadata - // TODO: this should read a profile.json file or something like that - for _, item := range dirList { - if !item.IsDir() { - continue - } - profile, err := loadProfile(elasticPackagePath, item.Name()) - if errors.Is(err, ErrNotAProfile) { - continue - } - if err != nil { - return profiles, errors.Wrapf(err, "error loading profile %s", item.Name()) - } - metadata, err := profile.metadata() - if err != nil { - return profiles, errors.Wrap(err, "error reading profile metadata") - } - profiles = append(profiles, metadata) - } - return profiles, nil -} - -// createProfile installs a new profile at the given package path location. -// overwriteExisting determines the behavior if a profile with the given name already exists. -// On true, it'll overwrite the profile, on false, it'll backup the existing profile to profilename_VERSION-DATE-CREATED -func createProfile(options Options) error { - profile, err := createAndCheckProfile(options.PackagePath, options.Name, options.OverwriteExisting) - if err != nil { - return errors.Wrap(err, "error creating new profile") - } - // write the resources - err = profile.writeProfileResources() - if err != nil { - return errors.Wrap(err, "error writing profile file") - } - return nil -} - -// createProfileFrom creates a new profile by copying over an existing profile -func createProfileFrom(options Options) error { - fromProfile, err := loadProfile(options.PackagePath, options.FromProfile) - if err != nil { - return errors.Wrapf(err, "error loading %s profile", options.FromProfile) - } - - newProfile, err := createAndCheckProfile(options.PackagePath, options.Name, options.OverwriteExisting) - if err != nil { - return errors.Wrap(err, "error creating new profile") - } - - newProfile.overwrite(fromProfile.configFiles) - err = newProfile.writeProfileResources() - if err != nil { - return errors.Wrap(err, "error writing new profile") - } - return nil -} - -// createAndCheckProfile does most of the heavy lifting for initializing a new profile, -// including dealing with profile overwrites -func createAndCheckProfile(packagePath, packageName string, overwriteExisting bool) (*Profile, error) { - profile, err := NewConfigProfile(packagePath, packageName) - if err != nil { - return nil, errors.Wrap(err, "error creating profile") - } - - // check to see if we have an existing profile at that location. - exists, err := profile.alreadyExists() - if err != nil { - return nil, errors.Wrap(err, "error checking for existing profile") - } - if exists { - localChanges, err := profile.localFilesChanged() - if err != nil { - return nil, errors.Wrapf(err, "error checking for changes in %s", profile.ProfilePath) - } - // If there are changes and we've selected CreateNew, move the old path - if localChanges && !overwriteExisting { - if localChanges && packageName == DefaultProfile { - logger.Warn("Default profile has been changed by user or updated by elastic-package. The current profile will be moved.") - } - // Migrate the existing profile - err = updateExistingDefaultProfile(packagePath) - if err != nil { - return nil, errors.Wrap(err, "error moving old profile") - } - err = os.Mkdir(profile.ProfilePath, 0755) - if err != nil { - return nil, errors.Wrapf(err, "error crating profile directory %s", profile.ProfilePath) - } - err = os.Mkdir(profile.ProfileStackPath, 0755) - if err != nil { - return nil, errors.Wrapf(err, "error crating profile directory %s", profile.ProfilePath) - } - } - } else { - err = os.Mkdir(profile.ProfilePath, 0755) - if err != nil { - return nil, errors.Wrapf(err, "error crating profile directory %s", profile.ProfilePath) - } - err = os.Mkdir(profile.ProfileStackPath, 0755) - if err != nil { - return nil, errors.Wrapf(err, "error crating profile directory %s", profile.ProfilePath) - } - } - - return profile, nil -} - -// updateExistingDefaultProfile migrates the old default profile to profile_VERSION_DATE-CREATED -func updateExistingDefaultProfile(path string) error { - profile, err := NewConfigProfile(path, DefaultProfile) - if err != nil { - return errors.Wrap(err, "error creating profile") - } - meta, err := profile.metadata() - if err != nil { - return errors.Wrap(err, "error updating metadata") - } - newName := fmt.Sprintf("default_%s_%d", meta.Version, meta.DateCreated.Unix()) - newFilePath := filepath.Join(filepath.Dir(profile.ProfilePath), newName) - meta.Name = newName - meta.Path = newFilePath - - err = profile.updateMetadata(meta) - if err != nil { - return errors.Wrap(err, "error updating metadata") - } - - err = os.Rename(profile.ProfilePath, newFilePath) - if err != nil { - return errors.Wrap(err, "error moving default profile") - } - return nil -} - -// deleteProfile deletes a given config profile. -func deleteProfile(elasticPackagePath string, profileName string) error { - if profileName == DefaultProfile { - return errors.New("cannot remove default profile") - } - - pathToDelete := filepath.Join(elasticPackagePath, profileName) - - return os.RemoveAll(pathToDelete) - -} diff --git a/internal/profile/profile.go b/internal/profile/profile.go index 73cebeec4..21403b201 100644 --- a/internal/profile/profile.go +++ b/internal/profile/profile.go @@ -5,324 +5,218 @@ package profile import ( - "encoding/json" + "errors" "fmt" "os" "path/filepath" + "strings" - "github.com/pkg/errors" + "github.com/elastic/go-resource" - "github.com/elastic/elastic-package/internal/logger" + "github.com/elastic/elastic-package/internal/configuration/locations" + "github.com/elastic/elastic-package/internal/files" ) -// Profile manages a a given user config profile -type Profile struct { - // ProfilePath is the absolute path to the profile - ProfilePath string - ProfileStackPath string - profileName string - configFiles map[configFile]*simpleFile -} - const ( - // DefaultProfile is the name of the default profile + // PackageProfileMetaFile is the filename of the profile metadata file + PackageProfileMetaFile = "profile.json" + + // DefaultProfile is the name of the default profile. DefaultProfile = "default" ) -// ErrNotAProfile is returned in cases where we don't have a valid profile directory -var ErrNotAProfile = errors.New("not a profile") +var ( + profileResources = []resource.Resource{ + &resource.File{ + Path: PackageProfileMetaFile, + Content: profileMetadataContent, + }, + } +) -// configFile is a type for for the config file names in a managed profile config -type configFile string - -// managedProfileFiles is the list of all files managed in a profile -// If you create a new file that's managed by a profile, it needs to go in this list -var managedProfileFiles = map[configFile]NewConfig{ - ElasticAgentDefaultEnvFile: newElasticAgentDefaultEnv, - ElasticAgent8xEnvFile: newElasticAgent8xEnv, - ElasticAgent86EnvFile: newElasticAgent86Env, - ElasticAgent80EnvFile: newElasticAgent80Env, - ElasticsearchConfigDefaultFile: newElasticsearchConfigDefault, - ElasticsearchConfig8xFile: newElasticsearchConfig8x, - ElasticsearchConfig86File: newElasticsearchConfig86, - ElasticsearchConfig80File: newElasticsearchConfig80, - KibanaConfigDefaultFile: newKibanaConfigDefault, - KibanaConfig8xFile: newKibanaConfig8x, - KibanaConfig86File: newKibanaConfig86, - KibanaConfig80File: newKibanaConfig80, - PackageRegistryDockerfileFile: newPackageRegistryDockerfile, - PackageRegistryConfigFile: newPackageRegistryConfig, - SnapshotFile: newSnapshotFile, - PackageProfileMetaFile: createProfileMetadata, +type Options struct { + PackagePath string + Name string + FromProfile string + OverwriteExisting bool } -// NewConfigProfile creates a new config profile manager -func NewConfigProfile(elasticPackagePath string, profileName string) (*Profile, error) { - profilePath := filepath.Join(elasticPackagePath, profileName) - - var configMap = map[configFile]*simpleFile{} - for fileItem, configInit := range managedProfileFiles { - cfg, err := configInit(profileName, profilePath) +func CreateProfile(options Options) error { + if options.PackagePath == "" { + loc, err := locations.NewLocationManager() if err != nil { - return nil, errors.Wrapf(err, "error initializing config %s", cfg) + return fmt.Errorf("error finding profile dir location: %w", err) } - configMap[fileItem] = cfg - } - - err := initTLSCertificates(profilePath, configMap) - if err != nil { - return nil, errors.Wrap(err, "error initializing TLS certificates") + options.PackagePath = loc.ProfileDir() } - newProfile := &Profile{ - profileName: profileName, - ProfilePath: profilePath, - ProfileStackPath: filepath.Join(profilePath, profileStackPath), - configFiles: configMap, + if options.Name == "" { + options.Name = DefaultProfile } - return newProfile, nil -} -// newProfileFromExistingFiles creates a profile from a list of absolute filepaths -// This can be used when migrating a config from a non-profiles-managed config set -// ignoreMissing will treat non-existant files as soft errors -func newProfileFromExistingFiles(elasticPackagePath string, profileName string, files []string, ignoreMissing bool) (*Profile, error) { - profilePath := filepath.Join(elasticPackagePath, profileName) - var configMap = map[configFile]*simpleFile{} - for _, file := range files { - if ignoreMissing { - // if we're treating missing files as soft errors, - // just continue on ErrNotExist - // If it's another kind of error, we'll pick it up in ReadFile - if _, err := os.Stat(file); errors.Is(err, os.ErrNotExist) { - continue - } + if !options.OverwriteExisting { + _, err := loadProfile(options.PackagePath, options.Name) + if err == nil { + return fmt.Errorf("profile %q already exists", options.Name) } - - byteFile, err := os.ReadFile(file) - if err != nil { - return nil, errors.Wrapf(err, "error reading file %s", file) - } - //format this in the way configFile expects - name := filepath.Base(file) - configMap[configFile(name)] = &simpleFile{ - name: name, - path: filepath.Join(profilePath, name), - body: string(byteFile), + if err != nil && err != ErrNotAProfile { + return fmt.Errorf("failed to check if profile %q exists: %w", options.Name, err) } } - //add metadata file - metadata, err := createProfileMetadata(profileName, profilePath) - if err != nil { - return nil, errors.Wrap(err, "error creating profile metadata") + // If they're creating from Default, assume they want the actual default, and + // not whatever is currently inside default. + if from := options.FromProfile; from != "" && from != DefaultProfile { + return createProfileFrom(options) } - configMap[PackageProfileMetaFile] = metadata - newProfile := &Profile{ - profileName: profileName, - ProfilePath: profilePath, - configFiles: configMap, - } - return newProfile, nil + return createProfile(options, profileResources) } -// loadProfile loads an existing profile -func loadProfile(elasticPackagePath string, profileName string) (*Profile, error) { - profilePath := filepath.Join(elasticPackagePath, profileName) +func createProfile(options Options, resources []resource.Resource) error { + profileDir := filepath.Join(options.PackagePath, options.Name) - isValid, err := isProfileDir(profilePath) - if err != nil { - return nil, errors.Wrapf(err, "error checking profile %s", profileName) - } + resourceManager := resource.NewManager() + resourceManager.AddFacter(resource.StaticFacter{ + "profile_name": options.Name, + "profile_path": profileDir, + }) - if !isValid { - return nil, ErrNotAProfile - } + os.MkdirAll(profileDir, 0755) + resourceManager.RegisterProvider("file", &resource.FileProvider{ + Prefix: profileDir, + }) - var configMap = map[configFile]*simpleFile{} - for fileItem, configInit := range managedProfileFiles { - cfg, err := configInit(profileName, profilePath) - if err != nil { - return nil, errors.Wrapf(err, "error initializing config %s", cfg) - } - configMap[fileItem] = cfg - } - - err = initTLSCertificates(profilePath, configMap) + results, err := resourceManager.Apply(resources) if err != nil { - return nil, errors.Wrap(err, "error initializing TLS certificates") + var errors []string + for _, result := range results { + if err := result.Err(); err != nil { + errors = append(errors, err.Error()) + } + } + return fmt.Errorf("%w: %s", err, strings.Join(errors, ", ")) } - profile := &Profile{ - profileName: profileName, - ProfilePath: profilePath, - ProfileStackPath: filepath.Join(profilePath, profileStackPath), - configFiles: configMap, - } + return nil +} - exists, err := profile.alreadyExists() +func createProfileFrom(options Options) error { + from, err := LoadProfile(options.FromProfile) if err != nil { - return nil, errors.Wrapf(err, "error checking if profile %s exists", profileName) + return fmt.Errorf("failed to load profile to copy %q: %w", options.FromProfile, err) } - if !exists { - return nil, fmt.Errorf("profile %s does not exist", profile.ProfilePath) - } - - err = profile.readProfileResources() + profileDir := filepath.Join(options.PackagePath, options.Name) + err = files.CopyAll(from.ProfilePath, profileDir) if err != nil { - return nil, errors.Wrapf(err, "error reading in profile %s", profileName) + return fmt.Errorf("failed to copy files from profile %q to %q", options.FromProfile, options.Name) } - return profile, nil + overwriteOptions := options + overwriteOptions.OverwriteExisting = true + return createProfile(overwriteOptions, profileResources) +} +// Profile manages a a given user config profile +type Profile struct { + // ProfilePath is the absolute path to the profile + ProfilePath string + ProfileName string } -// FetchPath returns an absolute path to the given file -func (profile Profile) FetchPath(file configFile) string { - return profile.configFiles[file].path +// Path returns an absolute path to the given file +func (profile Profile) Path(names ...string) string { + elems := append([]string{profile.ProfilePath}, names...) + return filepath.Join(elems...) } +// ErrNotAProfile is returned in cases where we don't have a valid profile directory +var ErrNotAProfile = errors.New("not a profile") + // ComposeEnvVars returns a list of environment variables that can be passed // to docker-compose for the sake of filling out paths and names in the snapshot.yml file. func (profile Profile) ComposeEnvVars() []string { return []string{ - fmt.Sprintf("PROFILE_NAME=%s", profile.profileName), - fmt.Sprintf("STACK_PATH=%s", profile.ProfileStackPath), + fmt.Sprintf("PROFILE_NAME=%s", profile.ProfileName), } } -// writeProfileResources writes the config files -func (profile Profile) writeProfileResources() error { - return writeConfigFiles(profile.configFiles) -} - -func writeConfigFiles(configFiles map[configFile]*simpleFile) error { - for _, cfgFiles := range configFiles { - err := cfgFiles.writeConfig() - if err != nil { - return errors.Wrap(err, "error writing config file") - } +// DeleteProfile deletes a profile from the default elastic-package config dir +func DeleteProfile(profileName string) error { + if profileName == DefaultProfile { + return errors.New("cannot remove default profile") } - return nil -} - -// overwrite updates the string contents of the config files -func (profile *Profile) overwrite(newBody map[configFile]*simpleFile) { - for key := range profile.configFiles { - // skip metadata - if key == PackageProfileMetaFile { - continue - } - toReplace, ok := newBody[key] - if ok { - updatedProfile := profile.configFiles[key] - updatedProfile.body = toReplace.body - profile.configFiles[key] = updatedProfile - } + loc, err := locations.NewLocationManager() + if err != nil { + return fmt.Errorf("error finding stack dir location: %w", err) } + pathToDelete := filepath.Join(loc.ProfileDir(), profileName) + return os.RemoveAll(pathToDelete) } -// alreadyExists checks to see if a profile with this name already exists -func (profile Profile) alreadyExists() (bool, error) { - packageMetadata := profile.configFiles[PackageProfileMetaFile] - // We do this in stages to make sure we return the right error. - _, err := os.Stat(profile.ProfilePath) - if errors.Is(err, os.ErrNotExist) { - return false, nil - } - if err != nil { - return false, errors.Wrapf(err, "error checking root directory: %s", packageMetadata.path) - } - - // If the folder exists, check to make sure it's a profile folder - _, err = os.Stat(packageMetadata.path) +// FetchAllProfiles returns a list of profile values +func FetchAllProfiles(elasticPackagePath string) ([]Metadata, error) { + dirList, err := os.ReadDir(elasticPackagePath) if errors.Is(err, os.ErrNotExist) { - return false, ErrNotAProfile + return []Metadata{}, nil } if err != nil { - return false, errors.Wrapf(err, "error checking metadata: %s", packageMetadata.path) - } - - //if it is, see if it has the same profile name - profileInfo, err := profile.metadata() - if err != nil { - return false, errors.Wrap(err, "error reading metadata") + return []Metadata{}, fmt.Errorf("error reading from directory %s: %w", elasticPackagePath, err) } - //TODO: this will break default_old, as we don't update the json - if profileInfo.Name != profile.profileName { - return false, nil - } - - return true, nil -} - -func (profile Profile) localFilesChanged() (bool, error) { - for cfgName, cfgFile := range profile.configFiles { - // skip checking the metadata file - // TODO: in the future, we might want to check version to see if the default profile needs to be updated - if cfgName == PackageProfileMetaFile { + var profiles []Metadata + // TODO: this should read a profile.json file or something like that + for _, item := range dirList { + if !item.IsDir() { continue } - changes, err := cfgFile.configFilesDiffer() - if err != nil { - return false, errors.Wrap(err, "error checking config file") - } - if changes { - return true, nil - } - } - return false, nil -} - -// readProfileResources reads the associated files into the config, as opposed to writing them out. -func (profile Profile) readProfileResources() error { - for _, cfgFile := range profile.configFiles { - err := cfgFile.readConfig() - if errors.Is(err, os.ErrNotExist) { - logger.Debugf("File %s not found while reading profile.", cfgFile.path) + profile, err := loadProfile(elasticPackagePath, item.Name()) + if errors.Is(err, ErrNotAProfile) { continue } if err != nil { - return errors.Wrap(err, "error reading in profile") + return profiles, fmt.Errorf("error loading profile %s: %w", item.Name(), err) } + metadata, err := loadProfileMetadata(filepath.Join(profile.ProfilePath, PackageProfileMetaFile)) + if err != nil { + return profiles, fmt.Errorf("error reading profile metadata: %w", err) + } + profiles = append(profiles, metadata) } - return nil + return profiles, nil } -// metadata returns the metadata struct for the profile -func (profile Profile) metadata() (Metadata, error) { - packageMetadata := profile.configFiles[PackageProfileMetaFile] - rawPackageMetadata, err := os.ReadFile(packageMetadata.path) +// LoadProfile loads an existing profile from the default elastic-package config dir. +func LoadProfile(profileName string) (*Profile, error) { + loc, err := locations.NewLocationManager() if err != nil { - return Metadata{}, errors.Wrap(err, "error reading metadata file") + return nil, fmt.Errorf("error finding stack dir location: %w", err) } - profileInfo := Metadata{} - - err = json.Unmarshal(rawPackageMetadata, &profileInfo) - if err != nil { - return Metadata{}, errors.Wrap(err, "error unmarshalling JSON") - } - return profileInfo, nil + return loadProfile(loc.ProfileDir(), profileName) } -// updateMetadata updates the metadata json file -func (profile *Profile) updateMetadata(meta Metadata) error { - packageMetadata := profile.configFiles[PackageProfileMetaFile] - metaString, err := json.Marshal(meta) +// loadProfile loads an existing profile +func loadProfile(elasticPackagePath string, profileName string) (*Profile, error) { + profilePath := filepath.Join(elasticPackagePath, profileName) + + isValid, err := isProfileDir(profilePath) if err != nil { - return errors.Wrap(err, "error marshalling metadata json") + return nil, fmt.Errorf("error checking profile %q: %w", profileName, err) } - err = os.WriteFile(packageMetadata.path, metaString, 0664) - if err != nil { - return errors.Wrap(err, "error writing metadata file") + if !isValid { + return nil, ErrNotAProfile } - return nil + + profile := Profile{ + ProfileName: profileName, + ProfilePath: profilePath, + } + + return &profile, nil } // isProfileDir checks to see if the given path points to a valid profile @@ -333,7 +227,7 @@ func isProfileDir(path string) (bool, error) { return false, nil } if err != nil { - return false, errors.Wrapf(err, "error stat: %s", metaPath) + return false, fmt.Errorf("error stat: %s: %w", metaPath, err) } return true, nil } diff --git a/internal/profile/profile_json.go b/internal/profile/profile_json.go index 449f051ca..edd142099 100644 --- a/internal/profile/profile_json.go +++ b/internal/profile/profile_json.go @@ -6,12 +6,15 @@ package profile import ( "encoding/json" + "io" + "os" "os/user" - "path/filepath" "time" "github.com/pkg/errors" + "github.com/elastic/go-resource" + "github.com/elastic/elastic-package/internal/version" ) @@ -24,14 +27,21 @@ type Metadata struct { Path string `json:"path"` } -// PackageProfileMetaFile is the filename of the profile metadata file -const PackageProfileMetaFile configFile = "profile.json" - -// createProfileMetadata creates the body of the profile.json file -func createProfileMetadata(profileName string, profilePath string) (*simpleFile, error) { +// profileMetadataContent generates the content of the profile.json file. +func profileMetadataContent(applyCtx resource.Context, w io.Writer) error { currentUser, err := user.Current() if err != nil { - return nil, errors.Wrap(err, "error fetching current user") + return errors.Wrap(err, "error fetching current user") + } + + profileName, found := applyCtx.Fact("profile_name") + if !found { + return errors.New("unknown profile name") + } + + profilePath, found := applyCtx.Fact("profile_path") + if !found { + return errors.New("unknown profile path") } profileData := Metadata{ @@ -42,14 +52,26 @@ func createProfileMetadata(profileName string, profilePath string) (*simpleFile, profilePath, } - jsonRaw, err := json.MarshalIndent(profileData, "", " ") + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + err = enc.Encode(profileData) if err != nil { - return nil, errors.Wrap(err, "error marshalling json") + return errors.Wrap(err, "error marshalling json") } - return &simpleFile{ - name: string(PackageProfileMetaFile), - path: filepath.Join(profilePath, string(PackageProfileMetaFile)), - body: string(jsonRaw), - }, nil + return nil +} + +func loadProfileMetadata(path string) (Metadata, error) { + d, err := os.ReadFile(path) + if err != nil { + return Metadata{}, errors.Wrap(err, "error reading metadata file") + } + + metadata := Metadata{} + err = json.Unmarshal(d, &metadata) + if err != nil { + return Metadata{}, errors.Wrapf(err, "error checking profile metadata file %q", path) + } + return metadata, nil } diff --git a/internal/profile/options.go b/internal/profile/profile_test.go similarity index 54% rename from internal/profile/options.go rename to internal/profile/profile_test.go index 7e3fca4a0..5fc03d5f4 100644 --- a/internal/profile/options.go +++ b/internal/profile/profile_test.go @@ -4,10 +4,16 @@ package profile -// Options defines available stack management options -type Options struct { - PackagePath string - Name string - FromProfile string - OverwriteExisting bool +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCreateProfile(t *testing.T) { + options := Options{ + PackagePath: t.TempDir(), + } + err := CreateProfile(options) + require.NoError(t, err) } diff --git a/internal/profile/static.go b/internal/profile/static.go deleted file mode 100644 index 68b72f0e6..000000000 --- a/internal/profile/static.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -// or more contributor license agreements. Licensed under the Elastic License; -// you may not use this file except in compliance with the Elastic License. - -package profile - -import ( - _ "embed" - "path/filepath" - "strings" -) - -// SnapshotFile is the docker-compose snapshot.yml file name -const SnapshotFile configFile = "snapshot.yml" - -//go:embed _static/docker-compose-stack.yml -var snapshotYml string - -// newSnapshotFile returns a Managed Config -func newSnapshotFile(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(SnapshotFile), - path: filepath.Join(profilePath, profileStackPath, string(SnapshotFile)), - body: snapshotYml, - }, nil -} - -// KibanaConfigDefaultFile is the default kibana config file -const KibanaConfigDefaultFile configFile = "kibana.config.default.yml" - -//go:embed _static/kibana_config_default.yml -var kibanaConfigDefaultYml string - -func newKibanaConfigDefault(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(KibanaConfigDefaultFile), - path: filepath.Join(profilePath, profileStackPath, string(KibanaConfigDefaultFile)), - body: kibanaConfigDefaultYml, - }, nil -} - -// KibanaConfig8xFile is the Kibana config file for 8.x stack family -const KibanaConfig8xFile configFile = "kibana.config.8x.yml" - -//go:embed _static/kibana_config_8x.yml -var kibanaConfig8xYml string - -func newKibanaConfig8x(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(KibanaConfig8xFile), - path: filepath.Join(profilePath, profileStackPath, string(KibanaConfig8xFile)), - body: kibanaConfig8xYml, - }, nil -} - -// KibanaConfig86File is the Kibana config file for the 8.x stack family (8.2 to 8.6) -const KibanaConfig86File configFile = "kibana.config.86.yml" - -//go:embed _static/kibana_config_86.yml -var kibanaConfig86Yml string - -func newKibanaConfig86(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(KibanaConfig86File), - path: filepath.Join(profilePath, profileStackPath, string(KibanaConfig86File)), - body: kibanaConfig86Yml, - }, nil -} - -// KibanaConfig80File is the Kibana config file for 8.0 stack family (8.0 to 8.1) -const KibanaConfig80File configFile = "kibana.config.80.yml" - -//go:embed _static/kibana_config_80.yml -var kibanaConfig80Yml string - -func newKibanaConfig80(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(KibanaConfig80File), - path: filepath.Join(profilePath, profileStackPath, string(KibanaConfig80File)), - body: kibanaConfig80Yml, - }, nil -} - -// ElasticsearchConfigDefaultFile is the default Elasticsearch config file -const ElasticsearchConfigDefaultFile configFile = "elasticsearch.config.default.yml" - -//go:embed _static/elasticsearch_config_default.yml -var elasticsearchConfigDefaultYml string - -func newElasticsearchConfigDefault(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(ElasticsearchConfigDefaultFile), - path: filepath.Join(profilePath, profileStackPath, string(ElasticsearchConfigDefaultFile)), - body: elasticsearchConfigDefaultYml, - }, nil -} - -// ElasticsearchConfig8xFile is the Elasticsearch config file for 8.x stack family -const ElasticsearchConfig8xFile configFile = "elasticsearch.config.8x.yml" - -//go:embed _static/elasticsearch_config_8x.yml -var elasticsearchConfig8xYml string - -func newElasticsearchConfig8x(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(ElasticsearchConfig8xFile), - path: filepath.Join(profilePath, profileStackPath, string(ElasticsearchConfig8xFile)), - body: elasticsearchConfig8xYml, - }, nil -} - -// ElasticsearchConfig8xFile is the Elasticsearch config file for 8.x stack family (8.2 to 8.6) -const ElasticsearchConfig86File configFile = "elasticsearch.config.86.yml" - -func newElasticsearchConfig86(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(ElasticsearchConfig86File), - path: filepath.Join(profilePath, profileStackPath, string(ElasticsearchConfig86File)), - body: elasticsearchConfig8xYml, - }, nil -} - -// ElasticsearchConfig80File is the Elasticsearch virtual config file name for 8.0 stack family (8.0 to 8.1) -// This file does not exist in the source code, since it's identical to the 8x config file. -const ElasticsearchConfig80File configFile = "elasticsearch.config.80.yml" - -func newElasticsearchConfig80(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(ElasticsearchConfig80File), - path: filepath.Join(profilePath, profileStackPath, string(ElasticsearchConfig80File)), - body: elasticsearchConfig8xYml, - }, nil -} - -// PackageRegistryConfigFile is the config file for the Elastic Package registry -const PackageRegistryConfigFile configFile = "package-registry.config.yml" - -//go:embed _static/package_registry.yml -var packageRegistryConfigYml string - -// newPackageRegistryConfig returns a Managed Config -func newPackageRegistryConfig(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(PackageRegistryConfigFile), - path: filepath.Join(profilePath, profileStackPath, string(PackageRegistryConfigFile)), - body: packageRegistryConfigYml, - }, nil -} - -// PackageRegistryBaseImage is the base Docker image of the Elastic Package Registry. -const PackageRegistryBaseImage = "docker.elastic.co/package-registry/package-registry:v1.16.3" - -// PackageRegistryDockerfileFile is the dockerfile for the Elastic package registry -const PackageRegistryDockerfileFile configFile = "Dockerfile.package-registry" - -//go:embed _static/Dockerfile.package-registry -var packageRegistryDockerfileTmpl string -var packageRegistryDockerfile = strings.Replace(packageRegistryDockerfileTmpl, - "__BASE_IMAGE__", PackageRegistryBaseImage, -1) - -// newPackageRegistryDockerfile returns a new config for the package-registry -func newPackageRegistryDockerfile(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(PackageRegistryDockerfileFile), - path: filepath.Join(profilePath, profileStackPath, string(PackageRegistryDockerfileFile)), - body: packageRegistryDockerfile, - }, nil -} - -// ElasticAgent80EnvFile is the .env for the 8.0 stack. -// This file does not exist in the source code, since it's identical to the 8x env file. -const ElasticAgent80EnvFile configFile = "elastic-agent.80.env" - -func newElasticAgent80Env(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(ElasticAgent80EnvFile), - path: filepath.Join(profilePath, profileStackPath, string(ElasticAgent80EnvFile)), - body: elasticAgent8xEnv, - }, nil -} - -// ElasticAgent86EnvFile is the .env for the 8.6 stack. -// This file does not exist in the source code, since it's identical to the 8x env file. -const ElasticAgent86EnvFile configFile = "elastic-agent.86.env" - -func newElasticAgent86Env(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(ElasticAgent86EnvFile), - path: filepath.Join(profilePath, profileStackPath, string(ElasticAgent86EnvFile)), - body: elasticAgent8xEnv, - }, nil -} - -// ElasticAgent8xEnvFile is the .env for the 8x stack. -const ElasticAgent8xEnvFile configFile = "elastic-agent.8x.env" - -//go:embed _static/elastic-agent_8x.env -var elasticAgent8xEnv string - -func newElasticAgent8xEnv(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(ElasticAgent8xEnvFile), - path: filepath.Join(profilePath, profileStackPath, string(ElasticAgent8xEnvFile)), - body: elasticAgent8xEnv, - }, nil -} - -// ElasticAgentDefaultEnvFile is the default .env file. -const ElasticAgentDefaultEnvFile configFile = "elastic-agent.default.env" - -//go:embed _static/elastic-agent_default.env -var elasticAgentDefaultEnv string - -func newElasticAgentDefaultEnv(_ string, profilePath string) (*simpleFile, error) { - return &simpleFile{ - name: string(ElasticAgentDefaultEnvFile), - path: filepath.Join(profilePath, profileStackPath, string(ElasticAgentDefaultEnvFile)), - body: elasticAgentDefaultEnv, - }, nil -} diff --git a/internal/profile/_static/Dockerfile.package-registry b/internal/stack/_static/Dockerfile.package-registry.tmpl similarity index 50% rename from internal/profile/_static/Dockerfile.package-registry rename to internal/stack/_static/Dockerfile.package-registry.tmpl index e6ff25594..848cd1aa3 100644 --- a/internal/profile/_static/Dockerfile.package-registry +++ b/internal/stack/_static/Dockerfile.package-registry.tmpl @@ -1,4 +1,5 @@ -FROM __BASE_IMAGE__ +FROM {{ fact "registry_base_image" }} + ARG PROFILE # Disable package validation (already done). @@ -7,5 +8,5 @@ ENV EPR_DISABLE_PACKAGE_VALIDATION=true ENV EPR_FEATURE_PROXY_MODE=true ENV EPR_PROXY_TO=https://epr.elastic.co -COPY profiles/${PROFILE}/stack/package-registry.config.yml /package-registry/config.yml -COPY stack/development/ /packages/development \ No newline at end of file +COPY profiles/${PROFILE}/stack/package-registry.yml /package-registry/config.yml +COPY stack/development/ /packages/development diff --git a/internal/install/_static/GeoLite2-ASN.mmdb b/internal/stack/_static/GeoLite2-ASN.mmdb similarity index 100% rename from internal/install/_static/GeoLite2-ASN.mmdb rename to internal/stack/_static/GeoLite2-ASN.mmdb diff --git a/internal/install/_static/GeoLite2-City.mmdb b/internal/stack/_static/GeoLite2-City.mmdb similarity index 100% rename from internal/install/_static/GeoLite2-City.mmdb rename to internal/stack/_static/GeoLite2-City.mmdb diff --git a/internal/install/_static/GeoLite2-Country.mmdb b/internal/stack/_static/GeoLite2-Country.mmdb similarity index 100% rename from internal/install/_static/GeoLite2-Country.mmdb rename to internal/stack/_static/GeoLite2-Country.mmdb diff --git a/internal/profile/_static/docker-compose-stack.yml b/internal/stack/_static/docker-compose-stack.yml.tmpl similarity index 76% rename from internal/profile/_static/docker-compose-stack.yml rename to internal/stack/_static/docker-compose-stack.yml.tmpl index 0ad5b8b3e..d52b7af8c 100644 --- a/internal/profile/_static/docker-compose-stack.yml +++ b/internal/stack/_static/docker-compose-stack.yml.tmpl @@ -1,19 +1,21 @@ +{{ $username := fact "username" }} +{{ $password := fact "password" }} version: '2.3' services: elasticsearch: image: "${ELASTICSEARCH_IMAGE_REF}" healthcheck: - test: "curl -s --cacert /usr/share/elasticsearch/config/certs/ca-cert.pem -f -u elastic:changeme https://127.0.0.1:9200/_cat/health | cut -f4 -d' ' | grep -E '(green|yellow)'" + test: "curl -s --cacert /usr/share/elasticsearch/config/certs/ca-cert.pem -f -u {{ $username }}:{{ $password }} https://127.0.0.1:9200/_cat/health | cut -f4 -d' ' | grep -E '(green|yellow)'" start_period: 300s interval: 5s environment: - "ES_JAVA_OPTS=-Xms1g -Xmx1g" - - "ELASTIC_PASSWORD=changeme" + - "ELASTIC_PASSWORD={{ $password }}" volumes: - - "./elasticsearch.config.${STACK_VERSION_VARIANT}.yml:/usr/share/elasticsearch/config/elasticsearch.yml" - - "../certs/elasticsearch:/usr/share/elasticsearch/config/certs" - - "../../../stack/ingest-geoip:/usr/share/elasticsearch/config/ingest-geoip" - - "../../../stack/service_tokens:/usr/share/elasticsearch/config/service_tokens" + - "./elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml" + - "./certs/elasticsearch:/usr/share/elasticsearch/config/certs" + - "./ingest-geoip:/usr/share/elasticsearch/config/ingest-geoip" + - "./service_tokens:/usr/share/elasticsearch/config/service_tokens" ports: - "127.0.0.1:9200:9200" @@ -35,14 +37,14 @@ services: start_period: 600s interval: 5s env_file: - - "../certs/ca.env" + - "./certs/ca.env" environment: # Is there a better way to add certificates to Kibana/Fleet? - "NODE_EXTRA_CA_CERTS=/usr/share/kibana/config/certs/ca-cert.pem" volumes: - - "./kibana.config.${STACK_VERSION_VARIANT}.yml:/usr/share/kibana/config/kibana.yml" - - "../certs/kibana:/usr/share/kibana/config/certs" - - "../../../stack/healthcheck.sh:/usr/share/kibana/healthcheck.sh" + - "./kibana.yml:/usr/share/kibana/config/kibana.yml" + - "./certs/kibana:/usr/share/kibana/config/certs" + - "./kibana_healthcheck.sh:/usr/share/kibana/healthcheck.sh" ports: - "127.0.0.1:5601:5601" @@ -55,7 +57,7 @@ services: package-registry: build: context: ../../../ - dockerfile: "${STACK_PATH}/Dockerfile.package-registry" + dockerfile: "./profiles/${PROFILE_NAME}/stack/Dockerfile.package-registry" args: PROFILE: "${PROFILE_NAME}" healthcheck: @@ -69,7 +71,7 @@ services: - "EPR_TLS_KEY=/etc/ssl/package-registry/key.pem" - "EPR_TLS_CERT=/etc/ssl/package-registry/cert.pem" volumes: - - "../certs/package-registry:/etc/ssl/package-registry" + - "./certs/package-registry:/etc/ssl/package-registry" ports: - "127.0.0.1:8080:8080" - "127.0.0.1:9000:9000" @@ -106,8 +108,8 @@ services: - "KIBANA_FLEET_SETUP=1" - "KIBANA_HOST=https://kibana:5601" volumes: - - "../certs/ca-cert.pem:/etc/ssl/certs/elastic-package.pem" - - "../certs/fleet-server:/etc/ssl/elastic-agent" + - "./certs/ca-cert.pem:/etc/ssl/certs/elastic-package.pem" + - "./certs/fleet-server:/etc/ssl/elastic-agent" ports: - "127.0.0.1:8220:8220" @@ -129,9 +131,9 @@ services: retries: 180 interval: 5s hostname: docker-fleet-agent - env_file: "./elastic-agent.${STACK_VERSION_VARIANT}.env" + env_file: "./elastic-agent.env" volumes: - - "../certs/ca-cert.pem:/etc/ssl/certs/elastic-package.pem" + - "./certs/ca-cert.pem:/etc/ssl/certs/elastic-package.pem" - type: bind source: ../../../tmp/service_logs/ target: /tmp/service_logs/ diff --git a/internal/profile/_static/elastic-agent_8x.env b/internal/stack/_static/elastic-agent.env.tmpl similarity index 64% rename from internal/profile/_static/elastic-agent_8x.env rename to internal/stack/_static/elastic-agent.env.tmpl index 1fea8128c..70a0b5cb5 100644 --- a/internal/profile/_static/elastic-agent_8x.env +++ b/internal/stack/_static/elastic-agent.env.tmpl @@ -1,5 +1,8 @@ +{{ $version := fact "agent_version" }} FLEET_ENROLL=1 -FLEET_TOKEN_POLICY_NAME=Elastic-Agent (elastic-package) FLEET_URL=https://fleet-server:8220 KIBANA_FLEET_HOST=https://kibana:5601 KIBANA_HOST=https://kibana:5601 +{{ if not (semverLessThan $version "8.0.0") }} +FLEET_TOKEN_POLICY_NAME=Elastic-Agent (elastic-package) +{{ end }} diff --git a/internal/profile/_static/elasticsearch_config_default.yml b/internal/stack/_static/elasticsearch.yml.tmpl similarity index 86% rename from internal/profile/_static/elasticsearch_config_default.yml rename to internal/stack/_static/elasticsearch.yml.tmpl index f72696cd3..9e14ec9a5 100644 --- a/internal/profile/_static/elasticsearch_config_default.yml +++ b/internal/stack/_static/elasticsearch.yml.tmpl @@ -7,15 +7,17 @@ indices.id_field_data.enabled: true xpack.license.self_generated.type: "trial" xpack.security.enabled: true xpack.security.authc.api_key.enabled: true - xpack.security.http.ssl.enabled: true xpack.security.http.ssl.key: "certs/key.pem" xpack.security.http.ssl.certificate: "certs/cert.pem" +ingest.geoip.downloader.enabled: false + +{{ $version := fact "elasticsearch_version" }} +{{ if semverLessThan $version "8.0.0" }} script.max_compilations_rate: "use-context" script.context.template.max_compilations_rate: "unlimited" script.context.ingest.cache_max_size: 2000 script.context.processor_conditional.cache_max_size: 2000 script.context.template.cache_max_size: 2000 - -ingest.geoip.downloader.enabled: false +{{ end }} diff --git a/internal/profile/_static/kibana_config_8x.yml b/internal/stack/_static/kibana.yml.tmpl similarity index 79% rename from internal/profile/_static/kibana_config_8x.yml rename to internal/stack/_static/kibana.yml.tmpl index 72cead61c..608586770 100644 --- a/internal/profile/_static/kibana_config_8x.yml +++ b/internal/stack/_static/kibana.yml.tmpl @@ -1,3 +1,4 @@ +{{ $version := fact "kibana_version" }} server.name: kibana server.host: "0.0.0.0" server.ssl.enabled: true @@ -7,20 +8,37 @@ server.ssl.certificateAuthorities: ["/usr/share/kibana/config/certs/ca-cert.pem" elasticsearch.hosts: [ "https://elasticsearch:9200" ] elasticsearch.ssl.certificateAuthorities: "/usr/share/kibana/config/certs/ca-cert.pem" + +{{ if semverLessThan $version "8.0.0" }} +elasticsearch.username: {{ fact "username" }} +elasticsearch.password: {{ fact "password" }} + +xpack.monitoring.ui.container.elasticsearch.enabled: true +xpack.fleet.enabled: true +xpack.fleet.agents.elasticsearch.host: "https://elasticsearch:9200" +{{ else }} elasticsearch.serviceAccountToken: "AAEAAWVsYXN0aWMva2liYW5hL2VsYXN0aWMtcGFja2FnZS1raWJhbmEtdG9rZW46b2x4b051SWNRa0tYMHdXazdLWmFBdw" monitoring.ui.container.elasticsearch.enabled: true +xpack.fleet.agents.elasticsearch.hosts: ["https://elasticsearch:9200"] +{{ end }} + xpack.fleet.registryUrl: "https://package-registry:8080" xpack.fleet.agents.enabled: true -xpack.fleet.agents.elasticsearch.hosts: ["https://elasticsearch:9200"] xpack.fleet.agents.fleet_server.hosts: ["https://fleet-server:8220"] + +{{ if not (semverLessThan $version "8.7.0") }} xpack.fleet.enableExperimental: ["experimentalDataStreamSettings"] # Enable experimental toggles in Fleet UI +{{ end }} xpack.encryptedSavedObjects.encryptionKey: "12345678901234567890123456789012" +{{ if not (semverLessThan $version "8.1.0") }} xpack.cloudSecurityPosture.enabled: true +{{ end }} +{{ if not (semverLessThan $version "8.0.0") }} xpack.fleet.packages: - name: system version: latest @@ -60,3 +78,4 @@ xpack.fleet.outputs: ca_trusted_fingerprint: "${ELASTIC_PACKAGE_CA_TRUSTED_FINGERPRINT}" is_default: true is_default_monitoring: true +{{ end }} diff --git a/internal/install/_static/kibana_healthcheck.sh b/internal/stack/_static/kibana_healthcheck.sh.tmpl similarity index 64% rename from internal/install/_static/kibana_healthcheck.sh rename to internal/stack/_static/kibana_healthcheck.sh.tmpl index 09bc39c35..386b55add 100644 --- a/internal/install/_static/kibana_healthcheck.sh +++ b/internal/stack/_static/kibana_healthcheck.sh.tmpl @@ -3,4 +3,4 @@ set -e curl -s --cacert /usr/share/kibana/config/certs/ca-cert.pem -f https://localhost:5601/login | grep kbn-injected-metadata 2>&1 >/dev/null -curl -s --cacert /usr/share/kibana/config/certs/ca-cert.pem -f -u elastic:changeme "https://elasticsearch:9200/_cat/indices/.security-*?h=health" | grep -v red +curl -s --cacert /usr/share/kibana/config/certs/ca-cert.pem -f -u {{ fact "username" }}:{{ fact "password" }} "https://elasticsearch:9200/_cat/indices/.security-*?h=health" | grep -v red diff --git a/internal/profile/_static/package_registry.yml b/internal/stack/_static/package-registry.yml similarity index 100% rename from internal/profile/_static/package_registry.yml rename to internal/stack/_static/package-registry.yml diff --git a/internal/install/_static/service_tokens b/internal/stack/_static/service_tokens similarity index 100% rename from internal/install/_static/service_tokens rename to internal/stack/_static/service_tokens diff --git a/internal/stack/boot.go b/internal/stack/boot.go index b2262a7ed..ab483fed0 100644 --- a/internal/stack/boot.go +++ b/internal/stack/boot.go @@ -23,6 +23,18 @@ const DockerComposeProjectName = "elastic-package-stack" // BootUp function boots up the Elastic stack. func BootUp(options Options) error { + // Print information before starting the stack, for cases where + // this is executed in the foreground, without daemon mode. + config := Config{ + Provider: ProviderCompose, + ElasticsearchHost: "https://127.0.0.1:9200", + ElasticsearchUsername: elasticsearchUsername, + ElasticsearchPassword: elasticsearchPassword, + KibanaHost: "https://127.0.0.1:5601", + CACertFile: options.Profile.Path(profileStackPath, CACertificateFile), + } + printUserConfig(options.Printer, config) + buildPackagesPath, found, err := builder.FindBuildPackagesDirectory() if err != nil { return errors.Wrap(err, "finding build packages directory failed") @@ -46,11 +58,16 @@ func BootUp(options Options) error { } } - fmt.Println("Packages from the following directories will be loaded into the package-registry:") - fmt.Println("- built-in packages (package-storage:snapshot Docker image)") + options.Printer.Println("Packages from the following directories will be loaded into the package-registry:") + options.Printer.Println("- built-in packages (package-storage:snapshot Docker image)") if found { - fmt.Printf("- %s\n", buildPackagesPath) + options.Printer.Printf("- %s\n", buildPackagesPath) + } + + err = applyResources(options.Profile, options.StackVersion) + if err != nil { + return errors.Wrap(err, "creating stack files failed") } err = dockerComposeBuild(options) @@ -73,6 +90,11 @@ func BootUp(options Options) error { return errors.Wrap(err, "running docker-compose failed") } + err = storeConfig(options.Profile, config) + if err != nil { + return errors.Wrap(err, "failed to store config") + } + return nil } diff --git a/internal/profile/certs.go b/internal/stack/certs.go similarity index 69% rename from internal/profile/certs.go rename to internal/stack/certs.go index 49f6b48f2..ac63774e6 100644 --- a/internal/profile/certs.go +++ b/internal/stack/certs.go @@ -2,7 +2,7 @@ // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. -package profile +package stack import ( "bytes" @@ -11,6 +11,8 @@ import ( "io" "path/filepath" + "github.com/elastic/go-resource" + "github.com/elastic/elastic-package/internal/certs" ) @@ -28,39 +30,41 @@ var ( CertificatesDirectory = "certs" // CACertificateFile is the path to the CA certificate file inside a profile. - CACertificateFile = configFile(filepath.Join(CertificatesDirectory, "ca-cert.pem")) + CACertificateFile = filepath.Join(CertificatesDirectory, "ca-cert.pem") // CAKeyFile is the path to the CA key file inside a profile. - CAKeyFile = configFile(filepath.Join(CertificatesDirectory, "ca-key.pem")) + CAKeyFile = filepath.Join(CertificatesDirectory, "ca-key.pem") // CAEnvFile is the path to the file with environment variables about the CA. - CAEnvFile = configFile(filepath.Join(CertificatesDirectory, "ca.env")) + CAEnvFile = filepath.Join(CertificatesDirectory, "ca.env") ) // initTLSCertificates initializes all the certificates needed to run the services // managed by elastic-package stack. It includes a CA, and a pair of keys and // certificates for each service. -func initTLSCertificates(profilePath string, configMap map[configFile]*simpleFile) error { +func initTLSCertificates(fileProvider string, profilePath string) ([]resource.Resource, error) { certsDir := filepath.Join(profilePath, CertificatesDirectory) caCertFile := filepath.Join(profilePath, string(CACertificateFile)) caKeyFile := filepath.Join(profilePath, string(CAKeyFile)) envFile := filepath.Join(profilePath, string(CAEnvFile)) + var resources []resource.Resource + ca, err := initCA(caCertFile, caKeyFile) if err != nil { - return err + return nil, err } - err = certWriterToSimpleFile(configMap, profilePath, caCertFile, ca.WriteCert) + resources, err = certWriteToResource(resources, fileProvider, profilePath, caCertFile, ca.WriteCert) if err != nil { - return err + return nil, err } - err = certWriterToSimpleFile(configMap, profilePath, caKeyFile, ca.WriteKey) + resources, err = certWriteToResource(resources, fileProvider, profilePath, caKeyFile, ca.WriteKey) if err != nil { - return err + return nil, err } - err = certWriterToSimpleFile(configMap, profilePath, envFile, ca.WriteEnv) + resources, err = certWriteToResource(resources, fileProvider, profilePath, envFile, ca.WriteEnv) if err != nil { - return err + return nil, err } for _, service := range tlsServices { @@ -70,47 +74,47 @@ func initTLSCertificates(profilePath string, configMap map[configFile]*simpleFil keyFile := filepath.Join(certsDir, "key.pem") cert, err := initServiceTLSCertificates(ca, caCertFile, certFile, keyFile, service) if err != nil { - return err + return nil, err } - err = certWriterToSimpleFile(configMap, profilePath, certFile, cert.WriteCert) + resources, err = certWriteToResource(resources, fileProvider, profilePath, certFile, cert.WriteCert) if err != nil { - return err + return nil, err } - err = certWriterToSimpleFile(configMap, profilePath, keyFile, cert.WriteKey) + resources, err = certWriteToResource(resources, fileProvider, profilePath, keyFile, cert.WriteKey) if err != nil { - return err + return nil, err } // Write the CA also in the service directory, so only a directory needs to be mounted // for services that need to configure the CA to validate other services certificates. - err = certWriterToSimpleFile(configMap, profilePath, caFile, ca.WriteCert) + resources, err = certWriteToResource(resources, fileProvider, profilePath, caFile, ca.WriteCert) if err != nil { - return err + return nil, err } } - return nil + return resources, nil } -func certWriterToSimpleFile(configMap map[configFile]*simpleFile, profilePath string, absPath string, writeFile func(w io.Writer) error) error { +func certWriteToResource(resources []resource.Resource, fileProvider string, profilePath string, absPath string, write func(w io.Writer) error) ([]resource.Resource, error) { path, err := filepath.Rel(profilePath, absPath) if err != nil { - return err + return resources, err } var buf bytes.Buffer - err = writeFile(&buf) + err = write(&buf) if err != nil { - return err + return resources, err } - configMap[configFile(path)] = &simpleFile{ - name: path, - path: absPath, - body: buf.String(), - } - return nil + return append(resources, &resource.File{ + Provider: fileProvider, + Path: path, + CreateParent: true, + Content: resource.FileContentLiteral(buf.String()), + }), nil } func initCA(certFile, keyFile string) (*certs.Issuer, error) { diff --git a/internal/profile/certs_test.go b/internal/stack/certs_test.go similarity index 81% rename from internal/profile/certs_test.go rename to internal/stack/certs_test.go index 5d8a9154e..2d7daf2eb 100644 --- a/internal/profile/certs_test.go +++ b/internal/stack/certs_test.go @@ -2,13 +2,14 @@ // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. -package profile +package stack import ( "os" "path/filepath" "testing" + "github.com/elastic/go-resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -20,11 +21,15 @@ func TestTLSCertsInitialization(t *testing.T) { assert.Error(t, verifyTLSCertificates(caCertFile, caCertFile, caKeyFile, "")) - configMap := make(map[configFile]*simpleFile) - err := initTLSCertificates(profilePath, configMap) + providerName := "test-file" + resources, err := initTLSCertificates(providerName, profilePath) require.NoError(t, err) - err = writeConfigFiles(configMap) + resourceManager := resource.NewManager() + resourceManager.RegisterProvider(providerName, &resource.FileProvider{ + Prefix: profilePath, + }) + _, err = resourceManager.Apply(resources) require.NoError(t, err) assert.NoError(t, verifyTLSCertificates(caCertFile, caCertFile, caKeyFile, "")) @@ -49,12 +54,11 @@ func TestTLSCertsInitialization(t *testing.T) { assert.Error(t, verifyTLSCertificates(caCertFile, serviceCertFile, serviceKeyFile, service)) // Check it is created again and is validated by the same CA. - configMap := make(map[configFile]*simpleFile) - err := initTLSCertificates(profilePath, configMap) + resources, err := initTLSCertificates(providerName, profilePath) require.NoError(t, err) - - err = writeConfigFiles(configMap) + _, err = resourceManager.Apply(resources) require.NoError(t, err) + assert.NoError(t, verifyTLSCertificates(caCertFile, serviceCertFile, serviceKeyFile, service)) }) } diff --git a/internal/stack/cloud.go b/internal/stack/cloud.go new file mode 100644 index 000000000..52a61b629 --- /dev/null +++ b/internal/stack/cloud.go @@ -0,0 +1,354 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package stack + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + + "github.com/elastic/cloud-sdk-go/pkg/api" + "github.com/elastic/cloud-sdk-go/pkg/api/deploymentapi" + "github.com/elastic/cloud-sdk-go/pkg/api/deploymentapi/deptemplateapi" + "github.com/elastic/cloud-sdk-go/pkg/auth" + "github.com/elastic/cloud-sdk-go/pkg/models" + "github.com/elastic/cloud-sdk-go/pkg/plan" + "github.com/elastic/cloud-sdk-go/pkg/plan/planutil" + + "github.com/elastic/elastic-package/internal/logger" + "github.com/elastic/elastic-package/internal/profile" +) + +const ( + paramCloudDeploymentID = "cloud_deployment_id" + paramCloudDeploymentAlias = "cloud_deployment_alias" +) + +var ( + errDeploymentNotExist = errors.New("deployment does not exist") +) + +type cloudProvider struct { + api *api.API + profile *profile.Profile +} + +func newCloudProvider(profile *profile.Profile) (*cloudProvider, error) { + apiKey := os.Getenv("EC_API_KEY") + if apiKey == "" { + return nil, errors.New("unable to obtain value from EC_API_KEY environment variable") + } + api, err := api.NewAPI(api.Config{ + Client: new(http.Client), + AuthWriter: auth.APIKey(apiKey), + }) + if err != nil { + return nil, fmt.Errorf("failed to initialize API client: %w", err) + } + + return &cloudProvider{ + api: api, + profile: profile, + }, nil +} + +func (cp *cloudProvider) BootUp(options Options) error { + _, err := cp.currentDeployment() + if err == nil { + // Do nothing, deployment already exists. + // TODO: Migrate configuration if changed. + config, err := LoadConfig(cp.profile) + if err != nil { + return err + } + printUserConfig(options.Printer, config) + return nil + } else if err != nil && err != errDeploymentNotExist { + return err + } + + // TODO: Parameterize this. + name := "elastic-package-test" + region := "gcp-europe-west3" + templateID := "gcp-io-optimized" + + logger.Debugf("Getting deployment template %q", templateID) + template, err := deptemplateapi.Get(deptemplateapi.GetParams{ + API: cp.api, + TemplateID: templateID, + Region: region, + StackVersion: options.StackVersion, + }) + if err != nil { + return fmt.Errorf("failed to get deployment template %q: %w", templateID, err) + } + + payload := template.DeploymentTemplate + + // Remove the resources that we don't need. + payload.Resources.Apm = nil + payload.Resources.Appsearch = nil + payload.Resources.EnterpriseSearch = nil + + // Initialize the plan with the id of the template, otherwise the create request fails. + if es := payload.Resources.Elasticsearch; len(es) > 0 { + plan := es[0].Plan + if plan.DeploymentTemplate == nil { + plan.DeploymentTemplate = &models.DeploymentTemplateReference{} + } + plan.DeploymentTemplate.ID = &templateID + } + + logger.Debugf("Creating deployment %q", name) + res, err := deploymentapi.Create(deploymentapi.CreateParams{ + API: cp.api, + Request: payload, + Overrides: &deploymentapi.PayloadOverrides{ + Name: name, + }, + }) + if err != nil { + return fmt.Errorf("failed to create deployment: %w", err) + } + if created := res.Created; created == nil || !*created { + return fmt.Errorf("request succeeded, but deployment was not created, check in the console UI") + } + + var config Config + config.Provider = ProviderCloud + config.Parameters = map[string]string{ + "cloud_deployment_alias": res.Alias, + } + deploymentID := res.ID + if deploymentID == nil { + return fmt.Errorf("deployment created, but couldn't get its ID, check in the console UI") + } + config.Parameters["cloud_deployment_id"] = *deploymentID + + for _, resource := range res.Resources { + kind := resource.Kind + if kind == nil { + continue + } + if *kind == "elasticsearch" { + if creds := resource.Credentials; creds != nil { + if creds.Username != nil { + config.ElasticsearchUsername = *creds.Username + } + if creds.Password != nil { + config.ElasticsearchPassword = *creds.Password + } + } + } + } + + deployment, err := deploymentapi.Get(deploymentapi.GetParams{ + API: cp.api, + DeploymentID: *deploymentID, + }) + if err != nil { + return fmt.Errorf("couldn't check deployment health: %w", err) + } + + config.ElasticsearchHost, err = cp.getServiceURL(deployment.Resources.Elasticsearch) + if err != nil { + return fmt.Errorf("failed to get elasticsearch host: %w", err) + } + config.KibanaHost, err = cp.getServiceURL(deployment.Resources.Kibana) + if err != nil { + return fmt.Errorf("failed to get kibana host: %w", err) + } + config.Parameters["fleet_url"], err = cp.getServiceURL(deployment.Resources.IntegrationsServer) + if err != nil { + return fmt.Errorf("failed to get fleet host: %w", err) + } + + printUserConfig(options.Printer, config) + + err = storeConfig(cp.profile, config) + if err != nil { + return fmt.Errorf("failed to store config: %w", err) + } + + logger.Debug("Waiting for creation plan to be completed") + err = planutil.TrackChange(planutil.TrackChangeParams{ + TrackChangeParams: plan.TrackChangeParams{ + API: cp.api, + DeploymentID: *deploymentID, + }, + Writer: &cloudTrackWriter{}, + Format: "text", + }) + if err != nil { + return fmt.Errorf("failed to track cluster creation: %w", err) + } + + return nil +} + +func (cp *cloudProvider) TearDown(options Options) error { + deployment, err := cp.currentDeployment() + if err != nil { + return fmt.Errorf("failed to find current deployment: %w", err) + } + if deployment.ID == nil { + return fmt.Errorf("deployment doesn't have id?") + } + + logger.Debugf("Deleting deployment %q", *deployment.ID) + + _, err = deploymentapi.Shutdown(deploymentapi.ShutdownParams{ + API: cp.api, + DeploymentID: *deployment.ID, + SkipSnapshot: true, + }) + if err != nil { + return fmt.Errorf("failed to shutdown deployment: %w", err) + } + + return nil +} + +func (*cloudProvider) Update(options Options) error { + fmt.Println("Nothing to do.") + return nil +} + +func (*cloudProvider) Dump(options DumpOptions) (string, error) { + return "", fmt.Errorf("not implemented") +} + +func (cp *cloudProvider) Status(options Options) ([]ServiceStatus, error) { + deployment, err := cp.currentDeployment() + if err != nil { + return nil, err + } + + status, _ := cp.deploymentStatus(deployment) + return status, nil +} + +func (*cloudProvider) deploymentStatus(deployment *models.DeploymentGetResponse) ([]ServiceStatus, bool) { + allHealthy := true + healthStatus := func(healthy *bool) string { + if healthy != nil && *healthy { + return "healthy" + } + allHealthy = false + return "unhealthy" + } + if healthy := deployment.Healthy; healthy == nil || !*healthy { + allHealthy = false + } + + var status []ServiceStatus + for _, resource := range deployment.Resources.Elasticsearch { + for i, instance := range resource.Info.Topology.Instances { + var name string + if instance.InstanceName == nil { + name = fmt.Sprintf("elasticsearch-%d", i) + } else { + name = fmt.Sprintf("elasticsearch-%s", *instance.InstanceName) + } + status = append(status, ServiceStatus{ + Name: name, + Version: instance.ServiceVersion, + Status: healthStatus(instance.Healthy), + }) + } + } + for _, resource := range deployment.Resources.Kibana { + for i, instance := range resource.Info.Topology.Instances { + var name string + if instance.InstanceName == nil { + name = fmt.Sprintf("kibana-%d", i) + } else { + name = fmt.Sprintf("kibana-%s", *instance.InstanceName) + } + status = append(status, ServiceStatus{ + Name: name, + Version: instance.ServiceVersion, + Status: healthStatus(instance.Healthy), + }) + } + } + for _, resource := range deployment.Resources.IntegrationsServer { + for i, instance := range resource.Info.Topology.Instances { + var name string + if instance.InstanceName == nil { + name = fmt.Sprintf("integrations-server-%d", i) + } else { + name = fmt.Sprintf("integrations-server-%s", *instance.InstanceName) + } + status = append(status, ServiceStatus{ + Name: name, + Version: instance.ServiceVersion, + Status: healthStatus(instance.Healthy), + }) + } + } + return status, allHealthy +} + +func (cp *cloudProvider) currentDeployment() (*models.DeploymentGetResponse, error) { + config, err := LoadConfig(cp.profile) + if err != nil { + return nil, err + } + deploymentID, found := config.Parameters[paramCloudDeploymentID] + if !found { + return nil, errDeploymentNotExist + } + deployment, err := deploymentapi.Get(deploymentapi.GetParams{ + API: cp.api, + DeploymentID: deploymentID, + }) + if err != nil { + return nil, fmt.Errorf("couldn't check deployment health: %w", err) + } + + // It seems that terminated deployments still exist, but hidden. + if hidden := deployment.Metadata.Hidden; hidden != nil && *hidden { + return nil, errDeploymentNotExist + } + + return deployment, nil +} + +func (*cloudProvider) getServiceURL(resourcesResponse any) (string, error) { + // Converting back and forth for easier access. + var resources []struct { + Info struct { + Metadata struct { + ServiceURL string `json:"service_url"` + } `json:"metadata"` + } `json:"info"` + } + + d, err := json.Marshal(resourcesResponse) + if err != nil { + return "", fmt.Errorf("failed to marshal resources: %w", err) + } + err = json.Unmarshal(d, &resources) + if err != nil { + return "", fmt.Errorf("failed to unmarshal resources back: %w", err) + } + + for _, resource := range resources { + if serviceURL := resource.Info.Metadata.ServiceURL; serviceURL != "" { + return serviceURL, nil + } + } + return "", fmt.Errorf("url not found") +} + +type cloudTrackWriter struct{} + +func (*cloudTrackWriter) Write(p []byte) (n int, err error) { + logger.Debug(string(p)) + return len(p), nil +} diff --git a/internal/stack/compose.go b/internal/stack/compose.go index a14a3aa22..a1e0bb00e 100644 --- a/internal/stack/compose.go +++ b/internal/stack/compose.go @@ -14,7 +14,6 @@ import ( "github.com/elastic/elastic-package/internal/docker" "github.com/elastic/elastic-package/internal/install" "github.com/elastic/elastic-package/internal/logger" - "github.com/elastic/elastic-package/internal/profile" ) type ServiceStatus struct { @@ -36,6 +35,7 @@ type envBuilder struct { vars []string } +// TODO: Use template variables instead of environment variables to parameterize docker-compose. func newEnvBuilder() *envBuilder { return new(envBuilder) } @@ -55,7 +55,7 @@ func (eb *envBuilder) build() []string { } func dockerComposeBuild(options Options) error { - c, err := compose.NewProject(DockerComposeProjectName, options.Profile.FetchPath(profile.SnapshotFile)) + c, err := compose.NewProject(DockerComposeProjectName, options.Profile.Path(profileStackPath, SnapshotFile)) if err != nil { return errors.Wrap(err, "could not create docker compose project") } @@ -81,7 +81,7 @@ func dockerComposeBuild(options Options) error { } func dockerComposePull(options Options) error { - c, err := compose.NewProject(DockerComposeProjectName, options.Profile.FetchPath(profile.SnapshotFile)) + c, err := compose.NewProject(DockerComposeProjectName, options.Profile.Path(profileStackPath, SnapshotFile)) if err != nil { return errors.Wrap(err, "could not create docker compose project") } @@ -107,7 +107,7 @@ func dockerComposePull(options Options) error { } func dockerComposeUp(options Options) error { - c, err := compose.NewProject(DockerComposeProjectName, options.Profile.FetchPath(profile.SnapshotFile)) + c, err := compose.NewProject(DockerComposeProjectName, options.Profile.Path(profileStackPath, SnapshotFile)) if err != nil { return errors.Wrap(err, "could not create docker compose project") } @@ -139,7 +139,7 @@ func dockerComposeUp(options Options) error { } func dockerComposeDown(options Options) error { - c, err := compose.NewProject(DockerComposeProjectName, options.Profile.FetchPath(profile.SnapshotFile)) + c, err := compose.NewProject(DockerComposeProjectName, options.Profile.Path(profileStackPath, SnapshotFile)) if err != nil { return errors.Wrap(err, "could not create docker compose project") } diff --git a/internal/stack/config.go b/internal/stack/config.go new file mode 100644 index 000000000..e6509f575 --- /dev/null +++ b/internal/stack/config.go @@ -0,0 +1,92 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package stack + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/elastic/elastic-package/internal/profile" +) + +var configFileName = "config.json" + +type Config struct { + Provider string `json:"provider,omitempty"` + Parameters map[string]string `json:"parameters,omitempty"` + + ElasticsearchHost string `json:"elasticsearch_host,omitempty"` + ElasticsearchUsername string `json:"elasticsearch_username,omitempty"` + ElasticsearchPassword string `json:"elasticsearch_password,omitempty"` + KibanaHost string `json:"kibana_host,omitempty"` + CACertFile string `json:"ca_cert_file,omitempty"` +} + +func configPath(profile *profile.Profile) string { + return profile.Path(profileStackPath, configFileName) +} + +func defaultConfig(profile *profile.Profile) Config { + return Config{ + Provider: DefaultProvider, + + // Hard-coded default values for backwards-compatibility. + ElasticsearchHost: "https://127.0.0.1:9200", + ElasticsearchUsername: elasticsearchUsername, + ElasticsearchPassword: elasticsearchPassword, + KibanaHost: "https://127.0.0.1:5601", + CACertFile: profile.Path(profileStackPath, CACertificateFile), + } +} + +func LoadConfig(profile *profile.Profile) (Config, error) { + d, err := os.ReadFile(configPath(profile)) + if errors.Is(err, os.ErrNotExist) { + return defaultConfig(profile), nil + } + if err != nil { + return Config{}, fmt.Errorf("failed to read stack config: %w", err) + } + + var config Config + err = json.Unmarshal(d, &config) + if err != nil { + return Config{}, fmt.Errorf("failed to decode stack config: %w", err) + } + + return config, nil +} + +func storeConfig(profile *profile.Profile, config Config) error { + d, err := json.Marshal(config) + if err != nil { + return fmt.Errorf("failed to encode stack config: %w", err) + } + + err = os.MkdirAll(filepath.Dir(configPath(profile)), 0755) + if err != nil { + return fmt.Errorf("failed to create parent directory: %w", err) + } + + err = os.WriteFile(configPath(profile), d, 0644) + if err != nil { + return fmt.Errorf("failed to write stack config: %s", err) + } + + return nil +} + +func printUserConfig(printer Printer, config Config) { + if printer == nil { + return + } + printer.Printf("Elasticsearch host: %s\n", config.ElasticsearchHost) + printer.Printf("Kibana host: %s\n", config.KibanaHost) + printer.Printf("Username: %s\n", config.ElasticsearchUsername) + printer.Printf("Password: %s\n", config.ElasticsearchPassword) +} diff --git a/internal/stack/dump.go b/internal/stack/dump.go index 008272afb..7a80a2707 100644 --- a/internal/stack/dump.go +++ b/internal/stack/dump.go @@ -52,7 +52,7 @@ func dumpStackLogs(options DumpOptions) error { return errors.Wrapf(err, "can't create output location (path: %s)", logsPath) } - snapshotPath := options.Profile.FetchPath(profile.SnapshotFile) + snapshotPath := options.Profile.Path(profileStackPath, SnapshotFile) for _, serviceName := range observedServices { logger.Debugf("Dump stack logs for %s", serviceName) diff --git a/internal/stack/initconfig.go b/internal/stack/initconfig.go index c23859150..916db46be 100644 --- a/internal/stack/initconfig.go +++ b/internal/stack/initconfig.go @@ -5,14 +5,6 @@ package stack import ( - "fmt" - "os" - - "github.com/pkg/errors" - "gopkg.in/yaml.v3" - - "github.com/elastic/elastic-package/internal/compose" - "github.com/elastic/elastic-package/internal/install" "github.com/elastic/elastic-package/internal/profile" ) @@ -24,58 +16,17 @@ type InitConfig struct { CACertificatePath string } -func StackInitConfig(elasticStackProfile *profile.Profile) (*InitConfig, error) { - // Read Elasticsearch username and password from Kibana configuration file. - // FIXME read credentials from correct Kibana config file, not default - body, err := os.ReadFile(elasticStackProfile.FetchPath(profile.KibanaConfigDefaultFile)) - if err != nil { - return nil, errors.Wrap(err, "error reading Kibana config file") - } - - var kibanaCfg struct { - ElasticsearchUsername string `yaml:"elasticsearch.username"` - ElasticsearchPassword string `yaml:"elasticsearch.password"` - } - err = yaml.Unmarshal(body, &kibanaCfg) - if err != nil { - return nil, errors.Wrap(err, "unmarshalling Kibana configuration failed") - } - - // Read Elasticsearch and Kibana hostnames from Elastic Stack Docker Compose configuration file. - p, err := compose.NewProject(DockerComposeProjectName, elasticStackProfile.FetchPath(profile.SnapshotFile)) +func StackInitConfig(profile *profile.Profile) (*InitConfig, error) { + config, err := LoadConfig(profile) if err != nil { - return nil, errors.Wrap(err, "could not create docker compose project") + return nil, err } - appConfig, err := install.Configuration() - if err != nil { - return nil, errors.Wrap(err, "can't read application configuration") - } - - serviceComposeConfig, err := p.Config(compose.CommandOptions{ - Env: newEnvBuilder(). - withEnvs(appConfig.StackImageRefs(install.DefaultStackVersion).AsEnv()). - withEnvs(elasticStackProfile.ComposeEnvVars()). - withEnv(stackVariantAsEnv(install.DefaultStackVersion)). - build(), - }) - if err != nil { - return nil, errors.Wrap(err, "could not get Docker Compose configuration for service") - } - - kib := serviceComposeConfig.Services["kibana"] - kibHostPort := fmt.Sprintf("https://%s:%d", kib.Ports[0].ExternalIP, kib.Ports[0].ExternalPort) - - es := serviceComposeConfig.Services["elasticsearch"] - esHostPort := fmt.Sprintf("https://%s:%d", es.Ports[0].ExternalIP, es.Ports[0].ExternalPort) - - caCert := elasticStackProfile.FetchPath(profile.CACertificateFile) - return &InitConfig{ - ElasticsearchHostPort: esHostPort, - ElasticsearchUsername: kibanaCfg.ElasticsearchUsername, - ElasticsearchPassword: kibanaCfg.ElasticsearchPassword, - KibanaHostPort: kibHostPort, - CACertificatePath: caCert, + ElasticsearchHostPort: config.ElasticsearchHost, + ElasticsearchUsername: config.ElasticsearchUsername, + ElasticsearchPassword: config.ElasticsearchPassword, + KibanaHostPort: config.KibanaHost, + CACertificatePath: config.CACertFile, }, nil } diff --git a/internal/stack/options.go b/internal/stack/options.go index 9fb263167..a1ed87782 100644 --- a/internal/stack/options.go +++ b/internal/stack/options.go @@ -14,4 +14,5 @@ type Options struct { Services []string Profile *profile.Profile + Printer Printer } diff --git a/internal/stack/providers.go b/internal/stack/providers.go new file mode 100644 index 000000000..81d4090ae --- /dev/null +++ b/internal/stack/providers.go @@ -0,0 +1,83 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package stack + +import ( + "fmt" + "strings" + + "github.com/elastic/elastic-package/internal/profile" +) + +const ( + ProviderCompose = "compose" + ProviderCloud = "cloud" +) + +var ( + DefaultProvider = ProviderCompose + SupportedProviders = []string{ + ProviderCompose, + ProviderCloud, + } +) + +// Printer is the interface that can be used to print information on operations. +type Printer interface { + Print(i ...interface{}) + Println(i ...interface{}) + Printf(format string, i ...interface{}) +} + +// Provider is the implementation of a stack provider. +type Provider interface { + // BootUp starts a stack. + BootUp(Options) error + + // TearDown stops and/or removes a stack. + TearDown(Options) error + + // Update updates resources associated to a stack. + Update(Options) error + + // Dump dumps data for debug purpouses. + Dump(DumpOptions) (string, error) + + // Status obtains status information of the stack. + Status(Options) ([]ServiceStatus, error) +} + +// BuildProvider returns the provider for the given name. +func BuildProvider(name string, profile *profile.Profile) (Provider, error) { + switch name { + case "compose": + return &composeProvider{}, nil + case "cloud": + return newCloudProvider(profile) + } + return nil, fmt.Errorf("unknown provider %q, supported providers: %s", name, strings.Join(SupportedProviders, ", ")) +} + +type composeProvider struct{} + +func (*composeProvider) BootUp(options Options) error { + return BootUp(options) +} + +func (*composeProvider) TearDown(options Options) error { + return TearDown(options) +} + +func (*composeProvider) Update(options Options) error { + return Update(options) +} + +func (*composeProvider) Dump(options DumpOptions) (string, error) { + return Dump(options) +} + +func (*composeProvider) Status(options Options) ([]ServiceStatus, error) { + return Status() +} diff --git a/internal/stack/resources.go b/internal/stack/resources.go new file mode 100644 index 000000000..d170aa25b --- /dev/null +++ b/internal/stack/resources.go @@ -0,0 +1,160 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package stack + +import ( + "embed" + "fmt" + "html/template" + "os" + "path/filepath" + "strings" + + "github.com/Masterminds/semver/v3" + "github.com/elastic/go-resource" + + "github.com/elastic/elastic-package/internal/profile" +) + +//go:embed _static +var static embed.FS + +const ( + // SnapshotFile is the docker-compose snapshot.yml file name. + SnapshotFile = "snapshot.yml" + + // ElasticsearchConfigFile is the elasticsearch config file. + ElasticsearchConfigFile = "elasticsearch.yml" + + // KibanaConfigFile is the kibana config file. + KibanaConfigFile = "kibana.yml" + + // KibanaHealthcheckFile is the kibana healthcheck. + KibanaHealthcheckFile = "kibana_healthcheck.sh" + + // PackageRegistryConfigFile is the config file for the Elastic Package registry + PackageRegistryConfigFile = "package-registry.yml" + + // PackageRegistryBaseImage is the base Docker image of the Elastic Package Registry. + PackageRegistryBaseImage = "docker.elastic.co/package-registry/package-registry:v1.17.0" + + // ElasticAgentEnvFile is the elastic agent environment variables file. + ElasticAgentEnvFile = "elastic-agent.env" + + profileStackPath = "stack" + + elasticsearchUsername = "elastic" + elasticsearchPassword = "changeme" +) + +var ( + templateFuncs = template.FuncMap{ + "semverLessThan": semverLessThan, + } + staticSource = resource.NewSourceFS(static).WithTemplateFuncs(templateFuncs) + stackResources = []resource.Resource{ + &resource.File{ + Path: "Dockerfile.package-registry", + Content: staticSource.Template("_static/Dockerfile.package-registry.tmpl"), + }, + &resource.File{ + Path: SnapshotFile, + Content: staticSource.Template("_static/docker-compose-stack.yml.tmpl"), + }, + &resource.File{ + Path: ElasticsearchConfigFile, + Content: staticSource.Template("_static/elasticsearch.yml.tmpl"), + }, + &resource.File{ + Path: "service_tokens", + Content: staticSource.File("_static/service_tokens"), + }, + &resource.File{ + Path: "ingest-geoip/GeoLite2-ASN.mmdb", + CreateParent: true, + Content: staticSource.File("_static/GeoLite2-ASN.mmdb"), + }, + &resource.File{ + Path: "ingest-geoip/GeoLite2-City.mmdb", + CreateParent: true, + Content: staticSource.File("_static/GeoLite2-City.mmdb"), + }, + &resource.File{ + Path: "ingest-geoip/GeoLite2-Country.mmdb", + CreateParent: true, + Content: staticSource.File("_static/GeoLite2-Country.mmdb"), + }, + &resource.File{ + Path: KibanaConfigFile, + Content: staticSource.Template("_static/kibana.yml.tmpl"), + }, + &resource.File{ + Path: KibanaHealthcheckFile, + Content: staticSource.Template("_static/kibana_healthcheck.sh.tmpl"), + }, + &resource.File{ + Path: PackageRegistryConfigFile, + Content: staticSource.File("_static/package-registry.yml"), + }, + &resource.File{ + Path: ElasticAgentEnvFile, + Content: staticSource.Template("_static/elastic-agent.env.tmpl"), + }, + } +) + +func applyResources(profile *profile.Profile, stackVersion string) error { + stackDir := filepath.Join(profile.ProfilePath, profileStackPath) + + resourceManager := resource.NewManager() + resourceManager.AddFacter(resource.StaticFacter{ + "registry_base_image": PackageRegistryBaseImage, + "elasticsearch_version": stackVersion, + "kibana_version": stackVersion, + "agent_version": stackVersion, + + "username": elasticsearchUsername, + "password": elasticsearchPassword, + }) + + os.MkdirAll(stackDir, 0755) + resourceManager.RegisterProvider("file", &resource.FileProvider{ + Prefix: stackDir, + }) + + resources := append([]resource.Resource{}, stackResources...) + + certResources, err := initTLSCertificates("file", profile.ProfilePath) + if err != nil { + return fmt.Errorf("failed to create TLS files: %w", err) + } + resources = append(resources, certResources...) + + results, err := resourceManager.Apply(resources) + if err != nil { + var errors []string + for _, result := range results { + if err := result.Err(); err != nil { + errors = append(errors, err.Error()) + } + } + return fmt.Errorf("%w: %s", err, strings.Join(errors, ", ")) + } + + return nil +} + +func semverLessThan(a, b string) (bool, error) { + sa, err := semver.NewVersion(a) + if err != nil { + return false, err + } + sb, err := semver.NewVersion(b) + if err != nil { + return false, err + } + + return sa.LessThan(sb), nil +} diff --git a/internal/stack/shellinit.go b/internal/stack/shellinit.go index 55f459b60..2e22e1a69 100644 --- a/internal/stack/shellinit.go +++ b/internal/stack/shellinit.go @@ -61,16 +61,15 @@ const ( export %s=%s export %s=%s export %s=%s -export %s=%s -` +export %s=%s` + // fish shell init code. // fish shell is similar but not compliant to POSIX. fishTemplate = `set -x %s %s; set -x %s %s; set -x %s %s; set -x %s %s; -set -x %s %s; -` +set -x %s %s;` // PowerShell init code. // Output to be evaluated with `elastic-package stack shellinit | Invoke-Expression diff --git a/internal/stack/update.go b/internal/stack/update.go index ad5215016..26188d641 100644 --- a/internal/stack/update.go +++ b/internal/stack/update.go @@ -8,12 +8,16 @@ import ( "github.com/pkg/errors" "github.com/elastic/elastic-package/internal/docker" - "github.com/elastic/elastic-package/internal/profile" ) // Update pulls down the most recent versions of the Docker images. func Update(options Options) error { - err := docker.Pull(profile.PackageRegistryBaseImage) + err := applyResources(options.Profile, options.StackVersion) + if err != nil { + return errors.Wrap(err, "creating stack files failed") + } + + err = docker.Pull(PackageRegistryBaseImage) if err != nil { return errors.Wrap(err, "pulling package-registry docker image failed") } diff --git a/internal/install/_static/Dockerfile.terraform_deployer b/internal/testrunner/runners/system/servicedeployer/_static/Dockerfile.terraform_deployer similarity index 100% rename from internal/install/_static/Dockerfile.terraform_deployer rename to internal/testrunner/runners/system/servicedeployer/_static/Dockerfile.terraform_deployer diff --git a/internal/install/_static/docker-custom-agent-base.yml b/internal/testrunner/runners/system/servicedeployer/_static/docker-custom-agent-base.yml similarity index 100% rename from internal/install/_static/docker-custom-agent-base.yml rename to internal/testrunner/runners/system/servicedeployer/_static/docker-custom-agent-base.yml diff --git a/internal/install/_static/terraform_deployer.yml b/internal/testrunner/runners/system/servicedeployer/_static/terraform_deployer.yml similarity index 100% rename from internal/install/_static/terraform_deployer.yml rename to internal/testrunner/runners/system/servicedeployer/_static/terraform_deployer.yml diff --git a/internal/install/_static/terraform_deployer_run.sh b/internal/testrunner/runners/system/servicedeployer/_static/terraform_deployer_run.sh similarity index 100% rename from internal/install/_static/terraform_deployer_run.sh rename to internal/testrunner/runners/system/servicedeployer/_static/terraform_deployer_run.sh diff --git a/internal/testrunner/runners/system/servicedeployer/compose.go b/internal/testrunner/runners/system/servicedeployer/compose.go index b7be23cd5..568ce7f0a 100644 --- a/internal/testrunner/runners/system/servicedeployer/compose.go +++ b/internal/testrunner/runners/system/servicedeployer/compose.go @@ -24,7 +24,7 @@ import ( // a Docker Compose file. type DockerComposeServiceDeployer struct { ymlPaths []string - sv ServiceVariant + variant ServiceVariant } type dockerComposeDeployedService struct { @@ -32,14 +32,14 @@ type dockerComposeDeployedService struct { ymlPaths []string project string - sv ServiceVariant + variant ServiceVariant } // NewDockerComposeServiceDeployer returns a new instance of a DockerComposeServiceDeployer. func NewDockerComposeServiceDeployer(ymlPaths []string, sv ServiceVariant) (*DockerComposeServiceDeployer, error) { return &DockerComposeServiceDeployer{ ymlPaths: ymlPaths, - sv: sv, + variant: sv, }, nil } @@ -49,7 +49,7 @@ func (d *DockerComposeServiceDeployer) SetUp(inCtxt ServiceContext) (DeployedSer service := dockerComposeDeployedService{ ymlPaths: d.ymlPaths, project: "elastic-package-service", - sv: d.sv, + variant: d.variant, } outCtxt := inCtxt @@ -71,15 +71,15 @@ func (d *DockerComposeServiceDeployer) SetUp(inCtxt ServiceContext) (DeployedSer } // Boot up service - if d.sv.active() { - logger.Infof("Using service variant: %s", d.sv.String()) + if d.variant.active() { + logger.Infof("Using service variant: %s", d.variant.String()) } serviceName := inCtxt.Name opts := compose.CommandOptions{ Env: append( []string{fmt.Sprintf("%s=%s", serviceLogsDirEnv, outCtxt.Logs.Folder.Local)}, - d.sv.Env...), + d.variant.Env...), ExtraArgs: []string{"--build", "-d"}, } err = p.Up(opts) @@ -138,7 +138,7 @@ func (s *dockerComposeDeployedService) Signal(signal string) error { opts := compose.CommandOptions{ Env: append( []string{fmt.Sprintf("%s=%s", serviceLogsDirEnv, s.ctxt.Logs.Folder.Local)}, - s.sv.Env...), + s.variant.Env...), ExtraArgs: []string{"-s", signal}, } if s.ctxt.Name != "" { @@ -166,14 +166,14 @@ func (s *dockerComposeDeployedService) TearDown() error { opts := compose.CommandOptions{ Env: append( []string{fmt.Sprintf("%s=%s", serviceLogsDirEnv, s.ctxt.Logs.Folder.Local)}, - s.sv.Env...), + s.variant.Env...), } processServiceContainerLogs(p, opts, s.ctxt.Name) if err := p.Down(compose.CommandOptions{ Env: append( []string{fmt.Sprintf("%s=%s", serviceLogsDirEnv, s.ctxt.Logs.Folder.Local)}, - s.sv.Env...), + s.variant.Env...), ExtraArgs: []string{"--volumes"}, // Remove associated volumes. }); err != nil { return errors.Wrap(err, "could not shut down service using Docker Compose") diff --git a/internal/testrunner/runners/system/servicedeployer/custom_agent.go b/internal/testrunner/runners/system/servicedeployer/custom_agent.go index 206452c2e..02165d0e0 100644 --- a/internal/testrunner/runners/system/servicedeployer/custom_agent.go +++ b/internal/testrunner/runners/system/servicedeployer/custom_agent.go @@ -8,6 +8,7 @@ import ( _ "embed" "fmt" "os" + "path/filepath" "github.com/pkg/errors" @@ -21,18 +22,25 @@ import ( "github.com/elastic/elastic-package/internal/stack" ) -const dockerCustomAgentName = "docker-custom-agent" +const ( + dockerCustomAgentName = "docker-custom-agent" + dockerCustomAgentDir = "docker_custom_agent" + dockerCustomAgentDockerfile = "docker-custom-agent-base.yml" +) + +//go:embed _static/docker-custom-agent-base.yml +var dockerCustomAgentDockerfileContent []byte // CustomAgentDeployer knows how to deploy a custom elastic-agent defined via // a Docker Compose file. type CustomAgentDeployer struct { - cfg string + dockerComposeFile string } // NewCustomAgentDeployer returns a new instance of a deployedCustomAgent. -func NewCustomAgentDeployer(cfgPath string) (*CustomAgentDeployer, error) { +func NewCustomAgentDeployer(dockerComposeFile string) (*CustomAgentDeployer, error) { return &CustomAgentDeployer{ - cfg: cfgPath, + dockerComposeFile: dockerComposeFile, }, nil } @@ -66,15 +74,20 @@ func (d *CustomAgentDeployer) SetUp(inCtxt ServiceContext) (DeployedService, err fmt.Sprintf("%s=%s", localCACertEnv, caCertPath), ) - ymlPaths, err := d.loadComposeDefinitions() + configDir, err := d.installDockerfile() if err != nil { - return nil, err + return nil, errors.Wrap(err, "could not create resources for custom agent") + } + + ymlPaths := []string{ + d.dockerComposeFile, + filepath.Join(configDir, dockerCustomAgentDockerfile), } service := dockerComposeDeployedService{ ymlPaths: ymlPaths, project: "elastic-package-service", - sv: ServiceVariant{ + variant: ServiceVariant{ Name: dockerCustomAgentName, Env: env, }, @@ -149,10 +162,25 @@ func (d *CustomAgentDeployer) SetUp(inCtxt ServiceContext) (DeployedService, err return &service, nil } -func (d *CustomAgentDeployer) loadComposeDefinitions() ([]string, error) { +// installDockerfile creates the files needed to run the custom elastic agent and returns +// the directory with these files. +func (d *CustomAgentDeployer) installDockerfile() (string, error) { locationManager, err := locations.NewLocationManager() if err != nil { - return nil, errors.Wrap(err, "can't locate Docker Compose file for Custom Agent deployer") + return "", errors.Wrap(err, "failed to find the configuration directory") } - return []string{d.cfg, locationManager.DockerCustomAgentDeployerYml()}, nil + + customAgentDir := filepath.Join(locationManager.DeployerDir(), dockerCustomAgentDir) + err = os.MkdirAll(customAgentDir, 0755) + if err != nil { + return "", errors.Wrap(err, "failed to create directory for custom agent files") + } + + customAgentDockerfile := filepath.Join(customAgentDir, dockerCustomAgentDockerfile) + err = os.WriteFile(customAgentDockerfile, dockerCustomAgentDockerfileContent, 0644) + if err != nil { + return "", errors.Wrap(err, "failed to create docker compose file for custom agent") + } + + return customAgentDir, nil } diff --git a/internal/testrunner/runners/system/servicedeployer/terraform.go b/internal/testrunner/runners/system/servicedeployer/terraform.go index 47279581a..329a18b4e 100644 --- a/internal/testrunner/runners/system/servicedeployer/terraform.go +++ b/internal/testrunner/runners/system/servicedeployer/terraform.go @@ -5,17 +5,38 @@ package servicedeployer import ( + _ "embed" + "fmt" "os" "path/filepath" + "strings" "github.com/pkg/errors" + "github.com/elastic/go-resource" + "github.com/elastic/elastic-package/internal/compose" "github.com/elastic/elastic-package/internal/configuration/locations" "github.com/elastic/elastic-package/internal/files" "github.com/elastic/elastic-package/internal/logger" ) +const ( + terraformDeployerDir = "terraform" + terraformDeployerYml = "terraform-deployer.yml" + terraformDeployerDockerfile = "Dockerfile" + terraformDeployerRun = "run.sh" +) + +//go:embed _static/terraform_deployer.yml +var terraformDeployerYmlContent string + +//go:embed _static/terraform_deployer_run.sh +var terraformDeployerRunContent string + +//go:embed _static/Dockerfile.terraform_deployer +var terraformDeployerDockerfileContent string + // TerraformServiceDeployer is responsible for deploying infrastructure described with Terraform definitions. type TerraformServiceDeployer struct { definitionsDir string @@ -32,9 +53,16 @@ func NewTerraformServiceDeployer(definitionsDir string) (*TerraformServiceDeploy func (tsd TerraformServiceDeployer) SetUp(inCtxt ServiceContext) (DeployedService, error) { logger.Debug("setting up service using Terraform deployer") - ymlPaths, err := tsd.loadComposeDefinitions() + configDir, err := tsd.installDockerfile() if err != nil { - return nil, errors.Wrap(err, "can't load Docker Compose definitions") + return nil, errors.Wrap(err, "can't install Docker Compose definitions") + } + + ymlPaths := []string{filepath.Join(configDir, terraformDeployerYml)} + envYmlPath := filepath.Join(tsd.definitionsDir, envYmlFile) + _, err = os.Stat(envYmlPath) + if err == nil { + ymlPaths = append(ymlPaths, envYmlPath) } service := dockerComposeDeployedService{ @@ -54,8 +82,12 @@ func (tsd TerraformServiceDeployer) SetUp(inCtxt ServiceContext) (DeployedServic return nil, errors.Wrap(err, "removing service logs failed") } + tfEnvironment := tsd.buildTerraformExecutorEnvironment(inCtxt) + // Set custom aliases, which may be used in agent policies. - serviceComposeConfig, err := p.Config(compose.CommandOptions{}) + serviceComposeConfig, err := p.Config(compose.CommandOptions{ + Env: tfEnvironment, + }) if err != nil { return nil, errors.Wrap(err, "could not get Docker Compose configuration for service") } @@ -65,7 +97,6 @@ func (tsd TerraformServiceDeployer) SetUp(inCtxt ServiceContext) (DeployedServic } // Boot up service - tfEnvironment := tsd.buildTerraformExecutorEnvironment(inCtxt) opts := compose.CommandOptions{ Env: tfEnvironment, ExtraArgs: []string{"--build", "-d"}, @@ -89,25 +120,49 @@ func (tsd TerraformServiceDeployer) SetUp(inCtxt ServiceContext) (DeployedServic return &service, nil } -func (tsd TerraformServiceDeployer) loadComposeDefinitions() ([]string, error) { +func (tsd TerraformServiceDeployer) installDockerfile() (string, error) { locationManager, err := locations.NewLocationManager() if err != nil { - return nil, errors.Wrap(err, "can't locate Docker Compose file for Terraform deployer") + return "", errors.Wrap(err, "failed to find the configuration directory") } - envYmlPath := filepath.Join(tsd.definitionsDir, envYmlFile) - _, err = os.Stat(envYmlPath) - if errors.Is(err, os.ErrNotExist) { - return []string{ - locationManager.TerraformDeployerYml(), - }, nil + tfDir := filepath.Join(locationManager.DeployerDir(), terraformDeployerDir) + + resources := []resource.Resource{ + &resource.File{ + Path: terraformDeployerYml, + Content: resource.FileContentLiteral(terraformDeployerYmlContent), + CreateParent: true, + }, + &resource.File{ + Path: terraformDeployerRun, + Content: resource.FileContentLiteral(terraformDeployerRunContent), + CreateParent: true, + }, + &resource.File{ + Path: terraformDeployerDockerfile, + Content: resource.FileContentLiteral(terraformDeployerDockerfileContent), + CreateParent: true, + }, } + + resourceManager := resource.NewManager() + resourceManager.RegisterProvider("file", &resource.FileProvider{ + Prefix: tfDir, + }) + + results, err := resourceManager.Apply(resources) if err != nil { - return nil, errors.Wrapf(err, "stat failed (path: %s)", envYmlPath) + var errors []string + for _, result := range results { + if err := result.Err(); err != nil { + errors = append(errors, err.Error()) + } + } + return "", fmt.Errorf("%w: %s", err, strings.Join(errors, ", ")) } - return []string{ - locationManager.TerraformDeployerYml(), envYmlPath, - }, nil + + return tfDir, nil } var _ ServiceDeployer = new(TerraformServiceDeployer)