diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..da87354 --- /dev/null +++ b/.gitignore @@ -0,0 +1,71 @@ +# Project specific +release +coverage.* + +# Created by .ignore support plugin (hsz.mobi) +### macOS template +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Go template +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# CMake +cmake-build-*/ + +# File-based project format +*.iws + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +.idea + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cb07209 --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +BINARY := manny +PKGS := $(shell go list ./... | grep -v /vendor) + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +.PHONY: all build clean spotless test cover release artifactory-upload ${GOBIN}/${BINARY} + +all: vendor test build + +build: ${GOBIN}/${BINARY} + +${GOBIN}/${BINARY}: + go build -o $@ main.go + +clean: + @echo "Removing package object files..." + @go clean ${PKGS} + @echo "Removing cache test results..." + @go clean -testcache + +spotless: clean + @echo "Removing vendor directory..." + @-rm -rf vendor + +vendor: spotless + @echo "Refreshing dependencies..." + @go mod tidy && go mod vendor + +test: + go test ${PKGS} ${TESTARGS} + +cover: TESTARGS=-coverprofile=coverage.out +cover: test + go tool cover -func=coverage.out -o coverage.txt + go tool cover -html=coverage.out -o coverage.html + @cat coverage.txt + @echo "Run 'open coverage.html' to view coverage report." + +VERSION ?= vlatest +PLATFORMS := windows linux darwin +os = $(word 1, $@) + +.PHONY: $(PLATFORMS) +$(PLATFORMS): + mkdir -p release + GOOS=$(os) GOARCH=amd64 go build -o release/$(BINARY)-$(VERSION)-$(os)-amd64 + +release: windows linux darwin diff --git a/README.md b/README.md index f79f4d5..de45827 100644 --- a/README.md +++ b/README.md @@ -1 +1,61 @@ # manny + +[![Build Status][BuildStatusImg]][BuildMasterURL] +[![Code Coverage][CodecovImg]][CodecovURL] + +Argo CD tool to generate K8s manifests from GitOps repo + +## Installation + +This process for adding additional custom tools to Argo CD is documented here: + +* https://argoproj.github.io/argo-cd/operator-manual/custom_tools/ + +The ArgoCD repo-server deployment must be updated to include an init container that downloads and installed manny. + +``` yaml +spec: + template: + spec: + volumes: + - name: custom-tools + emptyDir: {} + initContainers: + - name: download-tools + image: alpine:3.8 + command: [sh, -c] + args: + - wget -q -O manny.gz https://github.com/keikoproj/manny/manny-vlatest-linux-amd64.gz && + gunzip manny.gz && + chmod +x manny && + mv manny /custom-tools/manny + volumeMounts: + - mountPath: /custom-tools + name: custom-tools + containers: + - name: argocd-repo-server + volumeMounts: + - mountPath: /usr/local/bin/manny + name: custom-tools + subPath: manny +``` + +The Argo CD configmap must be updated to install manny as a plugin. + +``` yaml +data: + configManagementPlugins: | + - name: manny + generate: + command: [sh, -c] + args: ["manny build ."] +``` + +Also, for each Argo CD app that intends to use manny, the Application must be updated to reference the manny plugin. + +``` yaml +spec: + source: + plugin: + name: manny +``` \ No newline at end of file diff --git a/cmd/build.go b/cmd/build.go new file mode 100644 index 0000000..f6fcce4 --- /dev/null +++ b/cmd/build.go @@ -0,0 +1,88 @@ +package cmd + +import ( + "errors" + "fmt" + "path/filepath" + + "github.com/go-git/go-git/v5" + "github.com/spf13/cobra" + "go.uber.org/zap" + + "github.com/keikoproj/manny/configurator" + "github.com/keikoproj/manny/utils" +) + +var ( + buildCmd = &cobra.Command{ + Use: "build path/to/stacks", + Short: "Builds a manny deployment", + Long: "Builds a manny deployment", + Example: "manny build usw2", + RunE: buildConfig, + } +) + +func buildConfig(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New(ErrMissingArg) + } + + path := args[0] + + Logger.Debug("Git repository path", zap.String("repo-path", filepath.Join(path, git.GitDirName))) + + gitURL, err := utils.GitRepoRemote(path) + if err != nil { + return err + } + + // create a new configurator with defaults + config := configurator.New(configurator.Config{ + Path: path, + Logger: Logger, + GitURL: gitURL, + }) + + deployments, err := config.CreateDeployments() + if err != nil { + return err + } + + Logger.Debug("Deployments created", zap.Any("CloudResourceDeployments", len(deployments))) + + if validate { + if err := deployments.Validate(); err != nil { + Logger.Error("Validation failed", zap.Error(err)) + return err + } + } + + // early return for dry run + if dryRun { + return nil + } + + // render the manifest in a given format + bytes, err := deployments.Render(format) + if err != nil { + return err + } + + // output to location + if output != "stdout" { + // File location validation + ok, err := utils.ValidateAndWrite(output, bytes) + if !ok { + return err + } + + // early return + return nil + } + + // write to stdout + fmt.Printf("%s", bytes) + + return nil +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..c522095 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,88 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +const ( + ErrMissingArg = "missing argument" + ConfigDebug = "debug" + ConfigFormat = "format" + OutputFormat = "output" + ConfigValidate = "validate" + ConfigDryRun = "dry-run" +) + +var ( + Logger *zap.Logger + + // flag storage + debug bool + format string + output string + dryRun bool + validate bool + + // Commands + rootCmd = &cobra.Command{ + Use: "manny", + Short: "Argo CD tool to generate K8s manifests from GitOps repo", + Long: `Argo CD tool to generate K8s manifests from GitOps repo`, + } +) + +func Execute() { + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} + +func init() { + cobra.OnInitialize(initLogger) + + rootCmd.AddCommand(buildCmd) + + rootCmd.PersistentFlags().BoolVarP(&debug, ConfigDebug, "D", false, "sets debug mode") + buildCmd.PersistentFlags().StringVarP(&format, ConfigFormat, "f", "yaml", "sets output format") + buildCmd.PersistentFlags().StringVarP(&output, OutputFormat, "o", "stdout", "sets file location") + buildCmd.PersistentFlags().BoolVarP(&validate, ConfigValidate, "", true, "validates the CloudFormation output") + buildCmd.PersistentFlags().BoolVarP(&dryRun, ConfigDryRun, "", false, "does not output a CloudResource") +} + +// initLogger reads in config file and ENV variables if set. +func initLogger() { + cfg := zap.Config{ + Encoding: "console", + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + Level: zap.NewAtomicLevelAt(zapcore.InfoLevel), + EncoderConfig: zapcore.EncoderConfig{ + MessageKey: "message", + + LevelKey: "level", + EncodeLevel: zapcore.CapitalLevelEncoder, + + TimeKey: "time", + EncodeTime: zapcore.ISO8601TimeEncoder, + + CallerKey: "caller", + EncodeCaller: zapcore.ShortCallerEncoder, + }, + } + + if debug { + cfg.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel) + } + + l, err := cfg.Build() + if err != nil { + fmt.Printf("Error setting up logger: %s", err) + } + + Logger = l +} diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..044fb78 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,2 @@ +ignore: + - cmd \ No newline at end of file diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..b17051d --- /dev/null +++ b/config/config.go @@ -0,0 +1,101 @@ +package config + +import ( + "encoding/json" + "fmt" + "github.com/qri-io/jsonschema" + "gopkg.in/yaml.v3" +) + +var Schemaconfig = []byte( +`{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "template": { + "type": "object", + "properties":{ + "type":{ + "type":"string", + "enum": [ "file", "http", "s3" ] + }, + "path": { + "type": "string" + } + } + }, + "parameters" : { + "type": ["object", "null"] + }, + "timeout":{ + "type": ["integer", "null"] + }, + "stackname":{ + "type": ["string", "null"] + }, + "tags":{ + "type": ["object", "null"] + }, + "rolearn":{ + "type": ["string", "null"] + }, + "servicerolearn":{ + "type": ["string", "null"] + }, + "externalID":{ + "type": ["string", "null"] + }, + "duration":{ + "type": ["string", "null"] + }, + "acctnum":{ + "type": ["string", "null"] + }, + "expirywindow":{ + "type": ["string", "null"] + }, + "env":{ + "type": ["string", "null"] + }, + "region":{ + "type": ["string", "null"] + }, + "syncwave":{ + "type": ["integer", "null"] + } + }, + "additionalProperties": false, + "required": [ + "template" + ] +}`) + +func ValidateJSONSchema(schemaData []byte) (*jsonschema.RootSchema, error) { + rs := &jsonschema.RootSchema{} + if err := json.Unmarshal(schemaData, rs); err != nil { + return nil, fmt.Errorf( "schema unmarshalling error %s", err) + } + return rs, nil +} + + +func ConfigValidate(inputSchemaData, schemaData []byte) (bool, error) { + rs, err := ValidateJSONSchema(schemaData) + if err != nil{ + return false, fmt.Errorf("failed ValidateJSONSchema due to %s", err) + } + var body interface{} + if err := yaml.Unmarshal(inputSchemaData, &body); err != nil{ + return false, fmt.Errorf("yaml unmarshal failed inputSchemaData due to: %s", err) + } + inputContentJson, err := json.Marshal(body) + if err != nil { + return false, fmt.Errorf("json marshal failed inputContentJson due to: %s", err) + } + + if errs, _ := rs.ValidateBytes(inputContentJson); len(errs) > 0 { + return false, fmt.Errorf("validate failed due to: %s", errs) + } + + return true, nil +} \ No newline at end of file diff --git a/config/config_test.go b/config/config_test.go new file mode 100644 index 0000000..747847a --- /dev/null +++ b/config/config_test.go @@ -0,0 +1,70 @@ +package config + +import ( + "github.com/onsi/gomega" + "testing" +) +//Test_Validate_Success +func Test_Validate_Success(t *testing.T) { + var schemaData = []byte(`{ + "title": "Person", + "type": "object", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "age": { + "description": "Age in years", + "type": "integer", + "minimum": 0 + } + }, + "required": ["firstName", "lastName"] + }`) + g := gomega.NewGomegaWithT(t) + var valid = []byte(`{ + "firstName": "foo", + "lastName": "bar", + "age": 24 + }`) + ret, _ := ConfigValidate(valid, schemaData) + g.Expect(ret).To(gomega.BeTrue()) +} + +//Test_Validate_Failed +func Test_Validate_Failed(t *testing.T) { + var schemaData = []byte(`{ + "title": "Person", + "type": "object", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "age": { + "description": "Age in years", + "type": "integer", + "minimum": 0 + }, + }, + "required": ["firstName", "lastName"] + }`) + g := gomega.NewGomegaWithT(t) + var valid = []byte(`{ + "firstName": "foo", + "lastName": 45 + }`) + ret, _ := ConfigValidate(valid, schemaData) + g.Expect(ret).To(gomega.BeFalse()) +} + +func TestGetConfigSchemaError(t *testing.T) { + g := gomega.NewGomegaWithT(t) + _, err := ValidateJSONSchema([]byte("invalid.schema")) + g.Expect(err).To(gomega.HaveOccurred()) +} \ No newline at end of file diff --git a/configurator/configurator.go b/configurator/configurator.go new file mode 100644 index 0000000..4d26fdd --- /dev/null +++ b/configurator/configurator.go @@ -0,0 +1,713 @@ +package configurator + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "os" + "path" + "path/filepath" + "regexp" + "strconv" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3/s3iface" + "go.uber.org/zap" + "gopkg.in/yaml.v3" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/imdario/mergo" + gitopsv1alpha1 "github.com/keikoproj/cloudresource-manager/api/v1alpha1" + + "github.com/keikoproj/manny/config" + "github.com/keikoproj/manny/configurator/mocks" +) + +const ( + // MannyConfigName is a special name in manny that denotes the name of the configuration file + MannyConfigName = "config.yaml" + + // EmptyResourceRegExp Regex details : Should start with "Resources". + //Can have space in between. + //Should have ":" delimiter. + //Can have space in between. + //Can have "{}" or not. Space allowed between curly braces. + EmptyResourceRegExp = "^(Resources(\"{0,1})(\\s*):(\\s*)({(\\s*)}){0,1})$" +) + +var counter int +var baseDir []string +var AcceptedExtensions = []string{".yaml", ".yml", ".json"} + +type SL struct { + // stack lists + StackList []map[string]string +} + +var s SL + +type CloudResources []*CloudResourceDeployment + +// Validate runs various validations against the deployments +func (c CloudResources) Validate() error { + lookupTable := map[string]bool{} + + for _, resource := range c { + // find duplicate stack names + name := resource.Spec.Cloudformation.Stackname + if lookupTable[name] { + return errors.New("duplicate stack name found: " + name) + } + + lookupTable[name] = true + } + + return nil +} + + + +// Render returns a json or yaml byte array of the resources +func (c CloudResources) Render(format string) (bytes []byte, err error) { + switch format { + case "json": + for _, d := range c { + deployment, err := json.Marshal(d) + if err != nil { + return nil, err + } + + bytes = append(bytes, deployment...) + } + default: + for _, d := range c { + bytes = append(bytes, []byte("---\n")...) + + deployment, err := yaml.Marshal(d) + if err != nil { + return nil, err + } + + bytes = append(bytes, deployment...) + } + } + + return +} + +// CloudResourceDeployment is a Custom Resource duplicated from the GitOps controller. +// We had to duplicate it here because the built-in Kubernetes types are made for machine processing +// with JSON. In order to produce a YAML manifest that someone can read we have to represent that +// structure in our code. +type CloudResourceDeployment struct { + Kind string `yaml:"kind" json:"kind"` + APIVersion string `yaml:"apiVersion" json:"apiVersion"` + Metadata Metadata `yaml:"metadata" json:"metadata"` + Spec gitopsv1alpha1.CloudResourceDeploymentSpec `yaml:"spec" json:"spec"` +} + +type Metadata struct { + Name string `yaml:"name" json:"name"` + Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"` + Labels map[string]string `yaml:"labels,omitempty" json:"labels,omitempty"` + Annotations map[string]string `yaml:"annotations,omitempty" json:"annotations,omitempty"` +} + +type TemplatePath string + +func (t TemplatePath) Parse() Template { + var template Template + + switch true { + // s3 handler + case strings.HasPrefix(t.String(), "s3://"): + template.Type = "s3" + template.Path = t.String() + // http(s) handler + case strings.HasPrefix(t.String(), "http://"): + template.Type = "http" + template.Path = t.String() + // default is the file handler + default: + template.Type = "file" + template.Path = strings.TrimPrefix(t.String(), "file://") + } + + return template +} + +func contains(arr []string, str string) bool { + for _, a := range arr { + if a == str { + return true + } + } + return false +} + +func (m *MannyConfig) CheckForEmptyResource() (bool,error) { + regex,err := regexp.Compile(EmptyResourceRegExp) + if err != nil { + return true,err + } + // Don't generate manifests for empty resources + indexOfResource := strings.Index(string(m.CloudFormation), "Resource") + if indexOfResource == -1 { + return false,nil + } + resourceSubString := m.CloudFormation[indexOfResource:] + + return regex.Match(resourceSubString),nil +} + +func (m *MannyConfig) runResolvers(stackList []map[string]string, fPath string) error { + m.OutputParameters = map[string]string{} + + for key, value := range m.InputParameters { + var actualValue string + m.OutputParameters[key] = value.Value + switch value.Tag { + case "!environment_variable": + actualValue = os.Getenv(value.Value) + m.OutputParameters[key] = actualValue + case "!file_contents": + target := filepath.Join(filepath.Dir(fPath), value.Value) + data, err := ioutil.ReadFile(target) + if err != nil { + return err + } + m.OutputParameters[key] = string(data) + case "!stack_output": + sOutput := strings.Split(value.Value, "::") + sOutput[0] = filepath.Base(sOutput[0]) + for _, v := range stackList { + if v[sOutput[0]] != "" { + m.OutputParameters[key] = value.Tag + " " + v[sOutput[0]] + "::" + sOutput[1] + break + } else { + m.OutputParameters[key] = value.Tag + " " + value.Value + } + } + } + } + + return nil +} + +func (c Configurator) mannyConfigValidate(path string) bool { + inputConfigData, err := ioutil.ReadFile(path) + if err != nil { + return false + } + + _, err = config.ConfigValidate(inputConfigData, config.Schemaconfig) + if err != nil { + c.logger.Info("mannyConfig validation failed", zap.Error(err)) + return false + } + + c.logger.Debug("mannyConfig validation successful", zap.String("mannyConfig", path)) + return true +} + +func (t TemplatePath) IsEmpty() bool { + return t == "" +} + +func (t TemplatePath) String() string { + return string(t) +} + +type Template struct { + Type string `yaml:"type"` + Path string `yaml:"path"` + Version string `yaml:"version"` + Name string `yaml:"name"` +} + +// MannyConfig is a manny config. This is only read in yaml but can the Custom Resource that it gets written to can +// output in YAML or JSON. +type MannyConfig struct { + // FoundAt is the directory where Manny found the config + FoundAt string + // Template is the CloudFormation template provider config. + // Can be one of "file", "s3" + Template Template `yaml:"template"` + // TemplatePath provides the same functionality as Template but in one parseable string + TemplatePath TemplatePath `yaml:"template_path"` + // SyncWave + SyncWave int `yaml:"syncwave,omitempty"` + // InputParameters map to CloudFormation parameter values + InputParameters map[string]yaml.Node `yaml:"parameters,omitempty"` + // OutputParameters are derived from input parameters when using resolvers. These values are the ones that show up + // in the custom resource. + OutputParameters map[string]string + // Tags are CloudFormation tags to apply + Tags map[string]string `yaml:"tags"` + // StackName is the stack name to be used during execution + StackName string `yaml:"stackname"` + // RoleArn is the role to execute the CloudFormation with + RoleArn string `yaml:"rolearn"` + // ServiceRole that Cloudformation will assume. This is to have more security. + ServiceRoleARN string `yaml:"servicerolearn"` + // Expiry duration of the STS credentials. Defaults to 15 minutes if not set. + Duration time.Duration `yaml:"duration"` + // Optional ExternalID to pass along, defaults to nil if not set. + ExternalID string `yaml:"externalID,omitempty"` + // Optional AccountID. + AccountID string `yaml:"acctnum"` + // Optional Environment. + Environment string `yaml:"env,omitempty"` + // If ExpiryWindow is 0 or less it will be ignored. + ExpiryWindow time.Duration `yaml:"expirywindow,omitempty"` + // Timeout support + Timeout int `yaml:"timeout"` + //Region support + Region string `yaml:"region"` + // Base refers to additional configuration that should be loaded + Base string `yaml:"base"` + CloudFormation []byte +} + +// Configurator builds final configuration from many configurations +type Configurator struct { + // Bases is the list of configs to be merged + Bases []MannyConfig + // Global is the config with all bases consumed + Global MannyConfig + // Stacks are the stack specific configs + Stacks []MannyConfig + // Origin is the original path that contains a config.yaml + Origin string + // GitURL is the remote URL used by Git + GitURL string + // References is a list of bases used + References []string + // StackPrefix is the generated prefix of the stack + StackPrefix string + // StackTable is a reference table for stacks + StackTable map[string]bool + + logger *zap.Logger + s3Client s3iface.S3API +} + +type Config struct { + Path string + GitURL string + Logger *zap.Logger + S3Client s3iface.S3API +} + +// New creates a new configurator +func New(config Config) Configurator { + c := Configurator{ + logger: config.Logger, + GitURL: config.GitURL, + } + + // Set the origin to an absolute path + path, _ := filepath.Abs(config.Path) + c.Origin = path + "/" + + switch config.S3Client.(type) { + case *mocks.S3API: + c.s3Client = config.S3Client + default: + // use user credentials unless otherwise specified + config.Logger.Debug("S3Client not set, setting default") + + sess := session.Must(session.NewSessionWithOptions(session.Options{ + Config: aws.Config{Region: aws.String("us-west-2")}, + SharedConfigState: session.SharedConfigEnable, + })) + + c.s3Client = s3.New(sess) + } + + return c +} + +func (c *Configurator) CreateDeployments() (CloudResources, error) { + // build the config + err := c.loadBases() + if err != nil { + return nil, err + } + + // render a manifest for deployment + return c.loadStacks() +} + +// loadBases resolves base configs into the Global config +func (c *Configurator) loadBases() error { + // Abs() does not include a trailing slash + config := c.Origin + MannyConfigName + + c.logger.Debug("Finding config", zap.String("path", config)) + + // check to see if the config file exists + _, err := os.Stat(config) + if !os.IsNotExist(err) { + // Unmarshal the given config to determine if there are bases and recurses them + if err := c.unmarshal(config); err != nil { + return err + } + } + + // Merge all the configs into one, starting with the bases + c.Global, err = c.mergeBases(c.Bases) + if err != nil { + return err + } + + c.determineStackPrefix() + + c.logger.Debug("Merged base configs", zap.Int("BaseConfigs", len(c.Bases)), + zap.Any("Global Config", c.Global)) + + return nil +} + +func (c *Configurator) determineStackPrefix() { + if counter == 0 { + baseDir = strings.Split(filepath.Clean(c.Origin), "/") + } + counter++ + dir := strings.Split(filepath.Clean(c.Origin), "/") + a := len(dir) - len(baseDir) + if a >= 0{ + c.StackPrefix = strings.Join(dir[len(dir)-(a):], "-") + } + c.logger.Debug("Stack prefix determined", zap.String("StackPrefix", c.StackPrefix)) +} + +func (c Configurator) mergeBases(bases []MannyConfig) (MannyConfig, error) { + var global MannyConfig + + for _, config := range bases { + if err := mergo.Merge(&global, config); err != nil { + return MannyConfig{}, err + } + } + + return global, nil +} + +func (c *Configurator) unmarshal(parentPath string) error { + c.logger.Debug("Reading file", zap.String("path", parentPath)) + + data, err := ioutil.ReadFile(parentPath) + if err != nil { + return err + } + + var config MannyConfig + if err := yaml.Unmarshal(data, &config); err != nil { + return err + } + + config.FoundAt = filepath.Clean(filepath.Dir(parentPath)) + c.logger.Debug("Storing base location", zap.String("path", config.FoundAt)) + + // Recurse bases + if config.Base != "" { + c.logger.Debug("Base detected", zap.String("path", config.Base)) + + // determine absolute path + target := filepath.Join(filepath.Dir(parentPath), config.Base) + + c.logger.Debug("Determining target", zap.String("ConfigPath", config.Base), + zap.String("TargetPath", target)) + + // @ToDo: Convert to map/lookup table + for _, basePath := range c.References { + if target == basePath { + return errors.New("circular dependency found in " + target) + } + } + + if len(c.References)+1 > 10 { + return errors.New("more than 10 referenced bases") + } + + // track references + c.References = append(c.References, target) + + if err := c.unmarshal(target); err != nil { + return err + } + } + + c.Bases = append(c.Bases, config) + + return nil +} + +// loadStacks loads stacks from the local directory and creates CloudResourceDeployments from them +func (c *Configurator) loadStacks() (CloudResources, error) { + var resources CloudResources + + c.logger.Debug("Looking for stack configs", zap.String("path", c.Origin)) + + // find stack files in the origin directory + files, err := ioutil.ReadDir(c.Origin) + if err != nil { + return nil, err + } + // process VPC stacks first and then dir + var files_inorder, d []os.FileInfo + for i, f := range files { + if f.IsDir() { + d = append(d, files[i]) + }else { + files_inorder = append(files_inorder, f) + } + } + files_inorder = append(files_inorder, d...) + + // load the stack configs + for _, f := range files_inorder { + // Resolve relative directories + target := filepath.Join(filepath.Dir(c.Origin+"/"), f.Name()) + + if f.Name() == MannyConfigName { + continue + } + + // Recurse sub directories + if f.IsDir() { + c.logger.Debug("Walking directory for config", zap.String("path", target)) + + config := New(Config{ + Path: target + "/", + Logger: c.logger, + GitURL: c.GitURL, + }) + + if err := config.loadBases(); err != nil { + c.logger.Info("Unable to load base from higher level directory", zap.Error(err)) + continue + } + + deployments, err := config.loadStacks() + if err != nil { + c.logger.Info("Unable to load stacks from higher level directory", zap.Error(err)) + continue + } + + resources = append(resources, deployments...) + + continue + } + + extension := filepath.Ext(target) + if contains(AcceptedExtensions, extension) && c.mannyConfigValidate(target) { + // Handle stack generation + c.logger.Debug("Reading stack config", zap.String("path", target)) + + data, err := ioutil.ReadFile(target) + if err != nil { + return nil, err + } + + var config MannyConfig + // unmarshal the stack config + err = yaml.Unmarshal(data, &config) + if err != nil { + return nil, err + } + + // If no stack name is found, generate one + m := make(map[string]string) + if config.StackName == "" { + config.StackName = strings.TrimSuffix(f.Name(), filepath.Ext(f.Name())) + if c.StackPrefix != "" { + config.StackName = c.StackPrefix + "-" + config.StackName + } + m[f.Name()] = config.StackName + s.StackList = append(s.StackList, m) + } else { + m[f.Name()] = config.StackName + s.StackList = append(s.StackList, m) + } + + // determine whether the template block or template_path is used + if !config.TemplatePath.IsEmpty() { + config.Template = config.TemplatePath.Parse() + } + + switch config.Template.Type { + case "file": + // Resolve relative directories + target := filepath.Join(filepath.Dir(target), config.Template.Path) + + // Unmarshal the CloudFormation + config.CloudFormation, err = ioutil.ReadFile(target) + if err != nil { + return nil, err + } + case "http": + resp, err := http.Get(config.Template.Path) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusOK { + config.CloudFormation, err = ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + } else { + c.logger.Debug("http handler template download: http_status_code", zap.String("http_status", resp.Status)) + } + case "s3": + //Parsing s3 bucket and key + u, _ := url.Parse(config.Template.Path) + if u.Host == "" || u.Path == "/" || u.Path == "" { + return nil, errors.New("s3 Bucket or Key not found: " + config.Template.Path) + } + + // Support s3 http url format + if strings.Contains(u.Host, ".s3.amazonaws.com") { + u.Host = strings.ReplaceAll(u.Host, ".s3.amazonaws.com", "") + } + + // tmpfile + fPath, err := ioutil.TempFile("", "s3-"+path.Base(config.Template.Path)) + if err != nil { + return nil, err + } + defer os.Remove(fPath.Name()) // clean up + + //Download cfn template from s3 + downloader := s3manager.NewDownloaderWithClient(c.s3Client) + _, err = downloader.Download(fPath, + &s3.GetObjectInput{ + Bucket: aws.String(u.Host), + Key: aws.String(u.Path), + }) + if err != nil { + return nil, err + } + + // Unmarshal the CloudFormation + config.CloudFormation, err = ioutil.ReadFile(fPath.Name()) + if err != nil { + return nil, err + } + } + // Don't generate manifests for empty resources + resourceEmpty,err := config.CheckForEmptyResource() + if err != nil { + c.logger.Error("Error while check for resource count") + } + if !resourceEmpty { + c.Stacks = append(c.Stacks, config) + } + + } else { + c.logger.Debug("Skipping file", zap.String("path", target)) + } + } + + r, err := c.generateCR(c.Stacks) + if err != nil { + return nil, err + } + + resources = append(resources, r...) + + return resources, nil +} + +func (c Configurator) generateCR(stacks []MannyConfig) (CloudResources, error) { + var manifests CloudResources + + c.logger.With(zap.String("GitRemote", c.GitURL)).Debug("Writing git remote") + + for _, stack := range stacks { + // Run resolvers + if err := stack.runResolvers(s.StackList, c.Origin); err != nil { + return nil, err + } + + // Merge the Global config and the Stack config + if err := mergo.Merge(&stack, c.Global); err != nil { + return nil, err + } + if stack.SyncWave != 0 { + manifests = append(manifests, &CloudResourceDeployment{ + Kind: "CloudResourceDeployment", + APIVersion: "cloudresource.keikoproj.io/v1alpha1", + Metadata: Metadata{ + Name: stack.StackName, + Annotations: map[string]string{ + "source": c.GitURL, + "argocd.argoproj.io/sync-wave": strconv.Itoa(stack.SyncWave), + }, + }, + Spec: gitopsv1alpha1.CloudResourceDeploymentSpec{ + Cloudformation: &gitopsv1alpha1.StackSpec{ + Parameters: stack.OutputParameters, + Tags: stack.Tags, + Template: fmt.Sprintf("%s", stack.CloudFormation), + Stackname: stack.StackName, + CARole: gitopsv1alpha1.AssumeRoleProvider{ + RoleARN: stack.RoleArn, + RoleSessionName: "gitops-deployment", + ServiceRoleARN: stack.ServiceRoleARN, + ExternalID: stack.ExternalID, + AccountID: stack.AccountID, + Environment: stack.Environment, + Duration: stack.Duration, + ExpiryWindow: stack.ExpiryWindow, + }, + Timeout: stack.Timeout, + Region: stack.Region, + }, + }, + }) + } else { + manifests = append(manifests, &CloudResourceDeployment{ + Kind: "CloudResourceDeployment", + APIVersion: "cloudresource.keikoproj.io/v1alpha1", + Metadata: Metadata{ + Name: stack.StackName, + Annotations: map[string]string{ + "source": c.GitURL, + }, + }, + Spec: gitopsv1alpha1.CloudResourceDeploymentSpec{ + Cloudformation: &gitopsv1alpha1.StackSpec{ + Parameters: stack.OutputParameters, + Tags: stack.Tags, + Template: fmt.Sprintf("%s", stack.CloudFormation), + Stackname: stack.StackName, + CARole: gitopsv1alpha1.AssumeRoleProvider{ + RoleARN: stack.RoleArn, + RoleSessionName: "gitops-deployment", + ServiceRoleARN: stack.ServiceRoleARN, + ExternalID: stack.ExternalID, + AccountID: stack.AccountID, + Environment: stack.Environment, + Duration: stack.Duration, + ExpiryWindow: stack.ExpiryWindow, + }, + Timeout: stack.Timeout, + Region: stack.Region, + }, + }, + }) + } + } + + return manifests, nil +} \ No newline at end of file diff --git a/configurator/configurator_test.go b/configurator/configurator_test.go new file mode 100644 index 0000000..b1fdcd6 --- /dev/null +++ b/configurator/configurator_test.go @@ -0,0 +1,274 @@ +package configurator + +import ( + "context" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/service/s3" + "github.com/stretchr/testify/mock" + "github.com/keikoproj/cloudresource-manager/api/v1alpha1" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + + "github.com/keikoproj/manny/configurator/mocks" +) + +func TestExamples(t *testing.T) { + var table = []struct { + name string + path string + length int + error bool + }{ + {"nested configuration", "./examples/nested/aws/usw2/", 6, false}, + {"static configuration", "./examples/static/aws/usw2/", 3, false}, + {"static configuration", "./examples/static/aws/use2/", 2, false}, + {"s3 configuration", "./examples/s3/aws/usw2/", 2, false}, + {"http configuration", "./examples/http/aws/usw2/", 1, false}, + {"resolvers configuration", "./examples/resolvers/", 2, false}, + {"error on circular dependencies", "./tests/circular-dependency/", 0, true}, + {"error on greater than 10 base references", "./tests/base-reference-cap/", 0, true}, + { "don't create stack for empty resources", "./examples/complex/missing-resources/", 0, false}, + } + + for _, tt := range table { + t.Run(tt.name, func(t *testing.T) { + // set working directory as manny root directory + _, filename, _, _ := runtime.Caller(0) + wd := filepath.Join(filename, "../..") + err := os.Chdir(wd) + + // handle relative and absolute paths + if strings.HasPrefix(tt.path, ".") { + tt.path = filepath.Join(wd, tt.path) + } + + // show debug logs if a test fails + log, err := initTestLogger() + if err != nil { + t.Error(err) + } + + // mock s3 api + mockS3 := new(mocks.S3API) + + config := New(Config{ + Path: tt.path, + Logger: log, + S3Client: mockS3, + }) + + if tt.name == "s3 configuration" { + ctx := context.Background() + testFileBody := `{ + "Resources" : { + "HelloBucket" : { + "Type" : "AWS::S3::Bucket", + "Properties" : { + "AccessControl" : "PublicRead" + } + } + } + }` + contentLength := int64(len(testFileBody)) + s3GetObjectOutput := &s3.GetObjectOutput{ + Body: ioutil.NopCloser(strings.NewReader(testFileBody)), + ContentLength: &contentLength, + } + mockS3.On("GetObjectWithContext", ctx, mock.Anything, mock.Anything). + Return(s3GetObjectOutput, nil) + } + + deployments, err := config.CreateDeployments() + if err != nil && !tt.error { + t.Error(err) + } + + if len(deployments) != tt.length { + t.Errorf("Incorrect length got: %d want: %d\n", len(deployments), tt.length) + } + invalidConfig :=config.mannyConfigValidate("foo") + if invalidConfig{ + t.Errorf("mannyConfigValidate failed") + } + }) + } +} + +func Test_CloudResourcesValidate(t *testing.T) { + var table = []struct { + name string + resources CloudResources + error bool + }{ + {"duplicate stack name", CloudResources{ + { + Kind: "", + APIVersion: "", + Metadata: Metadata{}, + Spec: v1alpha1.CloudResourceDeploymentSpec{ + Cloudformation: &v1alpha1.StackSpec{ + Parameters: nil, + Tags: nil, + Template: "", + Stackname: "test1", + CARole: v1alpha1.AssumeRoleProvider{}, + }, + }, + }, + { + Kind: "", + APIVersion: "", + Metadata: Metadata{}, + Spec: v1alpha1.CloudResourceDeploymentSpec{ + Cloudformation: &v1alpha1.StackSpec{ + Parameters: nil, + Tags: nil, + Template: "", + Stackname: "test1", + CARole: v1alpha1.AssumeRoleProvider{}, + }, + }, + }, + }, true}, + {"happy path", CloudResources{ + { + Kind: "", + APIVersion: "", + Metadata: Metadata{}, + Spec: v1alpha1.CloudResourceDeploymentSpec{ + Cloudformation: &v1alpha1.StackSpec{ + Parameters: nil, + Tags: nil, + Template: "", + Stackname: "test1", + CARole: v1alpha1.AssumeRoleProvider{}, + }, + }, + }, + { + Kind: "", + APIVersion: "", + Metadata: Metadata{}, + Spec: v1alpha1.CloudResourceDeploymentSpec{ + Cloudformation: &v1alpha1.StackSpec{ + Parameters: nil, + Tags: nil, + Template: "", + Stackname: "test2", + CARole: v1alpha1.AssumeRoleProvider{}, + }, + }, + }, + }, true}, + } + + for _, tt := range table { + t.Run(tt.name, func(t *testing.T) { + if err := tt.resources.Validate(); err != nil && !tt.error { + t.Error(err) + } + }) + } +} + +func Test_CloudResourcesRender(t *testing.T) { + var table = []struct { + name string + resources CloudResources + jsonLength int + yamlLength int + error bool + }{ + {"happy path", CloudResources{ + { + Kind: "", + APIVersion: "", + Metadata: Metadata{}, + Spec: v1alpha1.CloudResourceDeploymentSpec{ + Cloudformation: &v1alpha1.StackSpec{ + Parameters: nil, + Tags: nil, + Template: "", + Stackname: "test1", + CARole: v1alpha1.AssumeRoleProvider{ + RoleARN: "arn:aws:iam::123456789000:role/cfn-fa-role", + RoleSessionName: "gitops-deployment", + }, + }, + }, + }, + { + Kind: "", + APIVersion: "", + Metadata: Metadata{}, + Spec: v1alpha1.CloudResourceDeploymentSpec{ + Cloudformation: &v1alpha1.StackSpec{ + Parameters: nil, + Tags: nil, + Template: "", + Stackname: "test2", + CARole: v1alpha1.AssumeRoleProvider{ + RoleARN: "arn:aws:iam::123456789000:role/cfn-fa-role", + RoleSessionName: "gitops-deployment", + }, + }, + }, + }, + }, 446, 522, true}, + } + + for _, tt := range table { + t.Run(tt.name, func(t *testing.T) { + bytes, err := tt.resources.Render("yaml") + if err != nil && !tt.error { + t.Error(err) + } + + // TODO: Should validate contents of the yaml doc, not just length + if len(bytes) != tt.yamlLength { + t.Logf(string(bytes)) + t.Errorf("YAML byte array length is incorrect; got: %d want %d", len(bytes), tt.yamlLength) + } + + bytes, err = tt.resources.Render("json") + if err != nil && !tt.error { + t.Error(err) + } + + // TODO: Should validate contents of the json doc, not just length + if len(bytes) != tt.jsonLength { + t.Logf(string(bytes)) + t.Errorf("JSON byte array length is incorrect; got: %d want %d", len(bytes), tt.jsonLength) + } + }) + } +} + +func initTestLogger() (*zap.Logger, error) { + cfg := zap.Config{ + Encoding: "console", + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + Level: zap.NewAtomicLevelAt(zapcore.DebugLevel), + EncoderConfig: zapcore.EncoderConfig{ + MessageKey: "message", + + LevelKey: "level", + EncodeLevel: zapcore.CapitalLevelEncoder, + + TimeKey: "time", + EncodeTime: zapcore.ISO8601TimeEncoder, + + CallerKey: "caller", + EncodeCaller: zapcore.ShortCallerEncoder, + }, + } + + return cfg.Build() +} diff --git a/configurator/mocks/s3.go b/configurator/mocks/s3.go new file mode 100644 index 0000000..43ff929 --- /dev/null +++ b/configurator/mocks/s3.go @@ -0,0 +1,958 @@ +package mocks + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/service/s3/s3iface" + "github.com/stretchr/testify/mock" +) + +type S3API struct { + mock.Mock + s3iface.S3API +} + +func (m *S3API) AbortMultipartUpload(*s3.AbortMultipartUploadInput) (*s3.AbortMultipartUploadOutput, error) { + return nil, nil +} +func (m *S3API) AbortMultipartUploadWithContext(aws.Context, *s3.AbortMultipartUploadInput, ...request.Option) (*s3.AbortMultipartUploadOutput, error) { + return nil, nil +} +func (m *S3API) AbortMultipartUploadRequest(*s3.AbortMultipartUploadInput) (*request.Request, *s3.AbortMultipartUploadOutput) { + return nil, nil +} + +func (m *S3API) CompleteMultipartUpload(*s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error) { + return nil, nil +} +func (m *S3API) CompleteMultipartUploadWithContext(aws.Context, *s3.CompleteMultipartUploadInput, ...request.Option) (*s3.CompleteMultipartUploadOutput, error) { + return nil, nil +} +func (m *S3API) CompleteMultipartUploadRequest(*s3.CompleteMultipartUploadInput) (*request.Request, *s3.CompleteMultipartUploadOutput) { + return nil, nil +} + +func (m *S3API) CopyObject(*s3.CopyObjectInput) (*s3.CopyObjectOutput, error) { + return nil, nil +} +func (m *S3API) CopyObjectWithContext(aws.Context, *s3.CopyObjectInput, ...request.Option) (*s3.CopyObjectOutput, error) { + return nil, nil +} +func (m *S3API) CopyObjectRequest(*s3.CopyObjectInput) (*request.Request, *s3.CopyObjectOutput) { + return nil, nil +} + +func (m *S3API) CreateBucket(*s3.CreateBucketInput) (*s3.CreateBucketOutput, error) { + return nil, nil +} +func (m *S3API) CreateBucketWithContext(aws.Context, *s3.CreateBucketInput, ...request.Option) (*s3.CreateBucketOutput, error) { + return nil, nil +} +func (m *S3API) CreateBucketRequest(*s3.CreateBucketInput) (*request.Request, *s3.CreateBucketOutput) { + return nil, nil +} + +func (m *S3API) CreateMultipartUpload(*s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) { + return nil, nil +} +func (m *S3API) CreateMultipartUploadWithContext(aws.Context, *s3.CreateMultipartUploadInput, ...request.Option) (*s3.CreateMultipartUploadOutput, error) { + return nil, nil +} +func (m *S3API) CreateMultipartUploadRequest(*s3.CreateMultipartUploadInput) (*request.Request, *s3.CreateMultipartUploadOutput) { + return nil, nil +} + +func (m *S3API) DeleteBucket(*s3.DeleteBucketInput) (*s3.DeleteBucketOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketWithContext(aws.Context, *s3.DeleteBucketInput, ...request.Option) (*s3.DeleteBucketOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketRequest(*s3.DeleteBucketInput) (*request.Request, *s3.DeleteBucketOutput) { + return nil, nil +} + +func (m *S3API) DeleteBucketAnalyticsConfiguration(*s3.DeleteBucketAnalyticsConfigurationInput) (*s3.DeleteBucketAnalyticsConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketAnalyticsConfigurationWithContext(aws.Context, *s3.DeleteBucketAnalyticsConfigurationInput, ...request.Option) (*s3.DeleteBucketAnalyticsConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketAnalyticsConfigurationRequest(*s3.DeleteBucketAnalyticsConfigurationInput) (*request.Request, *s3.DeleteBucketAnalyticsConfigurationOutput) { + return nil, nil +} + +func (m *S3API) DeleteBucketCors(*s3.DeleteBucketCorsInput) (*s3.DeleteBucketCorsOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketCorsWithContext(aws.Context, *s3.DeleteBucketCorsInput, ...request.Option) (*s3.DeleteBucketCorsOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketCorsRequest(*s3.DeleteBucketCorsInput) (*request.Request, *s3.DeleteBucketCorsOutput) { + return nil, nil +} + +func (m *S3API) DeleteBucketEncryption(*s3.DeleteBucketEncryptionInput) (*s3.DeleteBucketEncryptionOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketEncryptionWithContext(aws.Context, *s3.DeleteBucketEncryptionInput, ...request.Option) (*s3.DeleteBucketEncryptionOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketEncryptionRequest(*s3.DeleteBucketEncryptionInput) (*request.Request, *s3.DeleteBucketEncryptionOutput) { + return nil, nil +} + +func (m *S3API) DeleteBucketInventoryConfiguration(*s3.DeleteBucketInventoryConfigurationInput) (*s3.DeleteBucketInventoryConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketInventoryConfigurationWithContext(aws.Context, *s3.DeleteBucketInventoryConfigurationInput, ...request.Option) (*s3.DeleteBucketInventoryConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketInventoryConfigurationRequest(*s3.DeleteBucketInventoryConfigurationInput) (*request.Request, *s3.DeleteBucketInventoryConfigurationOutput) { + return nil, nil +} + +func (m *S3API) DeleteBucketLifecycle(*s3.DeleteBucketLifecycleInput) (*s3.DeleteBucketLifecycleOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketLifecycleWithContext(aws.Context, *s3.DeleteBucketLifecycleInput, ...request.Option) (*s3.DeleteBucketLifecycleOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketLifecycleRequest(*s3.DeleteBucketLifecycleInput) (*request.Request, *s3.DeleteBucketLifecycleOutput) { + return nil, nil +} + +func (m *S3API) DeleteBucketMetricsConfiguration(*s3.DeleteBucketMetricsConfigurationInput) (*s3.DeleteBucketMetricsConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketMetricsConfigurationWithContext(aws.Context, *s3.DeleteBucketMetricsConfigurationInput, ...request.Option) (*s3.DeleteBucketMetricsConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketMetricsConfigurationRequest(*s3.DeleteBucketMetricsConfigurationInput) (*request.Request, *s3.DeleteBucketMetricsConfigurationOutput) { + return nil, nil +} + +func (m *S3API) DeleteBucketPolicy(*s3.DeleteBucketPolicyInput) (*s3.DeleteBucketPolicyOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketPolicyWithContext(aws.Context, *s3.DeleteBucketPolicyInput, ...request.Option) (*s3.DeleteBucketPolicyOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketPolicyRequest(*s3.DeleteBucketPolicyInput) (*request.Request, *s3.DeleteBucketPolicyOutput) { + return nil, nil +} + +func (m *S3API) DeleteBucketReplication(*s3.DeleteBucketReplicationInput) (*s3.DeleteBucketReplicationOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketReplicationWithContext(aws.Context, *s3.DeleteBucketReplicationInput, ...request.Option) (*s3.DeleteBucketReplicationOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketReplicationRequest(*s3.DeleteBucketReplicationInput) (*request.Request, *s3.DeleteBucketReplicationOutput) { + return nil, nil +} + +func (m *S3API) DeleteBucketTagging(*s3.DeleteBucketTaggingInput) (*s3.DeleteBucketTaggingOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketTaggingWithContext(aws.Context, *s3.DeleteBucketTaggingInput, ...request.Option) (*s3.DeleteBucketTaggingOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketTaggingRequest(*s3.DeleteBucketTaggingInput) (*request.Request, *s3.DeleteBucketTaggingOutput) { + return nil, nil +} + +func (m *S3API) DeleteBucketWebsite(*s3.DeleteBucketWebsiteInput) (*s3.DeleteBucketWebsiteOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketWebsiteWithContext(aws.Context, *s3.DeleteBucketWebsiteInput, ...request.Option) (*s3.DeleteBucketWebsiteOutput, error) { + return nil, nil +} +func (m *S3API) DeleteBucketWebsiteRequest(*s3.DeleteBucketWebsiteInput) (*request.Request, *s3.DeleteBucketWebsiteOutput) { + return nil, nil +} + +func (m *S3API) DeleteObject(*s3.DeleteObjectInput) (*s3.DeleteObjectOutput, error) { + return nil, nil +} +func (m *S3API) DeleteObjectWithContext(aws.Context, *s3.DeleteObjectInput, ...request.Option) (*s3.DeleteObjectOutput, error) { + return nil, nil +} +func (m *S3API) DeleteObjectRequest(*s3.DeleteObjectInput) (*request.Request, *s3.DeleteObjectOutput) { + return nil, nil +} + +func (m *S3API) DeleteObjectTagging(*s3.DeleteObjectTaggingInput) (*s3.DeleteObjectTaggingOutput, error) { + return nil, nil +} +func (m *S3API) DeleteObjectTaggingWithContext(aws.Context, *s3.DeleteObjectTaggingInput, ...request.Option) (*s3.DeleteObjectTaggingOutput, error) { + return nil, nil +} +func (m *S3API) DeleteObjectTaggingRequest(*s3.DeleteObjectTaggingInput) (*request.Request, *s3.DeleteObjectTaggingOutput) { + return nil, nil +} + +func (m *S3API) DeleteObjects(*s3.DeleteObjectsInput) (*s3.DeleteObjectsOutput, error) { + return nil, nil +} +func (m *S3API) DeleteObjectsWithContext(aws.Context, *s3.DeleteObjectsInput, ...request.Option) (*s3.DeleteObjectsOutput, error) { + return nil, nil +} +func (m *S3API) DeleteObjectsRequest(*s3.DeleteObjectsInput) (*request.Request, *s3.DeleteObjectsOutput) { + return nil, nil +} + +func (m *S3API) DeletePublicAccessBlock(*s3.DeletePublicAccessBlockInput) (*s3.DeletePublicAccessBlockOutput, error) { + return nil, nil +} +func (m *S3API) DeletePublicAccessBlockWithContext(aws.Context, *s3.DeletePublicAccessBlockInput, ...request.Option) (*s3.DeletePublicAccessBlockOutput, error) { + return nil, nil +} +func (m *S3API) DeletePublicAccessBlockRequest(*s3.DeletePublicAccessBlockInput) (*request.Request, *s3.DeletePublicAccessBlockOutput) { + return nil, nil +} + +func (m *S3API) GetBucketAccelerateConfiguration(*s3.GetBucketAccelerateConfigurationInput) (*s3.GetBucketAccelerateConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketAccelerateConfigurationWithContext(aws.Context, *s3.GetBucketAccelerateConfigurationInput, ...request.Option) (*s3.GetBucketAccelerateConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketAccelerateConfigurationRequest(*s3.GetBucketAccelerateConfigurationInput) (*request.Request, *s3.GetBucketAccelerateConfigurationOutput) { + return nil, nil +} + +func (m *S3API) GetBucketAcl(*s3.GetBucketAclInput) (*s3.GetBucketAclOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketAclWithContext(aws.Context, *s3.GetBucketAclInput, ...request.Option) (*s3.GetBucketAclOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketAclRequest(*s3.GetBucketAclInput) (*request.Request, *s3.GetBucketAclOutput) { + return nil, nil +} + +func (m *S3API) GetBucketAnalyticsConfiguration(*s3.GetBucketAnalyticsConfigurationInput) (*s3.GetBucketAnalyticsConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketAnalyticsConfigurationWithContext(aws.Context, *s3.GetBucketAnalyticsConfigurationInput, ...request.Option) (*s3.GetBucketAnalyticsConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketAnalyticsConfigurationRequest(*s3.GetBucketAnalyticsConfigurationInput) (*request.Request, *s3.GetBucketAnalyticsConfigurationOutput) { + return nil, nil +} + +func (m *S3API) GetBucketCors(*s3.GetBucketCorsInput) (*s3.GetBucketCorsOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketCorsWithContext(aws.Context, *s3.GetBucketCorsInput, ...request.Option) (*s3.GetBucketCorsOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketCorsRequest(*s3.GetBucketCorsInput) (*request.Request, *s3.GetBucketCorsOutput) { + return nil, nil +} + +func (m *S3API) GetBucketEncryption(*s3.GetBucketEncryptionInput) (*s3.GetBucketEncryptionOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketEncryptionWithContext(aws.Context, *s3.GetBucketEncryptionInput, ...request.Option) (*s3.GetBucketEncryptionOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketEncryptionRequest(*s3.GetBucketEncryptionInput) (*request.Request, *s3.GetBucketEncryptionOutput) { + return nil, nil +} + +func (m *S3API) GetBucketInventoryConfiguration(*s3.GetBucketInventoryConfigurationInput) (*s3.GetBucketInventoryConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketInventoryConfigurationWithContext(aws.Context, *s3.GetBucketInventoryConfigurationInput, ...request.Option) (*s3.GetBucketInventoryConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketInventoryConfigurationRequest(*s3.GetBucketInventoryConfigurationInput) (*request.Request, *s3.GetBucketInventoryConfigurationOutput) { + return nil, nil +} + +func (m *S3API) GetBucketLifecycle(*s3.GetBucketLifecycleInput) (*s3.GetBucketLifecycleOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketLifecycleWithContext(aws.Context, *s3.GetBucketLifecycleInput, ...request.Option) (*s3.GetBucketLifecycleOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketLifecycleRequest(*s3.GetBucketLifecycleInput) (*request.Request, *s3.GetBucketLifecycleOutput) { + return nil, nil +} + +func (m *S3API) GetBucketLifecycleConfiguration(*s3.GetBucketLifecycleConfigurationInput) (*s3.GetBucketLifecycleConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketLifecycleConfigurationWithContext(aws.Context, *s3.GetBucketLifecycleConfigurationInput, ...request.Option) (*s3.GetBucketLifecycleConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketLifecycleConfigurationRequest(*s3.GetBucketLifecycleConfigurationInput) (*request.Request, *s3.GetBucketLifecycleConfigurationOutput) { + return nil, nil +} + +func (m *S3API) GetBucketLocation(*s3.GetBucketLocationInput) (*s3.GetBucketLocationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketLocationWithContext(aws.Context, *s3.GetBucketLocationInput, ...request.Option) (*s3.GetBucketLocationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketLocationRequest(*s3.GetBucketLocationInput) (*request.Request, *s3.GetBucketLocationOutput) { + return nil, nil +} + +func (m *S3API) GetBucketLogging(*s3.GetBucketLoggingInput) (*s3.GetBucketLoggingOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketLoggingWithContext(aws.Context, *s3.GetBucketLoggingInput, ...request.Option) (*s3.GetBucketLoggingOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketLoggingRequest(*s3.GetBucketLoggingInput) (*request.Request, *s3.GetBucketLoggingOutput) { + return nil, nil +} + +func (m *S3API) GetBucketMetricsConfiguration(*s3.GetBucketMetricsConfigurationInput) (*s3.GetBucketMetricsConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketMetricsConfigurationWithContext(aws.Context, *s3.GetBucketMetricsConfigurationInput, ...request.Option) (*s3.GetBucketMetricsConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketMetricsConfigurationRequest(*s3.GetBucketMetricsConfigurationInput) (*request.Request, *s3.GetBucketMetricsConfigurationOutput) { + return nil, nil +} + +func (m *S3API) GetBucketNotification(*s3.GetBucketNotificationConfigurationRequest) (*s3.NotificationConfigurationDeprecated, error) { + return nil, nil +} +func (m *S3API) GetBucketNotificationWithContext(aws.Context, *s3.GetBucketNotificationConfigurationRequest, ...request.Option) (*s3.NotificationConfigurationDeprecated, error) { + return nil, nil +} +func (m *S3API) GetBucketNotificationRequest(*s3.GetBucketNotificationConfigurationRequest) (*request.Request, *s3.NotificationConfigurationDeprecated) { + return nil, nil +} + +func (m *S3API) GetBucketNotificationConfiguration(*s3.GetBucketNotificationConfigurationRequest) (*s3.NotificationConfiguration, error) { + return nil, nil +} +func (m *S3API) GetBucketNotificationConfigurationWithContext(aws.Context, *s3.GetBucketNotificationConfigurationRequest, ...request.Option) (*s3.NotificationConfiguration, error) { + return nil, nil +} +func (m *S3API) GetBucketNotificationConfigurationRequest(*s3.GetBucketNotificationConfigurationRequest) (*request.Request, *s3.NotificationConfiguration) { + return nil, nil +} + +func (m *S3API) GetBucketPolicy(*s3.GetBucketPolicyInput) (*s3.GetBucketPolicyOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketPolicyWithContext(aws.Context, *s3.GetBucketPolicyInput, ...request.Option) (*s3.GetBucketPolicyOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketPolicyRequest(*s3.GetBucketPolicyInput) (*request.Request, *s3.GetBucketPolicyOutput) { + return nil, nil +} + +func (m *S3API) GetBucketPolicyStatus(*s3.GetBucketPolicyStatusInput) (*s3.GetBucketPolicyStatusOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketPolicyStatusWithContext(aws.Context, *s3.GetBucketPolicyStatusInput, ...request.Option) (*s3.GetBucketPolicyStatusOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketPolicyStatusRequest(*s3.GetBucketPolicyStatusInput) (*request.Request, *s3.GetBucketPolicyStatusOutput) { + return nil, nil +} + +func (m *S3API) GetBucketReplication(*s3.GetBucketReplicationInput) (*s3.GetBucketReplicationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketReplicationWithContext(aws.Context, *s3.GetBucketReplicationInput, ...request.Option) (*s3.GetBucketReplicationOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketReplicationRequest(*s3.GetBucketReplicationInput) (*request.Request, *s3.GetBucketReplicationOutput) { + return nil, nil +} + +func (m *S3API) GetBucketRequestPayment(*s3.GetBucketRequestPaymentInput) (*s3.GetBucketRequestPaymentOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketRequestPaymentWithContext(aws.Context, *s3.GetBucketRequestPaymentInput, ...request.Option) (*s3.GetBucketRequestPaymentOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketRequestPaymentRequest(*s3.GetBucketRequestPaymentInput) (*request.Request, *s3.GetBucketRequestPaymentOutput) { + return nil, nil +} + +func (m *S3API) GetBucketTagging(*s3.GetBucketTaggingInput) (*s3.GetBucketTaggingOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketTaggingWithContext(aws.Context, *s3.GetBucketTaggingInput, ...request.Option) (*s3.GetBucketTaggingOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketTaggingRequest(*s3.GetBucketTaggingInput) (*request.Request, *s3.GetBucketTaggingOutput) { + return nil, nil +} + +func (m *S3API) GetBucketVersioning(*s3.GetBucketVersioningInput) (*s3.GetBucketVersioningOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketVersioningWithContext(aws.Context, *s3.GetBucketVersioningInput, ...request.Option) (*s3.GetBucketVersioningOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketVersioningRequest(*s3.GetBucketVersioningInput) (*request.Request, *s3.GetBucketVersioningOutput) { + return nil, nil +} + +func (m *S3API) GetBucketWebsite(*s3.GetBucketWebsiteInput) (*s3.GetBucketWebsiteOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketWebsiteWithContext(aws.Context, *s3.GetBucketWebsiteInput, ...request.Option) (*s3.GetBucketWebsiteOutput, error) { + return nil, nil +} +func (m *S3API) GetBucketWebsiteRequest(*s3.GetBucketWebsiteInput) (*request.Request, *s3.GetBucketWebsiteOutput) { + return nil, nil +} + +func (m *S3API) GetObject(*s3.GetObjectInput) (*s3.GetObjectOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectWithContext(ctx aws.Context, input *s3.GetObjectInput, options ...request.Option) (*s3.GetObjectOutput, error) { + args := m.Called(ctx, input, options) + return args.Get(0).(*s3.GetObjectOutput), args.Error(1) +} +func (m *S3API) GetObjectRequest(*s3.GetObjectInput) (*request.Request, *s3.GetObjectOutput) { + return nil, nil +} + +func (m *S3API) GetObjectAcl(*s3.GetObjectAclInput) (*s3.GetObjectAclOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectAclWithContext(aws.Context, *s3.GetObjectAclInput, ...request.Option) (*s3.GetObjectAclOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectAclRequest(*s3.GetObjectAclInput) (*request.Request, *s3.GetObjectAclOutput) { + return nil, nil +} + +func (m *S3API) GetObjectLegalHold(*s3.GetObjectLegalHoldInput) (*s3.GetObjectLegalHoldOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectLegalHoldWithContext(aws.Context, *s3.GetObjectLegalHoldInput, ...request.Option) (*s3.GetObjectLegalHoldOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectLegalHoldRequest(*s3.GetObjectLegalHoldInput) (*request.Request, *s3.GetObjectLegalHoldOutput) { + return nil, nil +} + +func (m *S3API) GetObjectLockConfiguration(*s3.GetObjectLockConfigurationInput) (*s3.GetObjectLockConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectLockConfigurationWithContext(aws.Context, *s3.GetObjectLockConfigurationInput, ...request.Option) (*s3.GetObjectLockConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectLockConfigurationRequest(*s3.GetObjectLockConfigurationInput) (*request.Request, *s3.GetObjectLockConfigurationOutput) { + return nil, nil +} + +func (m *S3API) GetObjectRetention(*s3.GetObjectRetentionInput) (*s3.GetObjectRetentionOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectRetentionWithContext(aws.Context, *s3.GetObjectRetentionInput, ...request.Option) (*s3.GetObjectRetentionOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectRetentionRequest(*s3.GetObjectRetentionInput) (*request.Request, *s3.GetObjectRetentionOutput) { + return nil, nil +} + +func (m *S3API) GetObjectTagging(*s3.GetObjectTaggingInput) (*s3.GetObjectTaggingOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectTaggingWithContext(aws.Context, *s3.GetObjectTaggingInput, ...request.Option) (*s3.GetObjectTaggingOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectTaggingRequest(*s3.GetObjectTaggingInput) (*request.Request, *s3.GetObjectTaggingOutput) { + return nil, nil +} + +func (m *S3API) GetObjectTorrent(*s3.GetObjectTorrentInput) (*s3.GetObjectTorrentOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectTorrentWithContext(aws.Context, *s3.GetObjectTorrentInput, ...request.Option) (*s3.GetObjectTorrentOutput, error) { + return nil, nil +} +func (m *S3API) GetObjectTorrentRequest(*s3.GetObjectTorrentInput) (*request.Request, *s3.GetObjectTorrentOutput) { + return nil, nil +} + +func (m *S3API) GetPublicAccessBlock(*s3.GetPublicAccessBlockInput) (*s3.GetPublicAccessBlockOutput, error) { + return nil, nil +} +func (m *S3API) GetPublicAccessBlockWithContext(aws.Context, *s3.GetPublicAccessBlockInput, ...request.Option) (*s3.GetPublicAccessBlockOutput, error) { + return nil, nil +} +func (m *S3API) GetPublicAccessBlockRequest(*s3.GetPublicAccessBlockInput) (*request.Request, *s3.GetPublicAccessBlockOutput) { + return nil, nil +} + +func (m *S3API) HeadBucket(*s3.HeadBucketInput) (*s3.HeadBucketOutput, error) { + return nil, nil +} +func (m *S3API) HeadBucketWithContext(aws.Context, *s3.HeadBucketInput, ...request.Option) (*s3.HeadBucketOutput, error) { + return nil, nil +} +func (m *S3API) HeadBucketRequest(*s3.HeadBucketInput) (*request.Request, *s3.HeadBucketOutput) { + return nil, nil +} + +func (m *S3API) HeadObject(*s3.HeadObjectInput) (*s3.HeadObjectOutput, error) { + return nil, nil +} +func (m *S3API) HeadObjectWithContext(aws.Context, *s3.HeadObjectInput, ...request.Option) (*s3.HeadObjectOutput, error) { + return nil, nil +} +func (m *S3API) HeadObjectRequest(*s3.HeadObjectInput) (*request.Request, *s3.HeadObjectOutput) { + return nil, nil +} + +func (m *S3API) ListBucketAnalyticsConfigurations(*s3.ListBucketAnalyticsConfigurationsInput) (*s3.ListBucketAnalyticsConfigurationsOutput, error) { + return nil, nil +} +func (m *S3API) ListBucketAnalyticsConfigurationsWithContext(aws.Context, *s3.ListBucketAnalyticsConfigurationsInput, ...request.Option) (*s3.ListBucketAnalyticsConfigurationsOutput, error) { + return nil, nil +} +func (m *S3API) ListBucketAnalyticsConfigurationsRequest(*s3.ListBucketAnalyticsConfigurationsInput) (*request.Request, *s3.ListBucketAnalyticsConfigurationsOutput) { + return nil, nil +} + +func (m *S3API) ListBucketInventoryConfigurations(*s3.ListBucketInventoryConfigurationsInput) (*s3.ListBucketInventoryConfigurationsOutput, error) { + return nil, nil +} +func (m *S3API) ListBucketInventoryConfigurationsWithContext(aws.Context, *s3.ListBucketInventoryConfigurationsInput, ...request.Option) (*s3.ListBucketInventoryConfigurationsOutput, error) { + return nil, nil +} +func (m *S3API) ListBucketInventoryConfigurationsRequest(*s3.ListBucketInventoryConfigurationsInput) (*request.Request, *s3.ListBucketInventoryConfigurationsOutput) { + return nil, nil +} + +func (m *S3API) ListBucketMetricsConfigurations(*s3.ListBucketMetricsConfigurationsInput) (*s3.ListBucketMetricsConfigurationsOutput, error) { + return nil, nil +} +func (m *S3API) ListBucketMetricsConfigurationsWithContext(aws.Context, *s3.ListBucketMetricsConfigurationsInput, ...request.Option) (*s3.ListBucketMetricsConfigurationsOutput, error) { + return nil, nil +} +func (m *S3API) ListBucketMetricsConfigurationsRequest(*s3.ListBucketMetricsConfigurationsInput) (*request.Request, *s3.ListBucketMetricsConfigurationsOutput) { + return nil, nil +} + +func (m *S3API) ListBuckets(*s3.ListBucketsInput) (*s3.ListBucketsOutput, error) { + return nil, nil +} +func (m *S3API) ListBucketsWithContext(aws.Context, *s3.ListBucketsInput, ...request.Option) (*s3.ListBucketsOutput, error) { + return nil, nil +} +func (m *S3API) ListBucketsRequest(*s3.ListBucketsInput) (*request.Request, *s3.ListBucketsOutput) { + return nil, nil +} + +func (m *S3API) ListMultipartUploads(*s3.ListMultipartUploadsInput) (*s3.ListMultipartUploadsOutput, error) { + return nil, nil +} +func (m *S3API) ListMultipartUploadsWithContext(aws.Context, *s3.ListMultipartUploadsInput, ...request.Option) (*s3.ListMultipartUploadsOutput, error) { + return nil, nil +} +func (m *S3API) ListMultipartUploadsRequest(*s3.ListMultipartUploadsInput) (*request.Request, *s3.ListMultipartUploadsOutput) { + return nil, nil +} + +func (m *S3API) ListMultipartUploadsPages(*s3.ListMultipartUploadsInput, func(*s3.ListMultipartUploadsOutput, bool) bool) error { + return nil +} +func (m *S3API) ListMultipartUploadsPagesWithContext(aws.Context, *s3.ListMultipartUploadsInput, func(*s3.ListMultipartUploadsOutput, bool) bool, ...request.Option) error { + return nil +} + +func (m *S3API) ListObjectVersions(*s3.ListObjectVersionsInput) (*s3.ListObjectVersionsOutput, error) { + return nil, nil +} +func (m *S3API) ListObjectVersionsWithContext(aws.Context, *s3.ListObjectVersionsInput, ...request.Option) (*s3.ListObjectVersionsOutput, error) { + return nil, nil +} +func (m *S3API) ListObjectVersionsRequest(*s3.ListObjectVersionsInput) (*request.Request, *s3.ListObjectVersionsOutput) { + return nil, nil +} + +func (m *S3API) ListObjectVersionsPages(*s3.ListObjectVersionsInput, func(*s3.ListObjectVersionsOutput, bool) bool) error { + return nil +} +func (m *S3API) ListObjectVersionsPagesWithContext(aws.Context, *s3.ListObjectVersionsInput, func(*s3.ListObjectVersionsOutput, bool) bool, ...request.Option) error { + return nil +} + +func (m *S3API) ListObjects(*s3.ListObjectsInput) (*s3.ListObjectsOutput, error) { + return nil, nil +} +func (m *S3API) ListObjectsWithContext(aws.Context, *s3.ListObjectsInput, ...request.Option) (*s3.ListObjectsOutput, error) { + return nil, nil +} +func (m *S3API) ListObjectsRequest(*s3.ListObjectsInput) (*request.Request, *s3.ListObjectsOutput) { + return nil, nil +} + +func (m *S3API) ListObjectsPages(*s3.ListObjectsInput, func(*s3.ListObjectsOutput, bool) bool) error { + return nil +} +func (m *S3API) ListObjectsPagesWithContext(aws.Context, *s3.ListObjectsInput, func(*s3.ListObjectsOutput, bool) bool, ...request.Option) error { + return nil +} + +func (m *S3API) ListObjectsV2(*s3.ListObjectsV2Input) (*s3.ListObjectsV2Output, error) { + return nil, nil +} +func (m *S3API) ListObjectsV2WithContext(aws.Context, *s3.ListObjectsV2Input, ...request.Option) (*s3.ListObjectsV2Output, error) { + return nil, nil +} +func (m *S3API) ListObjectsV2Request(*s3.ListObjectsV2Input) (*request.Request, *s3.ListObjectsV2Output) { + return nil, nil +} + +func (m *S3API) ListObjectsV2Pages(*s3.ListObjectsV2Input, func(*s3.ListObjectsV2Output, bool) bool) error { + return nil +} +func (m *S3API) ListObjectsV2PagesWithContext(aws.Context, *s3.ListObjectsV2Input, func(*s3.ListObjectsV2Output, bool) bool, ...request.Option) error { + return nil +} + +func (m *S3API) ListParts(*s3.ListPartsInput) (*s3.ListPartsOutput, error) { + return nil, nil +} +func (m *S3API) ListPartsWithContext(aws.Context, *s3.ListPartsInput, ...request.Option) (*s3.ListPartsOutput, error) { + return nil, nil +} +func (m *S3API) ListPartsRequest(*s3.ListPartsInput) (*request.Request, *s3.ListPartsOutput) { + return nil, nil +} + +func (m *S3API) ListPartsPages(*s3.ListPartsInput, func(*s3.ListPartsOutput, bool) bool) error { + return nil +} +func (m *S3API) ListPartsPagesWithContext(aws.Context, *s3.ListPartsInput, func(*s3.ListPartsOutput, bool) bool, ...request.Option) error { + return nil +} + +func (m *S3API) PutBucketAccelerateConfiguration(*s3.PutBucketAccelerateConfigurationInput) (*s3.PutBucketAccelerateConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketAccelerateConfigurationWithContext(aws.Context, *s3.PutBucketAccelerateConfigurationInput, ...request.Option) (*s3.PutBucketAccelerateConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketAccelerateConfigurationRequest(*s3.PutBucketAccelerateConfigurationInput) (*request.Request, *s3.PutBucketAccelerateConfigurationOutput) { + return nil, nil +} + +func (m *S3API) PutBucketAcl(*s3.PutBucketAclInput) (*s3.PutBucketAclOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketAclWithContext(aws.Context, *s3.PutBucketAclInput, ...request.Option) (*s3.PutBucketAclOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketAclRequest(*s3.PutBucketAclInput) (*request.Request, *s3.PutBucketAclOutput) { + return nil, nil +} + +func (m *S3API) PutBucketAnalyticsConfiguration(*s3.PutBucketAnalyticsConfigurationInput) (*s3.PutBucketAnalyticsConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketAnalyticsConfigurationWithContext(aws.Context, *s3.PutBucketAnalyticsConfigurationInput, ...request.Option) (*s3.PutBucketAnalyticsConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketAnalyticsConfigurationRequest(*s3.PutBucketAnalyticsConfigurationInput) (*request.Request, *s3.PutBucketAnalyticsConfigurationOutput) { + return nil, nil +} + +func (m *S3API) PutBucketCors(*s3.PutBucketCorsInput) (*s3.PutBucketCorsOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketCorsWithContext(aws.Context, *s3.PutBucketCorsInput, ...request.Option) (*s3.PutBucketCorsOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketCorsRequest(*s3.PutBucketCorsInput) (*request.Request, *s3.PutBucketCorsOutput) { + return nil, nil +} + +func (m *S3API) PutBucketEncryption(*s3.PutBucketEncryptionInput) (*s3.PutBucketEncryptionOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketEncryptionWithContext(aws.Context, *s3.PutBucketEncryptionInput, ...request.Option) (*s3.PutBucketEncryptionOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketEncryptionRequest(*s3.PutBucketEncryptionInput) (*request.Request, *s3.PutBucketEncryptionOutput) { + return nil, nil +} + +func (m *S3API) PutBucketInventoryConfiguration(*s3.PutBucketInventoryConfigurationInput) (*s3.PutBucketInventoryConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketInventoryConfigurationWithContext(aws.Context, *s3.PutBucketInventoryConfigurationInput, ...request.Option) (*s3.PutBucketInventoryConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketInventoryConfigurationRequest(*s3.PutBucketInventoryConfigurationInput) (*request.Request, *s3.PutBucketInventoryConfigurationOutput) { + return nil, nil +} + +func (m *S3API) PutBucketLifecycle(*s3.PutBucketLifecycleInput) (*s3.PutBucketLifecycleOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketLifecycleWithContext(aws.Context, *s3.PutBucketLifecycleInput, ...request.Option) (*s3.PutBucketLifecycleOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketLifecycleRequest(*s3.PutBucketLifecycleInput) (*request.Request, *s3.PutBucketLifecycleOutput) { + return nil, nil +} + +func (m *S3API) PutBucketLifecycleConfiguration(*s3.PutBucketLifecycleConfigurationInput) (*s3.PutBucketLifecycleConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketLifecycleConfigurationWithContext(aws.Context, *s3.PutBucketLifecycleConfigurationInput, ...request.Option) (*s3.PutBucketLifecycleConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketLifecycleConfigurationRequest(*s3.PutBucketLifecycleConfigurationInput) (*request.Request, *s3.PutBucketLifecycleConfigurationOutput) { + return nil, nil +} + +func (m *S3API) PutBucketLogging(*s3.PutBucketLoggingInput) (*s3.PutBucketLoggingOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketLoggingWithContext(aws.Context, *s3.PutBucketLoggingInput, ...request.Option) (*s3.PutBucketLoggingOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketLoggingRequest(*s3.PutBucketLoggingInput) (*request.Request, *s3.PutBucketLoggingOutput) { + return nil, nil +} + +func (m *S3API) PutBucketMetricsConfiguration(*s3.PutBucketMetricsConfigurationInput) (*s3.PutBucketMetricsConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketMetricsConfigurationWithContext(aws.Context, *s3.PutBucketMetricsConfigurationInput, ...request.Option) (*s3.PutBucketMetricsConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketMetricsConfigurationRequest(*s3.PutBucketMetricsConfigurationInput) (*request.Request, *s3.PutBucketMetricsConfigurationOutput) { + return nil, nil +} + +func (m *S3API) PutBucketNotification(*s3.PutBucketNotificationInput) (*s3.PutBucketNotificationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketNotificationWithContext(aws.Context, *s3.PutBucketNotificationInput, ...request.Option) (*s3.PutBucketNotificationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketNotificationRequest(*s3.PutBucketNotificationInput) (*request.Request, *s3.PutBucketNotificationOutput) { + return nil, nil +} + +func (m *S3API) PutBucketNotificationConfiguration(*s3.PutBucketNotificationConfigurationInput) (*s3.PutBucketNotificationConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketNotificationConfigurationWithContext(aws.Context, *s3.PutBucketNotificationConfigurationInput, ...request.Option) (*s3.PutBucketNotificationConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketNotificationConfigurationRequest(*s3.PutBucketNotificationConfigurationInput) (*request.Request, *s3.PutBucketNotificationConfigurationOutput) { + return nil, nil +} + +func (m *S3API) PutBucketPolicy(*s3.PutBucketPolicyInput) (*s3.PutBucketPolicyOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketPolicyWithContext(aws.Context, *s3.PutBucketPolicyInput, ...request.Option) (*s3.PutBucketPolicyOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketPolicyRequest(*s3.PutBucketPolicyInput) (*request.Request, *s3.PutBucketPolicyOutput) { + return nil, nil +} + +func (m *S3API) PutBucketReplication(*s3.PutBucketReplicationInput) (*s3.PutBucketReplicationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketReplicationWithContext(aws.Context, *s3.PutBucketReplicationInput, ...request.Option) (*s3.PutBucketReplicationOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketReplicationRequest(*s3.PutBucketReplicationInput) (*request.Request, *s3.PutBucketReplicationOutput) { + return nil, nil +} + +func (m *S3API) PutBucketRequestPayment(*s3.PutBucketRequestPaymentInput) (*s3.PutBucketRequestPaymentOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketRequestPaymentWithContext(aws.Context, *s3.PutBucketRequestPaymentInput, ...request.Option) (*s3.PutBucketRequestPaymentOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketRequestPaymentRequest(*s3.PutBucketRequestPaymentInput) (*request.Request, *s3.PutBucketRequestPaymentOutput) { + return nil, nil +} + +func (m *S3API) PutBucketTagging(*s3.PutBucketTaggingInput) (*s3.PutBucketTaggingOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketTaggingWithContext(aws.Context, *s3.PutBucketTaggingInput, ...request.Option) (*s3.PutBucketTaggingOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketTaggingRequest(*s3.PutBucketTaggingInput) (*request.Request, *s3.PutBucketTaggingOutput) { + return nil, nil +} + +func (m *S3API) PutBucketVersioning(*s3.PutBucketVersioningInput) (*s3.PutBucketVersioningOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketVersioningWithContext(aws.Context, *s3.PutBucketVersioningInput, ...request.Option) (*s3.PutBucketVersioningOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketVersioningRequest(*s3.PutBucketVersioningInput) (*request.Request, *s3.PutBucketVersioningOutput) { + return nil, nil +} + +func (m *S3API) PutBucketWebsite(*s3.PutBucketWebsiteInput) (*s3.PutBucketWebsiteOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketWebsiteWithContext(aws.Context, *s3.PutBucketWebsiteInput, ...request.Option) (*s3.PutBucketWebsiteOutput, error) { + return nil, nil +} +func (m *S3API) PutBucketWebsiteRequest(*s3.PutBucketWebsiteInput) (*request.Request, *s3.PutBucketWebsiteOutput) { + return nil, nil +} + +func (m *S3API) PutObject(*s3.PutObjectInput) (*s3.PutObjectOutput, error) { + return nil, nil +} +func (m *S3API) PutObjectWithContext(aws.Context, *s3.PutObjectInput, ...request.Option) (*s3.PutObjectOutput, error) { + return nil, nil +} +func (m *S3API) PutObjectRequest(*s3.PutObjectInput) (*request.Request, *s3.PutObjectOutput) { + return nil, nil +} + +func (m *S3API) PutObjectAcl(*s3.PutObjectAclInput) (*s3.PutObjectAclOutput, error) { + return nil, nil +} +func (m *S3API) PutObjectAclWithContext(aws.Context, *s3.PutObjectAclInput, ...request.Option) (*s3.PutObjectAclOutput, error) { + return nil, nil +} +func (m *S3API) PutObjectAclRequest(*s3.PutObjectAclInput) (*request.Request, *s3.PutObjectAclOutput) { + return nil, nil +} + +func (m *S3API) PutObjectLegalHold(*s3.PutObjectLegalHoldInput) (*s3.PutObjectLegalHoldOutput, error) { + return nil, nil +} +func (m *S3API) PutObjectLegalHoldWithContext(aws.Context, *s3.PutObjectLegalHoldInput, ...request.Option) (*s3.PutObjectLegalHoldOutput, error) { + return nil, nil +} +func (m *S3API) PutObjectLegalHoldRequest(*s3.PutObjectLegalHoldInput) (*request.Request, *s3.PutObjectLegalHoldOutput) { + return nil, nil +} + +func (m *S3API) PutObjectLockConfiguration(*s3.PutObjectLockConfigurationInput) (*s3.PutObjectLockConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutObjectLockConfigurationWithContext(aws.Context, *s3.PutObjectLockConfigurationInput, ...request.Option) (*s3.PutObjectLockConfigurationOutput, error) { + return nil, nil +} +func (m *S3API) PutObjectLockConfigurationRequest(*s3.PutObjectLockConfigurationInput) (*request.Request, *s3.PutObjectLockConfigurationOutput) { + return nil, nil +} + +func (m *S3API) PutObjectRetention(*s3.PutObjectRetentionInput) (*s3.PutObjectRetentionOutput, error) { + return nil, nil +} +func (m *S3API) PutObjectRetentionWithContext(aws.Context, *s3.PutObjectRetentionInput, ...request.Option) (*s3.PutObjectRetentionOutput, error) { + return nil, nil +} +func (m *S3API) PutObjectRetentionRequest(*s3.PutObjectRetentionInput) (*request.Request, *s3.PutObjectRetentionOutput) { + return nil, nil +} + +func (m *S3API) PutObjectTagging(*s3.PutObjectTaggingInput) (*s3.PutObjectTaggingOutput, error) { + return nil, nil +} +func (m *S3API) PutObjectTaggingWithContext(aws.Context, *s3.PutObjectTaggingInput, ...request.Option) (*s3.PutObjectTaggingOutput, error) { + return nil, nil +} +func (m *S3API) PutObjectTaggingRequest(*s3.PutObjectTaggingInput) (*request.Request, *s3.PutObjectTaggingOutput) { + return nil, nil +} + +func (m *S3API) PutPublicAccessBlock(*s3.PutPublicAccessBlockInput) (*s3.PutPublicAccessBlockOutput, error) { + return nil, nil +} +func (m *S3API) PutPublicAccessBlockWithContext(aws.Context, *s3.PutPublicAccessBlockInput, ...request.Option) (*s3.PutPublicAccessBlockOutput, error) { + return nil, nil +} +func (m *S3API) PutPublicAccessBlockRequest(*s3.PutPublicAccessBlockInput) (*request.Request, *s3.PutPublicAccessBlockOutput) { + return nil, nil +} + +func (m *S3API) RestoreObject(*s3.RestoreObjectInput) (*s3.RestoreObjectOutput, error) { + return nil, nil +} +func (m *S3API) RestoreObjectWithContext(aws.Context, *s3.RestoreObjectInput, ...request.Option) (*s3.RestoreObjectOutput, error) { + return nil, nil +} +func (m *S3API) RestoreObjectRequest(*s3.RestoreObjectInput) (*request.Request, *s3.RestoreObjectOutput) { + return nil, nil +} + +func (m *S3API) SelectObjectContent(*s3.SelectObjectContentInput) (*s3.SelectObjectContentOutput, error) { + return nil, nil +} +func (m *S3API) SelectObjectContentWithContext(aws.Context, *s3.SelectObjectContentInput, ...request.Option) (*s3.SelectObjectContentOutput, error) { + return nil, nil +} +func (m *S3API) SelectObjectContentRequest(*s3.SelectObjectContentInput) (*request.Request, *s3.SelectObjectContentOutput) { + return nil, nil +} + +func (m *S3API) UploadPart(*s3.UploadPartInput) (*s3.UploadPartOutput, error) { + return nil, nil +} +func (m *S3API) UploadPartWithContext(aws.Context, *s3.UploadPartInput, ...request.Option) (*s3.UploadPartOutput, error) { + return nil, nil +} +func (m *S3API) UploadPartRequest(*s3.UploadPartInput) (*request.Request, *s3.UploadPartOutput) { + return nil, nil +} + +func (m *S3API) UploadPartCopy(*s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error) { + return nil, nil +} +func (m *S3API) UploadPartCopyWithContext(aws.Context, *s3.UploadPartCopyInput, ...request.Option) (*s3.UploadPartCopyOutput, error) { + return nil, nil +} +func (m *S3API) UploadPartCopyRequest(*s3.UploadPartCopyInput) (*request.Request, *s3.UploadPartCopyOutput) { + return nil, nil +} + +func (m *S3API) WaitUntilBucketExists(*s3.HeadBucketInput) error { + return nil +} +func (m *S3API) WaitUntilBucketExistsWithContext(aws.Context, *s3.HeadBucketInput, ...request.WaiterOption) error { + return nil +} + +func (m *S3API) WaitUntilBucketNotExists(*s3.HeadBucketInput) error { + return nil +} +func (m *S3API) WaitUntilBucketNotExistsWithContext(aws.Context, *s3.HeadBucketInput, ...request.WaiterOption) error { + return nil +} + +func (m *S3API) WaitUntilObjectExists(*s3.HeadObjectInput) error { + return nil +} +func (m *S3API) WaitUntilObjectExistsWithContext(aws.Context, *s3.HeadObjectInput, ...request.WaiterOption) error { + return nil +} + +func (m *S3API) WaitUntilObjectNotExists(*s3.HeadObjectInput) error { + return nil +} +func (m *S3API) WaitUntilObjectNotExistsWithContext(aws.Context, *s3.HeadObjectInput, ...request.WaiterOption) error { + return nil +} diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 0000000..6b9616b --- /dev/null +++ b/docs/config.md @@ -0,0 +1,53 @@ +# Configuration + +Manny's `build` subcommand is a wrapper around `Configurator`. Configurators job does all the business logic for Manny. +First base configurations are loaded, followed by stacks, and finally a deployment manifest is output. + +### Bases + +Bases are the first thing that are loaded, their data structure can be +[found here](https://github.com/keikoproj/manny/blob/master/configurator/configurator.go#L113-L138). Manny +configs can inherit from eachother using a field called "Base", which is how base configs got their name. + +#### Configuration Inheritance Example + +Given the following directory structure: + + usw2 + ├── config.yaml + ├── vpc1 + │ ├── config.yaml + │ └── privatelink + │ └── config.yaml + └── vpc2 + ├── config.yaml + └── privatelink + └── config.yaml + +Given that: +- `usw2/vpc1/privatelink/config.yaml` has a `base` field of `../config.yaml` and a `test` field of `test` +- `usw2/vpc1/config.yaml` has a `base` field of `../config.yaml` and a `test` field of `test-2` +- `usw2/config.yaml` has a `test` field of `test-3` +- The user performs: `manny build usw2/vpc1/privatelink` + +Manny will: +1. Get a list of files in `usw2/vpc1/privatelink/` and find `config.yaml`. +2. Reach `config.yaml` and extract the base. When a base is found Manny will pause unmarshalling data and will read the +next base. +3. Read `usw2/vpc1/config.yaml` and discover another base, pausing unmarshalling as it did before. +4. Read `usw2/config.yaml` +5. After unmarshal it goes back to the previous config, creates a `MannyConfig`, and stores that struct in a `slice` +6. After all `MannyConfig` objects have been stored in `Configurator.Bases` Manny will apply each `MannyConfig` in +`Configurator.Bases` to `Configurator.Global` from first to last index + +This process gives us a trail to audit failures and is still quite fast given that empty structs do not occupy memory. +That said, this process can be improved. + +#### Picking up additional deployments + +Manny is capable of discovering higher level deployments. Assuming the same structure as above, and an input of +`manny build usw2/vpc1` Manny would discover the `privatelink` directory and then create a second deployment that is +entirely separated from the first. + +Manny does this while evaluating files and directories in a provided directory. It then creates a new `Configurator` +instance and calls `loadBases()` and `loadStacks()`. The deployments are then added to the parent `Configurator.Stacks`. diff --git a/docs/rendering.md b/docs/rendering.md new file mode 100644 index 0000000..35116da --- /dev/null +++ b/docs/rendering.md @@ -0,0 +1,10 @@ +# Rendering + +Manny is not cognizant of the underlying CloudFormation that it is packaging up. When a user submits a template, whether +in JSON or YAML format, Manny will simply read the file bytes and attach them to the Manny config. All user related input +goes untouched in that regard. + +Formatting _does_ apply to how Manny sends the Cloud Resource to ArgoCD. Manny can send in JSON or YAML formats, although, +traditional Kubernetes styled JSON (which is not actually valid JSON) seems to cause problems with ArgoCD. + +By default, Manny uses YAML formatting. \ No newline at end of file diff --git a/docs/testing-strategy.md b/docs/testing-strategy.md new file mode 100644 index 0000000..36811a9 --- /dev/null +++ b/docs/testing-strategy.md @@ -0,0 +1,33 @@ +# Testing Strategy + +Manny uses a series of test tables to validate internal functions. The test tables and functions can be divided into the +following categories: + +- Examples +- Negative Tests +- Unit tests + +### Examples + +The purpose of testing against the `examples` directory is two fold: +- We establish a contract for use of Manny so that we don't unintentionally break core functionality +- We educate users on how to properly use Manny without detailed instruction pages + +### Negative Tests + +The `tests` directory consists of our negative tests and showcases how _not_ to use Manny and establishes a contract of +what will definitively be an end-user error. + +### Unit Tests + +Although all of the above tests are ran in unit style testing, they actually accomplish more E2E level testing but with +better introspection/coverage support. + +These tests are specifically more unit test oriented and make singular method calls and evaluate output. + +### Conventions + +- All tests contain a case named "Valid" that demonstrates how to use the function or method. +- All tests use a field named "Identifier" to differentiate one test from the other. +- All tests are runnable by `go test` and contribute to coverage +- Core functionality should include a test in `examples` \ No newline at end of file diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..251c121 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,4 @@ +This directory contains examples which are also used in testing to ensure a on-going commitment to our customers + +- [Nested](nested) +- [Static](static) \ No newline at end of file diff --git a/examples/complex/aws/localfile-stack.yaml b/examples/complex/aws/localfile-stack.yaml new file mode 100644 index 0000000..56b2b65 --- /dev/null +++ b/examples/complex/aws/localfile-stack.yaml @@ -0,0 +1,8 @@ +template: + type: file + path: ../templates/template.yaml +parameters: + Port: 443 +stackname: "localfile-stack" +tags: + manny-generated: true diff --git a/examples/complex/missing-resources/security-group.yaml b/examples/complex/missing-resources/security-group.yaml new file mode 100644 index 0000000..8081dbf --- /dev/null +++ b/examples/complex/missing-resources/security-group.yaml @@ -0,0 +1,8 @@ +template: + type: file + path: security_group_stack.txt +parameters: + Port: 443 +stackname: "localfile-stack" +tags: + manny-generated: true \ No newline at end of file diff --git a/examples/complex/missing-resources/security_group_stack.txt b/examples/complex/missing-resources/security_group_stack.txt new file mode 100644 index 0000000..cc74263 --- /dev/null +++ b/examples/complex/missing-resources/security_group_stack.txt @@ -0,0 +1,7 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: Manny template... +Parameters: + VpcId: + Description: VPC Id + Type: String +Resources: {} \ No newline at end of file diff --git a/examples/complex/missing-resources/security_group_stack1.txt b/examples/complex/missing-resources/security_group_stack1.txt new file mode 100644 index 0000000..384a45e --- /dev/null +++ b/examples/complex/missing-resources/security_group_stack1.txt @@ -0,0 +1,7 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: Manny template... +Parameters: + VpcId: + Description: VPC Id + Type: String +Resources: { } \ No newline at end of file diff --git a/examples/complex/templates/template.yaml b/examples/complex/templates/template.yaml new file mode 100644 index 0000000..370bd06 --- /dev/null +++ b/examples/complex/templates/template.yaml @@ -0,0 +1,250 @@ +AWSTemplateFormatVersion: 2010-09-09 +Description: Creates Custom VPC +Metadata: + Name: chp-vpc-custom + Version: 0.2.3 + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: VPC Name + Parameters: + - VpcName + - Label: + default: >- + Subnet Configuration: + VPC size CAN NOT EXCEED /16 or 65,534 IP addresses. + For example: The stack would not get created if you pick 6XL (/18) for both private + and alternate subnets. 6XL means 16k IP addresses and it would try to create 3 private + subnets and 3 alternate subnets each with 16k IP addresses, which adds up to 98k IP addresses. + Parameters: + - PrivateSubnetSize + - DataSubnetSize + - IngressSubnetSize + - EgressSubnetSize + - Label: + default: Alternate Subnet Configuration (optional) + Parameters: + - AlternateSubnetName + - AlternateSubnetSize + - AlternateSubnetRouteTableName + + ParameterLabels: + VpcName: + default: VPC name + PrivateSubnetSize: + default: Size of the private subnet(s) + DataSubnetSize: + default: Size of the data subnet(s) + AlternateSubnetName: + default: Alternate subnet name + AlternateSubnetSize: + default: Size of the Alternate subnet(s) + AlternateSubnetRouteTableName: + default: Alternate subnet route table name + IngressSubnetSize: + default: Size of the Ingress subnet(s) + EgressSubnetSize: + default: Size of theEgress subnet(s) + +Parameters: + VpcName: + Type: String + PrivateSubnetSize: + Type: String + Default: M + AllowedValues: + - 2XS + - XS + - S + - M + - L + - XL + - 2XL + - 3XL + - 4XL + - 5XL + - 6XL + Description: >- + Size "2XS" supports up to 48 App/Web EC2 instances, Lambda functions or EMR clusters, Size "XS" supports 96 (2 * 2XS), + "S" supports 192 (2 * XS), and so on. + DataSubnetSize: + Type: String + Default: XS + AllowedValues: + - 2XS + - XS + - S + - M + - L + - XL + - 2XL + - 3XL + - 4XL + - 5XL + - 6XL + Description: >- + Size "2XS" can support 48 RDS/EC2 instances or ElastiCache clusters, Size "XS" supports 96 (2 * 2XS) RDS/EC2 instances or + ElastiCache clusters, "S" supports 192 (2 * XS), and so on. + AlternateSubnetName: + Type: String + Default: "" + Description: >- + Alternate subnet name + AlternateSubnetSize: + Type: String + Default: XS + AllowedValues: + - 2XS + - XS + - S + - M + - L + - XL + - 2XL + - 3XL + - 4XL + - 5XL + - 6XL + Description: >- + Size "2XS" supports up to 48 App/Web EC2 instances, Lambda functions or EMR clusters, Size "XS" supports 96 (2 * 2XS), + "S" supports 192 (2 * XS), and so on. + IngressSubnetSize: + Type: String + Default: XL + AllowedValues: + - S + - M + - L + - XL + - 2XL + Description: >- + Size "XL" supports up to 200 ELBs and "2XL" supports up to 400 (2 * XL) ELBs. + EgressSubnetSize: + Type: String + Default: XS + AllowedValues: + - XS + Description: >- + Egress subnets house NAT/Internet gateways. 96 private IP addresses are available for NAT/Internet gateways. + AlternateSubnetRouteTableName: + Type: String + Default: replication + AllowedValues: + - ingress + - egress + - private + - data + - replication + Description: >- + Name of the alternate subnet route table + +Mappings: + EnvMap: + Development: + AdminAccountId: 124755957763 + AdminRegion: "us-west-2" + Production: + AdminAccountId: 427900722408 + AdminRegion: "us-west-2" + CidrMap: + 2XS: + SubnetSize: "28" + XS: + SubnetSize: "27" + S: + SubnetSize: "26" + M: + SubnetSize: "25" + L: + SubnetSize: "24" + XL: + SubnetSize: "23" + 2XL: + SubnetSize: "22" + 3XL: + SubnetSize: "21" + 4XL: + SubnetSize: "20" + 5XL: + SubnetSize: "19" + 6XL: + SubnetSize: "18" + +Conditions: + CreateAlternateSubnet: !Not [!Equals [!Ref AlternateSubnetName, ""]] + +Resources: + uuidGenerator: + Type: Custom::uuidGenerator + Properties: + ServiceToken: + Fn::Sub: + - arn:aws:sns:${AWS::Region}:${AccountId}:uuid-generator-prd + - { AccountId: !FindInMap [EnvMap, Production, AdminAccountId] } + Account: !Sub ${AWS::AccountId} + + AppCiRequester: + Type: Custom::AppCiRequester + Properties: + ServiceToken: + Fn::Sub: + - arn:aws:sns:${AWS::Region}:${AccountId}:aws-vpn-appci-prd + - { AccountId: !FindInMap [EnvMap, Production, AdminAccountId] } + AccountId: !Sub ${AWS::AccountId} + SessionId: !GetAtt uuidGenerator.SessionID + + VpcCidrAllocator: + Type: Custom::VpcCidrAllocator + Properties: + ServiceToken: + Fn::Sub: + - arn:aws:sns:${AWS::Region}:${AccountId}:aws-vpn-cidr-prd + - { AccountId: !FindInMap [EnvMap, Production, AdminAccountId] } + Account: !Sub ${AWS::AccountId} + Region: !Sub ${AWS::Region} + Private: !FindInMap [CidrMap, !Ref PrivateSubnetSize, SubnetSize] + Data: !FindInMap [CidrMap, !Ref DataSubnetSize, SubnetSize] + Alternate: !If + - CreateAlternateSubnet + - !FindInMap [CidrMap, !Ref AlternateSubnetSize, SubnetSize] + - "0" + Ingress: !FindInMap [CidrMap, !Ref IngressSubnetSize, SubnetSize] + Egress: !FindInMap [CidrMap, !Ref EgressSubnetSize, SubnetSize] + SessionId: !GetAtt uuidGenerator.SessionID + VpcOnly: "true" + AppEnv: !GetAtt AppCiRequester.AppType + + VpcStack: + Type: AWS::CloudFormation::Stack + Properties: + TemplateURL: https://s3-us-west-2.amazonaws.com/patterns-artifacts-release/vpc-v2.7.2/cf_template_master.json + Parameters: + CidrBlock: !GetAtt VpcCidrAllocator.VpcCidr + CidrBlockPrivate: !GetAtt VpcCidrAllocator.PrivateSubnets + CidrBlockIngress: !GetAtt VpcCidrAllocator.IngressSubnets + CidrBlockEgress: !GetAtt VpcCidrAllocator.EgressSubnets + CidrBlockData: !GetAtt VpcCidrAllocator.DataSubnets + CidrBlockAlt1: !GetAtt VpcCidrAllocator.AlternateSubnets + TagCfnVersion: "2.1.2" + Alt1SubnetName: !If + - CreateAlternateSubnet + - !Ref AlternateSubnetName + - !Ref "AWS::NoValue" + Alt1SubnetRouteTable: !If + - CreateAlternateSubnet + - !Ref AlternateSubnetRouteTableName + - !Ref "AWS::NoValue" + CentralVpcLogAccount: "996354045376" + VpcFlowLogRentention: "7" + Region: !Sub ${AWS::Region} + InstanceTenancy: "default" + TagVpcName: !Ref VpcName + TagEnv: !GetAtt AppCiRequester.AppType + StackName: !Ref VpcName + ArtifactBucket: patterns-artifacts-release + ArtifactFolder: vpc-v2.7.2 + +Outputs: + VpcId: + Value: !GetAtt VpcStack.Outputs.VpcId + Description: The VPC ID diff --git a/examples/http/aws/usw2/http-stack1.yaml b/examples/http/aws/usw2/http-stack1.yaml new file mode 100644 index 0000000..f3665bd --- /dev/null +++ b/examples/http/aws/usw2/http-stack1.yaml @@ -0,0 +1,8 @@ +template: + type: http + path: "https://raw.githubusercontent.com/awslabs/aws-cloudformation-templates/master/aws/services/EC2/EC2InstanceWithSecurityGroupSample.yaml" +parameters: + VpcName: "Test" +stackname: "http-stack1" +tags: + manny-generated: true diff --git a/examples/nested/README.md b/examples/nested/README.md new file mode 100644 index 0000000..5b369f6 --- /dev/null +++ b/examples/nested/README.md @@ -0,0 +1,4 @@ +## Test Plan + +The nested configuration draws overrides from bases but also consumes a sub-directory (payments). This sub-directory +produce an additional three stacks for a total of six. \ No newline at end of file diff --git a/examples/nested/aws/config.yaml b/examples/nested/aws/config.yaml new file mode 100644 index 0000000..4964da3 --- /dev/null +++ b/examples/nested/aws/config.yaml @@ -0,0 +1,6 @@ +timeout: 15 +servicerolearn: arn:aws:iam::123456789:role/cfn-sample-role +duration: 20m +externalID: 45JBASDA +acctnum: 1234567 +expirywindow: 20m \ No newline at end of file diff --git a/examples/nested/aws/usw2/config.yaml b/examples/nested/aws/usw2/config.yaml new file mode 100644 index 0000000..b1985d4 --- /dev/null +++ b/examples/nested/aws/usw2/config.yaml @@ -0,0 +1,4 @@ +base: ../config.yaml +timeout: 10 +env: dev +region: us-west-2 \ No newline at end of file diff --git a/examples/nested/aws/usw2/localfile-stack1.yaml b/examples/nested/aws/usw2/localfile-stack1.yaml new file mode 100644 index 0000000..5b72d69 --- /dev/null +++ b/examples/nested/aws/usw2/localfile-stack1.yaml @@ -0,0 +1,10 @@ +template: + type: file + path: ../../../templates/template.yaml +parameters: + Port: 80 +stackname: "localfile-stack1" +tags: + manny-generated: true +rolearn: arn:aws:iam::527657872078:role/cfn-fa-role2 +timeout: 5 \ No newline at end of file diff --git a/examples/nested/aws/usw2/localfile-stack2.yaml b/examples/nested/aws/usw2/localfile-stack2.yaml new file mode 100644 index 0000000..fc4bdeb --- /dev/null +++ b/examples/nested/aws/usw2/localfile-stack2.yaml @@ -0,0 +1,8 @@ +template: + type: file + path: ../../../templates/template.yaml +parameters: + Port: 443 +stackname: "localfile-stack2" +tags: + manny-generated: true diff --git a/examples/nested/aws/usw2/localfile-stack3.yaml b/examples/nested/aws/usw2/localfile-stack3.yaml new file mode 100644 index 0000000..2f35427 --- /dev/null +++ b/examples/nested/aws/usw2/localfile-stack3.yaml @@ -0,0 +1,8 @@ +template: + type: file + path: ../../../templates/template.yaml +parameters: + Port: 8080 +stackname: "localfile-stack3" +tags: + manny-generated: true diff --git a/examples/nested/aws/usw2/payments/config.yaml b/examples/nested/aws/usw2/payments/config.yaml new file mode 100644 index 0000000..c315b36 --- /dev/null +++ b/examples/nested/aws/usw2/payments/config.yaml @@ -0,0 +1 @@ +base: ../config.yaml \ No newline at end of file diff --git a/examples/nested/aws/usw2/payments/localfile-stack1.yaml b/examples/nested/aws/usw2/payments/localfile-stack1.yaml new file mode 100644 index 0000000..60a9850 --- /dev/null +++ b/examples/nested/aws/usw2/payments/localfile-stack1.yaml @@ -0,0 +1,8 @@ +template: + type: file + path: ../../../../templates/template.yaml +parameters: + Port: 1000 +tags: + manny-generated: true +rolearn: arn:aws:iam::527657872078:role/cfn-fa-role2 diff --git a/examples/nested/aws/usw2/payments/localfile-stack2.yaml b/examples/nested/aws/usw2/payments/localfile-stack2.yaml new file mode 100644 index 0000000..ef745b0 --- /dev/null +++ b/examples/nested/aws/usw2/payments/localfile-stack2.yaml @@ -0,0 +1,7 @@ +template: + type: file + path: ../../../../templates/template.yaml +parameters: + Port: 1001 +tags: + manny-generated: true diff --git a/examples/nested/aws/usw2/payments/localfile-stack3.yaml b/examples/nested/aws/usw2/payments/localfile-stack3.yaml new file mode 100644 index 0000000..09fde9c --- /dev/null +++ b/examples/nested/aws/usw2/payments/localfile-stack3.yaml @@ -0,0 +1,7 @@ +template: + type: file + path: ../../../../templates/template.yaml +parameters: + Port: 1002 +tags: + manny-generated: true diff --git a/examples/resolvers/resolver.txt b/examples/resolvers/resolver.txt new file mode 100644 index 0000000..a4e15e0 --- /dev/null +++ b/examples/resolvers/resolver.txt @@ -0,0 +1,2 @@ +foo bar +hello world \ No newline at end of file diff --git a/examples/resolvers/stack-1.yaml b/examples/resolvers/stack-1.yaml new file mode 100644 index 0000000..4b69e63 --- /dev/null +++ b/examples/resolvers/stack-1.yaml @@ -0,0 +1,7 @@ +parameters: + GOPATH: !environment_variable GOPATH + data: !file_contents ./resolver.txt +stackname: test-1 +template: + type: file + path: ../templates/template.yaml \ No newline at end of file diff --git a/examples/resolvers/stack.yaml b/examples/resolvers/stack.yaml new file mode 100644 index 0000000..0ed4656 --- /dev/null +++ b/examples/resolvers/stack.yaml @@ -0,0 +1,7 @@ +parameters: + GOROOT: !environment_variable GOROOT + foo: !stack_output stack-1.yaml::vpcId +stackname: test-0 +template: + type: file + path: ../templates/template.yaml \ No newline at end of file diff --git a/examples/s3/README.md b/examples/s3/README.md new file mode 100644 index 0000000..2c1a3da --- /dev/null +++ b/examples/s3/README.md @@ -0,0 +1 @@ +Manny s3 configuration examples. diff --git a/examples/s3/aws/usw2/s3-stack1.yaml b/examples/s3/aws/usw2/s3-stack1.yaml new file mode 100644 index 0000000..ef919ed --- /dev/null +++ b/examples/s3/aws/usw2/s3-stack1.yaml @@ -0,0 +1,8 @@ +template: + type: s3 + path: "s3://cfn-usw2/S3_Bucket.template" +parameters: + VpcName: "Test" +stackname: "s3-stack1" +tags: + manny-generated: true diff --git a/examples/s3/aws/usw2/s3-stack2.yaml b/examples/s3/aws/usw2/s3-stack2.yaml new file mode 100644 index 0000000..84c49c0 --- /dev/null +++ b/examples/s3/aws/usw2/s3-stack2.yaml @@ -0,0 +1,8 @@ +template: + type: s3 + path: "s3://cfn-usw2/S3_Bucket.template" +parameters: + Port: 8080 +stackname: "s3-stack2" +tags: + manny-generated: true diff --git a/examples/static/README.md b/examples/static/README.md new file mode 100644 index 0000000..1356a0c --- /dev/null +++ b/examples/static/README.md @@ -0,0 +1,2 @@ +Manny can be configured with or without a `config.yaml`. Configuring without a `config.yaml` or bases is called static +configuration. \ No newline at end of file diff --git a/examples/static/aws/use2/localfile-stack1.yaml b/examples/static/aws/use2/localfile-stack1.yaml new file mode 100644 index 0000000..8563ebd --- /dev/null +++ b/examples/static/aws/use2/localfile-stack1.yaml @@ -0,0 +1,11 @@ +template: + type: file + path: ../../../templates/template.yaml +parameters: + Port: 80 + vpcId: !environment_variable vpc_Id +stackname: "localfile-stack1" +tags: + manny-generated: true +rolearn: arn:aws:iam::123456789:role/cfn-fa-role2 +syncwave: 3 \ No newline at end of file diff --git a/examples/static/aws/use2/localfile-stack2.yaml b/examples/static/aws/use2/localfile-stack2.yaml new file mode 100644 index 0000000..dcc85bd --- /dev/null +++ b/examples/static/aws/use2/localfile-stack2.yaml @@ -0,0 +1,10 @@ +template: + type: file + path: ../../../templates/template.yaml +parameters: + Port: 80 + vpcId: !environment_variable vpc_Id +stackname: "localfile-stack2" +tags: + manny-generated: true +rolearn: arn:aws:iam::123456789:role/cfn-fa-role2 \ No newline at end of file diff --git a/examples/static/aws/usw2/localfile-stack1.yaml b/examples/static/aws/usw2/localfile-stack1.yaml new file mode 100644 index 0000000..7fb396b --- /dev/null +++ b/examples/static/aws/usw2/localfile-stack1.yaml @@ -0,0 +1,10 @@ +template: + type: file + path: ../../../templates/template.yaml +parameters: + Port: 80 + vpcId: !environment_variable vpc_Id +stackname: "localfile-stack1" +tags: + manny-generated: true +rolearn: arn:aws:iam::123456789:role/cfn-fa-role2 diff --git a/examples/static/aws/usw2/localfile-stack2.yaml b/examples/static/aws/usw2/localfile-stack2.yaml new file mode 100644 index 0000000..59bb878 --- /dev/null +++ b/examples/static/aws/usw2/localfile-stack2.yaml @@ -0,0 +1,10 @@ +template: + type: file + path: ../../../templates/template.yaml +parameters: + Port: 443 + data: !file_contents ./sample.yaml + foo: !stack_output ../foo/vpc.yaml::vpcId +stackname: "localfile-stack2" +tags: + manny-generated: true diff --git a/examples/static/aws/usw2/localfile-stack3.yaml b/examples/static/aws/usw2/localfile-stack3.yaml new file mode 100644 index 0000000..2fa67c4 --- /dev/null +++ b/examples/static/aws/usw2/localfile-stack3.yaml @@ -0,0 +1,8 @@ +template: + type: file + path: ../../../templates/template.yaml +parameters: + Port: 8080 + vpcId: !stack_output ../foo/localfile-stack1.yaml::vpcId +tags: + manny-generated: true diff --git a/examples/static/aws/usw2/sample.yaml b/examples/static/aws/usw2/sample.yaml new file mode 100644 index 0000000..a4e15e0 --- /dev/null +++ b/examples/static/aws/usw2/sample.yaml @@ -0,0 +1,2 @@ +foo bar +hello world \ No newline at end of file diff --git a/examples/templates/template.yaml b/examples/templates/template.yaml new file mode 100644 index 0000000..f5b48ba --- /dev/null +++ b/examples/templates/template.yaml @@ -0,0 +1,28 @@ +AWSTemplateFormatVersion: 2010-09-09 + +Parameters: + ChapiVersion: + Description: The version of the chapi template to use. + Type: String + Default: latest + VpcId: + Type: AWS::EC2::VPC::Id + Description: >- + The VPC where the endpoint has to be deployed. + Port: + Type: Number + Description: The port to allow inbound from Direct Expert Access Egress CIDRs + Default: '443' + +Resources: + ExpertIngressSG: + Type: AWS::CloudFormation::Stack + Properties: + TemplateURL: !Sub + - "https://raw.githubusercontent.com/awslabs/aws-cloudformation-templates/master/aws/services/EC2/EC2InstanceWithSecurityGroupSample.yaml" + - { Version: !Ref ChapiVersion } + Parameters: + VpcId: + Ref: VpcId + Port: + Ref: Port \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3799cf7 --- /dev/null +++ b/go.mod @@ -0,0 +1,30 @@ +module github.com/keikoproj/manny + +go 1.13 + +require ( + github.com/aws/aws-sdk-go v1.32.5 + github.com/go-git/go-git/v5 v5.0.0 + github.com/golang/protobuf v1.4.2 // indirect + github.com/google/go-cmp v0.5.0 // indirect + github.com/imdario/mergo v0.3.9 + github.com/json-iterator/go v1.1.10 // indirect + github.com/keikoproj/cloudresource-manager v0.0.0-20210610065713-81b7b045e158 + github.com/onsi/gomega v1.8.1 + github.com/qri-io/jsonschema v0.1.0 + github.com/spf13/cobra v0.0.6 + github.com/stretchr/testify v1.5.1 + go.uber.org/zap v1.15.0 + golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 // indirect + golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect + golang.org/x/net v0.0.0-20200602114024-627f9648deb9 // indirect + golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 // indirect + golang.org/x/text v0.3.3 // indirect + golang.org/x/tools v0.0.0-20200606014950-c42cb6316fb6 // indirect + google.golang.org/protobuf v1.24.0 // indirect + gopkg.in/yaml.v2 v2.3.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 + honnef.co/go/tools v0.0.1-2020.1.4 // indirect + k8s.io/api v0.18.4 // indirect + sigs.k8s.io/controller-runtime v0.6.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6e9f350 --- /dev/null +++ b/go.sum @@ -0,0 +1,670 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +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-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +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/aslakhellesoy/gox v1.0.100/go.mod h1:AJl542QsKKG96COVsv0N74HHzVQgDIQPceVUh1aeU2M= +github.com/aws/aws-sdk-go v1.30.13/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.32.5 h1:Sz0C7deIoMu5lFGTVkIN92IEZrUz1AWIDDW+9p6n1Rk= +github.com/aws/aws-sdk-go v1.32.5/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +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-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cucumber/gherkin-go/v11 v11.0.0/go.mod h1:CX33k2XU2qog4e+TFjOValoq6mIUq0DmVccZs238R9w= +github.com/cucumber/godog v0.9.0/go.mod h1:roWCHkpeK6UTOyIRRl7IR+fgfBeZ4vZR7OSq2J/NbM4= +github.com/cucumber/messages-go/v10 v10.0.1/go.mod h1:kA5T38CBlBbYLU12TIrJ4fk4wSkVVOgyh7Enyy8WnSg= +github.com/cucumber/messages-go/v10 v10.0.3/go.mod h1:9jMZ2Y8ZxjLY6TG2+x344nt5rXstVVDYSdS5ySfI1WY= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +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/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +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-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc= +github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg= +github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54= +github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +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.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +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/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +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/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +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/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.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +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/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +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/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.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +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/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.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +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/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= +github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/keikoproj/cloudresource-manager v0.0.0-20210610065713-81b7b045e158 h1:ldXCg+ecfljWs5FWjIMGN5uCBT5LeTsT43nlI+vRR1s= +github.com/keikoproj/cloudresource-manager v0.0.0-20210610065713-81b7b045e158/go.mod h1:8JMX7xw2dl2oXKITV6U4zCI3rPXhT85ZFXedf3Hy/rY= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +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/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= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +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.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +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/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +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= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/qri-io/jsonpointer v0.1.0 h1:OcTtTmorodUCRc2CZhj/ZwOET8zVj6uo0ArEmzoThZI= +github.com/qri-io/jsonpointer v0.1.0/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64= +github.com/qri-io/jsonschema v0.1.0 h1:tqV3dOfgSsF2FOqQ8oNWbo87Jh3dwlN5luXsgioilxE= +github.com/qri-io/jsonschema v0.1.0/go.mod h1:QpzJ6gBQ0GYgGmh7mDQ1YsvvhSgE4rYj0k8t5MBOmUY= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/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 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +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/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +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.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= +github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +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 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= +github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8= +github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +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.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/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-20190510104115-cbcb75029529/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-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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-20180906233101-161cd47e91fd/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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190522155817-f3200d17e092/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-20190813141303-74dc4d7220e7/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-20191004110552-13f9640d40b9/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190209173611-3b5209105503/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-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/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-20190206041539-40960b6deb8e/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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +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-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/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-20200606014950-c42cb6316fb6 h1:5Y8c5HBW6hBYnGEE3AbJPV0R8RsQmg1/eaJrpvasns0= +golang.org/x/tools v0.0.0-20200606014950-c42cb6316fb6/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= +gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +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.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4= +k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8= +k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= +k8s.io/api v0.18.4 h1:8x49nBRxuXGUlDlwlWd3RMY1SayZrzFfxea3UZSkFw4= +k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4= +k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= +k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8= +k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= +k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= +k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA= +k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= +k8s.io/apimachinery v0.18.4 h1:ST2beySjhqwJoIFk6p7Hp5v5O0hYY6Gngq/gUYXTPIA= +k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= +k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= +k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= +k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI= +k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE= +k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= +k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= +k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= +k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs= +k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM= +k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY= +k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= +k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= +sigs.k8s.io/controller-runtime v0.5.2/go.mod h1:JZUwSMVbxDupo0lTJSSFP5pimEyxGynROImSsqIOx1A= +sigs.k8s.io/controller-runtime v0.6.0 h1:Fzna3DY7c4BIP6KwfSlrfnj20DJ+SeMBK8HSFvOk9NM= +sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU= +sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/main.go b/main.go new file mode 100644 index 0000000..68728aa --- /dev/null +++ b/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/keikoproj/manny/cmd" + +func main() { + cmd.Execute() +} diff --git a/tests/base-reference-cap/config-1.yaml b/tests/base-reference-cap/config-1.yaml new file mode 100644 index 0000000..dec1ae2 --- /dev/null +++ b/tests/base-reference-cap/config-1.yaml @@ -0,0 +1 @@ +base: config-2.yaml \ No newline at end of file diff --git a/tests/base-reference-cap/config-10.yaml b/tests/base-reference-cap/config-10.yaml new file mode 100644 index 0000000..863ffa7 --- /dev/null +++ b/tests/base-reference-cap/config-10.yaml @@ -0,0 +1 @@ +base: config-11.yaml \ No newline at end of file diff --git a/tests/base-reference-cap/config-11.yaml b/tests/base-reference-cap/config-11.yaml new file mode 100644 index 0000000..e69de29 diff --git a/tests/base-reference-cap/config-2.yaml b/tests/base-reference-cap/config-2.yaml new file mode 100644 index 0000000..b0aa28e --- /dev/null +++ b/tests/base-reference-cap/config-2.yaml @@ -0,0 +1 @@ +base: config-3.yaml \ No newline at end of file diff --git a/tests/base-reference-cap/config-3.yaml b/tests/base-reference-cap/config-3.yaml new file mode 100644 index 0000000..89fb97e --- /dev/null +++ b/tests/base-reference-cap/config-3.yaml @@ -0,0 +1 @@ +base: config-4.yaml \ No newline at end of file diff --git a/tests/base-reference-cap/config-4.yaml b/tests/base-reference-cap/config-4.yaml new file mode 100644 index 0000000..118b346 --- /dev/null +++ b/tests/base-reference-cap/config-4.yaml @@ -0,0 +1 @@ +base: config-5.yaml \ No newline at end of file diff --git a/tests/base-reference-cap/config-5.yaml b/tests/base-reference-cap/config-5.yaml new file mode 100644 index 0000000..7100dd5 --- /dev/null +++ b/tests/base-reference-cap/config-5.yaml @@ -0,0 +1 @@ +base: config-6.yaml \ No newline at end of file diff --git a/tests/base-reference-cap/config-6.yaml b/tests/base-reference-cap/config-6.yaml new file mode 100644 index 0000000..08db592 --- /dev/null +++ b/tests/base-reference-cap/config-6.yaml @@ -0,0 +1 @@ +base: config-7.yaml \ No newline at end of file diff --git a/tests/base-reference-cap/config-7.yaml b/tests/base-reference-cap/config-7.yaml new file mode 100644 index 0000000..42a01fd --- /dev/null +++ b/tests/base-reference-cap/config-7.yaml @@ -0,0 +1 @@ +base: config-8.yaml \ No newline at end of file diff --git a/tests/base-reference-cap/config-8.yaml b/tests/base-reference-cap/config-8.yaml new file mode 100644 index 0000000..77c42cc --- /dev/null +++ b/tests/base-reference-cap/config-8.yaml @@ -0,0 +1 @@ +base: config-9.yaml \ No newline at end of file diff --git a/tests/base-reference-cap/config-9.yaml b/tests/base-reference-cap/config-9.yaml new file mode 100644 index 0000000..ce7ea57 --- /dev/null +++ b/tests/base-reference-cap/config-9.yaml @@ -0,0 +1 @@ +base: config-10.yaml \ No newline at end of file diff --git a/tests/base-reference-cap/config.yaml b/tests/base-reference-cap/config.yaml new file mode 100644 index 0000000..ba19e58 --- /dev/null +++ b/tests/base-reference-cap/config.yaml @@ -0,0 +1 @@ +base: config-1.yaml \ No newline at end of file diff --git a/tests/circular-dependency/config.yaml b/tests/circular-dependency/config.yaml new file mode 100644 index 0000000..4889b80 --- /dev/null +++ b/tests/circular-dependency/config.yaml @@ -0,0 +1 @@ +base: config2.yaml \ No newline at end of file diff --git a/tests/circular-dependency/config2.yaml b/tests/circular-dependency/config2.yaml new file mode 100644 index 0000000..4150da3 --- /dev/null +++ b/tests/circular-dependency/config2.yaml @@ -0,0 +1 @@ +base: config.yaml \ No newline at end of file diff --git a/utils/file.go b/utils/file.go new file mode 100644 index 0000000..0febac8 --- /dev/null +++ b/utils/file.go @@ -0,0 +1,34 @@ +package utils + +import ( + "errors" + "io/ioutil" + "os" + "path/filepath" +) + +const ( + ErrDestinationIsDir = "Destination is a directory: " + ErrDestinationExists = "Destination already exists: " +) + +// ValidateAndWrite checks that a location does not exist and that it is not a directory +func ValidateAndWrite(location string, bytes []byte) (bool, error) { + // validation + location = filepath.Clean(location) + if fl, err := os.Stat(location); !os.IsNotExist(err) { + if fl.Mode().IsDir() { + return false, errors.New(ErrDestinationIsDir + location) + } + + return false, errors.New(ErrDestinationExists + location) + } + + // write + err := ioutil.WriteFile(location, bytes, 0644) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/utils/file_test.go b/utils/file_test.go new file mode 100644 index 0000000..88a3529 --- /dev/null +++ b/utils/file_test.go @@ -0,0 +1,131 @@ +package utils + +import ( + "io/ioutil" + "os" + "reflect" + "strings" + "testing" +) + +func Test_ValidateAndWrite(t *testing.T) { + // test table + tt := []struct { + // Name of the test + Identifier string + // PreDir will create a directory before ValidateAndWrite() is called + PreDir string + // PreWrite will write to a file before ValidateAndWrite() is called + PreWrite string + // File location to write to, passed to ValidateAndWrite() + Location string + // File content to write, passed to ValidateAndWrite() + Content []byte + // Whether we want an error + WantErr bool + // What you want the return bool to be + Output bool + // The error that the message should be prefixed with + ErrPrefix string + }{ + { + "Valid", + "", + "", + "/tmp/manny_test", + []byte("test"), + false, + true, + "", + }, + { + "Location already exists", + "", + "/tmp/manny_test", + "/tmp/manny_test", + []byte("test"), + true, + false, + ErrDestinationExists, + }, + { + "Location is a directory", + "/tmp/manny_test", + "", + "/tmp/manny_test", + []byte("test"), + true, + false, + ErrDestinationIsDir, + }, + } + + // testing loop + for _, tc := range tt { + var cleanup []string + + t.Run(tc.Identifier, func(t *testing.T) { + // creates a file + if tc.PreWrite != "" { + cleanup = append(cleanup, tc.PreWrite) + err := ioutil.WriteFile(tc.PreWrite, tc.Content, 0644) + if err != nil { + t.Errorf("Error reading file: %s", err) + } + } + + // creates a directory + if tc.PreDir != "" { + cleanup = append(cleanup, tc.PreDir) + err := os.Mkdir(tc.PreDir, 0644) + if err != nil { + t.Errorf("Error reading file: %s", err) + } + } + + got, err := ValidateAndWrite(tc.Location, tc.Content) + haveErr := err != nil + + // clean up any files that were generated + if got { + cleanup = append(cleanup, tc.Location) + } + + // evaluate output + if tc.Output != got { + t.Errorf("Error with output: got: %t, want: %t", got, tc.Output) + } + + // err prefix is wrong + if haveErr && tc.WantErr && !strings.HasPrefix(err.Error(), tc.ErrPrefix) { + t.Errorf("Error not prefixed as expected. got: %s, want: %s", err.Error(), tc.ErrPrefix) + } + + // didn't expect an error but got one + if haveErr && !tc.WantErr { + t.Errorf("Got: %s, want: nil", err) + } + + // expected an error but didn't get one + if haveErr && err == nil { + t.Errorf("Got: nil,") + } + + b, err := ioutil.ReadFile(tc.Location) + if err != nil && tc.Output { + t.Errorf("Error reading file: %s", err) + } + + if !reflect.DeepEqual(tc.Content, b) && tc.Output { + t.Errorf("File content is not the same. got: %s, want: %s", b, tc.Content) + } + }) + + for _, location := range cleanup { + err := os.Remove(location) + if err != nil { + t.Errorf("Error removing file: %s", err) + } + } + } +} diff --git a/utils/git.go b/utils/git.go new file mode 100644 index 0000000..7241b81 --- /dev/null +++ b/utils/git.go @@ -0,0 +1,24 @@ +package utils + +import ( + "path/filepath" + + "github.com/go-git/go-git/v5" +) + +// GitRepoRemote gets the remote URL of the git repo +func GitRepoRemote(location string) (string, error) { + path := filepath.Join(location, git.GitDirName) + + r, err := git.PlainOpenWithOptions(path, &git.PlainOpenOptions{DetectDotGit: true}) + if err != nil { + return "", err + } + + rem, err := r.Remote(git.DefaultRemoteName) + if err != nil { + return "", err + } + + return rem.Config().URLs[0], nil +} diff --git a/utils/git_test.go b/utils/git_test.go new file mode 100644 index 0000000..305458a --- /dev/null +++ b/utils/git_test.go @@ -0,0 +1,60 @@ +package utils + +import ( + "strings" + "testing" +) + +func Test_GitRepoRemote(t *testing.T) { + // test table + tt := []struct { + Identifier string + Path string + Output string + WantErr bool + ErrPrefix string + }{ + { + "Valid", + "../", + "keikoproj/manny.git", + false, + "", + }, + { + "Not a git directory", + "/tmp", + "", + true, + "repository does not exist", + }, + } + + // testing loop + for _, tc := range tt { + t.Run(tc.Identifier, func(t *testing.T) { + out, err := GitRepoRemote(tc.Path) + haveErr := err != nil + + // evaluate output + if !strings.Contains(out, tc.Output) { + t.Errorf("Error with output: got: %s, want: %s", out, tc.Output) + } + + // err prefix is wrong + if haveErr && tc.WantErr && !strings.HasPrefix(err.Error(), tc.ErrPrefix) { + t.Errorf("Error not prefixed as expected. got: %s, want: %s", err.Error(), tc.ErrPrefix) + } + + // didn't expect an error but got one + if haveErr && !tc.WantErr { + t.Errorf("Got: %s, want: nil", err) + } + + // expected an error but didn't get one + if haveErr && err == nil { + t.Errorf("Got: nil,") + } + }) + } +}