diff --git a/README.md b/README.md index 93025ee4..b41550c4 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,52 @@ Defines options for the starter pod. This includes: * passing in custom image * passing in labels and annotations +### k6 outputs + +#### k6 Cloud output + +k6 supports [output to its Cloud](https://k6.io/docs/results-visualization/cloud) with `k6 run --out cloud script.js` command. This feature is available in k6-operator as well for subscribed users. Note that it supports only `parallelism: 20` or less. + +To use this option in k6-operator, set the argument in yaml: + +```yaml +... + script: + configMap: + name: "" + arguments: --out cloud +... +``` + +Then uncomment cloud output section in `config/default/kustomization.yaml` and copy your token from the Cloud there: + +```yaml +# Uncomment this section if you need cloud output and copy-paste your token +secretGenerator: +- name: cloud-token + literals: + - token= + options: + annotations: + kubernetes.io/service-account.name: k6-operator-controller + labels: + k6cloud: token +``` + +This is sufficient to run k6 with the Cloud output and default values of `projectID` and `name` (`"k6-operator-test"`). For non-default values, extended script options can be used like this: + +```js +export let options = { + ... + ext: { + loadimpact: { + name: 'Configured k6-operator test', + projectID: 1234567, + } + } +}; +``` + ### Cleaning up between test runs After completing a test run, you need to clean up the test jobs created. This is done by running the following command: ```bash diff --git a/api/v1alpha1/k6_types.go b/api/v1alpha1/k6_types.go index 8d901cf5..fdc31a01 100644 --- a/api/v1alpha1/k6_types.go +++ b/api/v1alpha1/k6_types.go @@ -90,7 +90,7 @@ type K6Configmap struct { type Cleanup string // Stage describes which stage of the test execution lifecycle our runners are in -// +kubebuilder:validation:Enum=created;started;finished +// +kubebuilder:validation:Enum=initialization;initialized;created;started;finished type Stage string // K6Status defines the observed state of K6 diff --git a/config/crd/bases/k6.io_k6s.yaml b/config/crd/bases/k6.io_k6s.yaml index 0ce4aa8f..de4b2b81 100644 --- a/config/crd/bases/k6.io_k6s.yaml +++ b/config/crd/bases/k6.io_k6s.yaml @@ -1956,6 +1956,8 @@ spec: description: Stage describes which stage of the test execution lifecycle our runners are in enum: + - initialization + - initialized - created - started - finished diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 254418f6..0b48ca42 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -62,3 +62,14 @@ vars: # kind: Service # version: v1 # name: webhook-service + +# Uncomment this section if you need cloud output and copy-paste your token +# secretGenerator: +# - name: cloud-token +# literals: +# - token= +# options: +# annotations: +# kubernetes.io/service-account.name: k6-operator-controller +# labels: +# k6cloud: token diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 9ad24385..1be289c2 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -45,6 +45,7 @@ rules: - "" resources: - pods + - pods/log verbs: - get - list @@ -70,3 +71,11 @@ rules: - get - patch - update + - apiGroups: + - "" + resources: + - secrets + verbs: + - list + - get + - watch diff --git a/controllers/k6_controller.go b/controllers/k6_controller.go index d6641550..03a2e161 100644 --- a/controllers/k6_controller.go +++ b/controllers/k6_controller.go @@ -56,12 +56,22 @@ func (r *K6Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { return ctrl.Result{Requeue: true}, err } + log.Info(fmt.Sprintf("Reconcile(); stage = %s", k6.Status.Stage)) + switch k6.Status.Stage { case "": + return InitializeJobs(ctx, log, k6, r) + case "initialization": + // here we're just waiting until initialize is done + // Note: it is present as a separate stage to ensure there's only one + // initialization job at a time + return ctrl.Result{}, nil + case "initialized": return CreateJobs(ctx, log, k6, r) case "created": return StartJobs(ctx, log, k6, r) case "started": + // wait for test to finish and then mark as finished return FinishJobs(ctx, log, k6, r) case "finished": // delete if configured diff --git a/controllers/k6_create.go b/controllers/k6_create.go index 957085c2..12c5479b 100644 --- a/controllers/k6_create.go +++ b/controllers/k6_create.go @@ -14,7 +14,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" ) -// CreateJobs that will spawn k6 pods, running distributed tests +// CreateJobs creates jobs that will spawn k6 pods for distributed test func CreateJobs(ctx context.Context, log logr.Logger, k6 *v1alpha1.K6, r *K6Reconciler) (ctrl.Result, error) { var err error var res ctrl.Result @@ -25,6 +25,7 @@ func CreateJobs(ctx context.Context, log logr.Logger, k6 *v1alpha1.K6, r *K6Reco return res, err } + log.Info("Changing stage of K6 status to created") k6.Status.Stage = "created" if err = r.Client.Status().Update(ctx, k6); err != nil { log.Error(err, "Could not update status of custom resource") @@ -62,11 +63,14 @@ func launchTest(ctx context.Context, k6 *v1alpha1.K6, index int, log logr.Logger msg := fmt.Sprintf("Launching k6 test #%d", index) log.Info(msg) - if job, err = jobs.NewRunnerJob(k6, index); err != nil { + if job, err = jobs.NewRunnerJob(k6, index, testRunId, token); err != nil { log.Error(err, "Failed to generate k6 test job") return err } + log.Info(fmt.Sprintf("Runner job is ready to start with image `%s` and command `%s`", + job.Spec.Template.Spec.Containers[0].Image, job.Spec.Template.Spec.Containers[0].Command)) + if err = ctrl.SetControllerReference(k6, job, r.Scheme); err != nil { log.Error(err, "Failed to set controller reference for job") return err diff --git a/controllers/k6_finish.go b/controllers/k6_finish.go index b7fb9550..8dffd10f 100644 --- a/controllers/k6_finish.go +++ b/controllers/k6_finish.go @@ -3,47 +3,85 @@ package controllers import ( "context" "fmt" + "time" "github.com/go-logr/logr" "github.com/grafana/k6-operator/api/v1alpha1" + "github.com/grafana/k6-operator/pkg/cloud" + "github.com/grafana/k6-operator/pkg/types" batchv1 "k8s.io/api/batch/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/wait" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" ) -// Mark k6 as finished as jobs finish +// FinishJobs waits for the pods to finish, performs finishing call for cloud output and moves state to "finished". func FinishJobs(ctx context.Context, log logr.Logger, k6 *v1alpha1.K6, r *K6Reconciler) (ctrl.Result, error) { - selector := labels.SelectorFromSet(map[string]string{ - "app": "k6", - "k6_cr": k6.Name, - }) + log.Info("Waiting for pods to finish") - opts := &client.ListOptions{LabelSelector: selector, Namespace: k6.Namespace} - jl := &batchv1.JobList{} + // Here we assume that the test runs for some time and there is no need to + // check it more often than twice in a minute. + // + // The total timeout for the test is set to duration of the test + 2 min. + // These 2 min are meant to cover the time needed to start the pods: sometimes + // pods are ready a bit later than operator reaches this stage so from the + // viewpoint of operator it takes longer. This behaviour depends on the setup of + // cluster. 2 min are meant to be a sufficient safeguard for such cases. - if err := r.List(ctx, jl, opts); err != nil { - log.Error(err, "Could not list jobs") - return ctrl.Result{}, err - } + testDuration := inspectOutput.TotalDuration.TimeDuration() + + err := wait.PollImmediate(time.Second*30, testDuration+time.Minute*2, func() (done bool, err error) { + selector := labels.SelectorFromSet(map[string]string{ + "app": "k6", + "k6_cr": k6.Name, + "runner": "true", + }) + + opts := &client.ListOptions{LabelSelector: selector, Namespace: k6.Namespace} + jl := &batchv1.JobList{} - //TODO: We should distinguish between suceeded/failed - var finished int32 - for _, job := range jl.Items { - if job.Status.Active != 0 { - continue + if err := r.List(ctx, jl, opts); err != nil { + log.Error(err, "Could not list jobs") + return false, nil + } + + // TODO: We should distinguish between Suceeded/Failed/Unknown + var finished int32 + for _, job := range jl.Items { + if job.Status.Active != 0 { + continue + } + finished++ } - finished++ - } - log.Info(fmt.Sprintf("%d/%d jobs complete", finished, k6.Spec.Parallelism+1)) + log.Info(fmt.Sprintf("%d/%d jobs complete", finished, k6.Spec.Parallelism)) - // parallelism (pods) + starter pod = total expected - if finished == k6.Spec.Parallelism+1 { - k6.Status.Stage = "finished" - if err := r.Client.Status().Update(ctx, k6); err != nil { - log.Error(err, "Could not update status of custom resource") + if finished >= k6.Spec.Parallelism { + return true, nil } + + return false, nil + }) + + if err != nil { + log.Error(err, "Waiting for pods to finish ended with error") + } + + // If this is a test run with cloud output, try to finalize it regardless. + if cli := types.ParseCLI(&k6.Spec); cli.HasCloudOut { + if err = cloud.FinishTestRun(testRunId); err != nil { + log.Error(err, "Could not finalize the test run with cloud output") + } else { + log.Info(fmt.Sprintf("Cloud test run %s was finalized succesfully", testRunId)) + } + } + + log.Info("Changing stage of K6 status to finished") + k6.Status.Stage = "finished" + if err = r.Client.Status().Update(ctx, k6); err != nil { + log.Error(err, "Could not update status of custom resource") + return ctrl.Result{}, err } return ctrl.Result{}, nil diff --git a/controllers/k6_initialize.go b/controllers/k6_initialize.go new file mode 100644 index 00000000..8d804a12 --- /dev/null +++ b/controllers/k6_initialize.go @@ -0,0 +1,211 @@ +package controllers + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "time" + + "github.com/go-logr/logr" + "github.com/grafana/k6-operator/api/v1alpha1" + "github.com/grafana/k6-operator/pkg/cloud" + "github.com/grafana/k6-operator/pkg/resources/jobs" + "github.com/grafana/k6-operator/pkg/types" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// k6 Cloud related vars +// Right now operator works with one test at a time so these should be safe. +var ( + testRunId string + token string + inspectOutput cloud.InspectOutput +) + +// InitializeJobs creates jobs that will run initial checks for distributed test if any are necessary +func InitializeJobs(ctx context.Context, log logr.Logger, k6 *v1alpha1.K6, r *K6Reconciler) (res ctrl.Result, err error) { + log.Info("Initialize test") + + log.Info("Changing stage of K6 status to initialization") + k6.Status.Stage = "initialization" + if err = r.Client.Status().Update(ctx, k6); err != nil { + log.Error(err, "Could not update status of custom resource") + return + } + + if cli := types.ParseCLI(&k6.Spec); cli.HasCloudOut { + var ( + secrets corev1.SecretList + secretOpts = &client.ListOptions{ + // TODO: find out a better way to get namespace here + Namespace: "k6-operator-system", + LabelSelector: labels.SelectorFromSet(map[string]string{ + "k6cloud": "token", + }), + } + ) + if err := r.List(ctx, &secrets, secretOpts); err != nil { + log.Error(err, "Failed to load k6 Cloud token") + return res, err + } + + if len(secrets.Items) < 1 { + err := fmt.Errorf("There are no secrets to hold k6 Cloud token") + log.Error(err, err.Error()) + return res, err + } + + if t, ok := secrets.Items[0].Data["token"]; !ok { + err := fmt.Errorf("The secret doesn't have a field token for k6 Cloud") + log.Error(err, err.Error()) + return res, err + } else { + token = string(t) + } + log.Info("Token for k6 Cloud was loaded.") + + var initializer *batchv1.Job + if initializer, err = jobs.NewInitializerJob(k6, cli.ArchiveArgs); err != nil { + return res, err + } + + log.Info(fmt.Sprintf("Initializer job is ready to start with image `%s` and command `%s`", + initializer.Spec.Template.Spec.Containers[0].Image, initializer.Spec.Template.Spec.Containers[0].Command)) + + if err = ctrl.SetControllerReference(k6, initializer, r.Scheme); err != nil { + log.Error(err, "Failed to set controller reference for the initialize job") + return + } + + if err = r.Create(ctx, initializer); err != nil { + log.Error(err, "Failed to launch k6 test initializer") + return + } + err = wait.PollImmediate(time.Second*5, time.Second*60, func() (done bool, err error) { + var ( + listOpts = &client.ListOptions{ + Namespace: k6.Namespace, + LabelSelector: labels.SelectorFromSet(map[string]string{ + "app": "k6", + "k6_cr": k6.Name, + "job-name": fmt.Sprintf("%s-initializer", k6.Name), + }), + } + podList = &corev1.PodList{} + ) + if err := r.List(ctx, podList, listOpts); err != nil { + log.Error(err, "Could not list pods") + return false, err + } + if len(podList.Items) < 1 { + log.Info("No initializing pod found yet") + return false, nil + } + + // there should be only 1 initializer pod + if podList.Items[0].Status.Phase != "Succeeded" { + log.Info("Waiting for initializing pod to finish") + return false, nil + } + + // Here we need to get the output of the pod + // pods/log is not currently supported by controller-runtime client and it is officially + // recommended to use REST client instead: + // https://github.com/kubernetes-sigs/controller-runtime/issues/1229 + + var opts corev1.PodLogOptions + config, err := rest.InClusterConfig() + if err != nil { + log.Error(err, "unable to fetch in-cluster REST config") + // don't return here + return false, nil + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + log.Error(err, "unable to get access to clientset") + // don't return here + return false, nil + } + req := clientset.CoreV1().Pods(k6.Namespace).GetLogs(podList.Items[0].Name, &opts) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*60) + defer cancel() + + podLogs, err := req.Stream(ctx) + if err != nil { + log.Error(err, "unable to stream logs from the pod") + // don't return here + return false, nil + } + defer podLogs.Close() + + buf := new(bytes.Buffer) + _, err = io.Copy(buf, podLogs) + if err != nil { + log.Error(err, "unable to copy logs from the pod") + return false, err + } + + if err := json.Unmarshal(buf.Bytes(), &inspectOutput); err != nil { + // this shouldn't normally happen but if it does, let's log output by default + log.Error(err, fmt.Sprintf("unable to marshal: `%s`", buf.String())) + return true, err + } + + log.Info(fmt.Sprintf("k6 inspect: %+v", inspectOutput)) + + if int32(inspectOutput.MaxVUs) < k6.Spec.Parallelism { + err = fmt.Errorf("number of instances > number of VUs") + // TODO maybe change this to a warning and simply set parallelism = maxVUs and proceed with execution? + // But logr doesn't seem to have warning level by default, only with V() method... + // It makes sense to return to this after / during logr VS logrus issue https://github.com/grafana/k6-operator/issues/84 + log.Error(err, "Parallelism argument cannot be larger than maximum VUs in the script", + "maxVUs", inspectOutput.MaxVUs, + "parallelism", k6.Spec.Parallelism) + return false, err + } + + host := getEnvVar(k6.Spec.Runner.Env, "K6_CLOUD_HOST") + + if refID, err := cloud.CreateTestRun(inspectOutput, k6.Spec.Parallelism, host, token, log); err != nil { + return true, err + } else { + testRunId = refID + log.Info(fmt.Sprintf("Created cloud test run: %s", testRunId)) + return true, nil + } + }) + } + + if err != nil { + log.Error(err, "Failed to initialize script with cloud output") + return + } + + log.Info("Changing stage of K6 status to initialized") + k6.Status.Stage = "initialized" + if err = r.Client.Status().Update(ctx, k6); err != nil { + log.Error(err, "Could not update status of custom resource") + return + } + + return res, nil +} + +func getEnvVar(vars []corev1.EnvVar, name string) string { + for _, v := range vars { + if v.Name == name { + return v.Value + } + } + return "" +} diff --git a/controllers/k6_start.go b/controllers/k6_start.go index 358c7ee3..4cc90f14 100644 --- a/controllers/k6_start.go +++ b/controllers/k6_start.go @@ -33,13 +33,13 @@ func StartJobs(ctx context.Context, log logr.Logger, k6 *v1alpha1.K6, r *K6Recon err := wait.PollImmediate(time.Second*5, time.Second*60, func() (done bool, err error) { selector := labels.SelectorFromSet(map[string]string{ - "app": "k6", - "k6_cr": k6.Name, + "app": "k6", + "k6_cr": k6.Name, + "runner": "true", }) opts := &client.ListOptions{LabelSelector: selector, Namespace: k6.Namespace} pl := &v1.PodList{} - if e := r.List(ctx, pl, opts); e != nil { log.Error(e, "Could not list pods") return false, e @@ -80,7 +80,7 @@ func StartJobs(ctx context.Context, log logr.Logger, k6 *v1alpha1.K6, r *K6Recon starter := jobs.NewStarterJob(k6, hostnames) if err = ctrl.SetControllerReference(k6, starter, r.Scheme); err != nil { - log.Error(err, "Failed to set controller reference for job") + log.Error(err, "Failed to set controller reference for the start job") } if err = r.Create(ctx, starter); err != nil { @@ -96,6 +96,7 @@ func StartJobs(ctx context.Context, log logr.Logger, k6 *v1alpha1.K6, r *K6Recon return ctrl.Result{}, err } + log.Info("Changing stage of K6 status to started") k6.Status.Stage = "started" if err = r.Client.Status().Update(ctx, k6); err != nil { log.Error(err, "Could not update status of custom resource") diff --git a/go.mod b/go.mod index c568ae27..6e30bf41 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,11 @@ go 1.17 require ( github.com/go-logr/logr v0.1.0 github.com/go-test/deep v1.0.7 - github.com/onsi/ginkgo v1.12.1 + github.com/onsi/ginkgo v1.14.0 github.com/onsi/gomega v1.10.1 + github.com/sirupsen/logrus v1.8.1 + github.com/stretchr/testify v1.7.0 + go.k6.io/k6 v0.35.0 k8s.io/api v0.18.6 k8s.io/apimachinery v0.18.6 k8s.io/client-go v0.18.6 @@ -14,48 +17,66 @@ require ( ) require ( - cloud.google.com/go v0.38.0 // indirect + cloud.google.com/go v0.46.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect + github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06 // indirect github.com/evanphx/json-patch v4.5.0+incompatible // indirect + github.com/fatih/color v1.12.0 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/ghodss/yaml v1.0.0 // indirect github.com/go-logr/zapr v0.1.0 // indirect + github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/gogo/protobuf v1.3.1 // indirect github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect - github.com/golang/protobuf v1.4.2 // indirect - github.com/google/go-cmp v0.4.0 // indirect + github.com/golang/protobuf v1.4.3 // indirect + github.com/google/go-cmp v0.5.1 // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.1.1 // indirect + github.com/google/uuid v1.1.2 // indirect github.com/googleapis/gnostic v0.3.1 // indirect + github.com/gorilla/websocket v1.4.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/imdario/mergo v0.3.9 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.10 // indirect + github.com/kelseyhightower/envconfig v1.4.0 // indirect + github.com/kubernetes/helm v2.9.0+incompatible // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-isatty v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/nxadm/tail v1.4.4 // indirect + github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect github.com/pkg/errors v0.8.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.4.1 // indirect github.com/prometheus/procfs v0.0.11 // indirect + github.com/spf13/afero v1.2.2 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/atomic v1.4.0 // indirect go.uber.org/multierr v1.1.0 // indirect go.uber.org/zap v1.10.0 // indirect - golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 // indirect - golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 // indirect - golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect - golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect - golang.org/x/text v0.3.3 // indirect - golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect - golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect + golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e // indirect + golang.org/x/net v0.0.0-20211101194204-95aca89e93de // indirect + golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect + golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 // indirect + golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect + golang.org/x/text v0.3.7-0.20210503195748-5c7c50ebbd4f // indirect + golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gomodules.xyz/jsonpatch/v2 v2.0.1 // indirect - google.golang.org/appengine v1.5.0 // indirect - google.golang.org/protobuf v1.23.0 // indirect + google.golang.org/appengine v1.6.1 // indirect + google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12 // indirect + gopkg.in/guregu/null.v3 v3.3.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v2 v2.3.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect k8s.io/apiextensions-apiserver v0.18.6 // indirect k8s.io/klog v1.0.0 // indirect k8s.io/klog/v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 34a63f82..de6b77c5 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,17 @@ 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= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 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= @@ -10,18 +20,31 @@ github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxB 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/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= 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/DataDog/datadog-go v0.0.0-20180330214955-e67964b4021a/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= 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/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= 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/Soontao/goHttpDigestClient v0.0.0-20170320082612-6d28bb1415c5/go.mod h1:5Q4+CyR7+Q3VMG8f78ou+QSX/BNUNUx5W48eFRat8DQ= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= 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/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM= +github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 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/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -29,11 +52,19 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 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/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 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/etcd v3.3.13+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= @@ -42,37 +73,56 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 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/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/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E= +github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= 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/dop251/goja v0.0.0-20211022113120-dc8c55024d06 h1:XqC5eocqw7r3+HOhKYqaYH07XBiBDp9WE3NQK8XHSn4= +github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= 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-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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 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/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813/go.mod h1:P+oSoE9yhSRvsmYyZsshflcR6ePWYLql6UU1amW13IM= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 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= @@ -120,6 +170,8 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh 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-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= @@ -133,86 +185,152 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9 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.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 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.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/pprof v0.0.0-20190515194954-54271f7e092f/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/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/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/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= 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/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 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/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 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.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jhump/protoreflect v1.10.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= 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/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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/kubernetes/helm v2.9.0+incompatible h1:X6Tl40RMiqT0GD8hT3+jFPgkZV1EB6vYsoJDX8YkY+c= +github.com/kubernetes/helm v2.9.0+incompatible/go.mod h1:3Nb8I82ptmDi7OvvBQK25X1bwxg+WMAkusUQXHxu8ag= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/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/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/manyminds/api2go v0.0.0-20180125085803-95be7bd0455e/go.mod h1:Z60vy0EZVSu0bOugCHdcN5ZxFMKSpjRgsnh0XKPFqqk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 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/mccutchen/go-httpbin v1.1.2-0.20190116014521-c5cb2f4802fa h1:lx8ZnNPwjkXSzOROz0cg69RlErRXs+L3eDkggASWKLo= +github.com/mccutchen/go-httpbin v1.1.2-0.20190116014521-c5cb2f4802fa/go.mod h1:fhpOYavp5g2K74XDl/ao2y4KvhqVtKlkg1e+0UaQv7I= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 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= @@ -224,19 +342,26 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m 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/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= +github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +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.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 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.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 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= @@ -245,8 +370,10 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 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= @@ -254,23 +381,41 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: 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/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI= github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +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/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 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 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= 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 v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= 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= @@ -278,26 +423,41 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn 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.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 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/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/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= +github.com/yuin/goldmark v1.1.32/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.k6.io/k6 v0.35.0 h1:nvUSaYEngrh05OxcUiIsB0B+VQsg73OKBtvObHPBabs= +go.k6.io/k6 v0.35.0/go.mod h1:ges4SvRBSi9xMOGmIlv7l/7zwuAE+WiHN+9MlP4SxgE= 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.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -306,79 +466,134 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-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-20190605123033-f99c8df09eb5/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-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= +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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e h1:8foAy0aoO5GkqCvAEJ4VC4P3zksTg4X4aJCDpZzmgQI= +golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 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-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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-20180218175443-cbe0f9307d01/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-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-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-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= +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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211101194204-95aca89e93de h1:dKoXPECQZ51dGVSkuiD9YzeNpLT4UPUY4d3xo0sWrkU= +golang.org/x/net v0.0.0-20211101194204-95aca89e93de/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/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/sync v0.0.0-20200625203802-6e8e738ad208/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-20180823144017-11551d06cbcc/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-20181026203630-95b1ffbd15a5/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-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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 h1:yhBbb4IRs2HS9PPlAg6DMC6mUOKexJBNsLf4Z+6En1Q= +golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= +golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/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/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/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7-0.20210503195748-5c7c50ebbd4f h1:yQJrRE0hDxDFmZLlRaw+3vusO4fwNHgHIjUOMO7bHYI= +golang.org/x/text v0.3.7-0.20210503195748-5c7c50ebbd4f/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 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/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/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= @@ -387,45 +602,95 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/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-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 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/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 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-20190425155659-357c62f0e4bb/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-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200903010400-9bfcb5116336 h1:ZcAny/XH59BbzUOKydQpvIlklwibW3T9SvDE5cGhdzc= +google.golang.org/genproto v0.0.0-20200903010400-9bfcb5116336/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/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.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 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/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= 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.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +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/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12 h1:OwhZOOMuf7leLaSCuxtQ9FW7ui2L2L6UKOtKAUqovUQ= +google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/guregu/null.v2 v2.1.2/go.mod h1:XORrx8tyS5ZDcyUboCIxQtta/Aujk/6pfWrn9Xe33mU= +gopkg.in/guregu/null.v3 v3.3.0 h1:8j3ggqq+NgKt/O7mbFVUFKUMWN+l1AmT5jQmJ6nPh2c= +gopkg.in/guregu/null.v3 v3.3.0/go.mod h1:E4tX2Qe3h7QdL+uZ3a0vqvYwKQsRSQKM5V4YltdgH9Y= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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= @@ -434,14 +699,21 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD 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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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-20190418001031-e561f6794a2a/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/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.18.6 h1:osqrAXbOQjkKIWDTjrqxWQ3w0GkKb1KA1XkUGHHYpeE= k8s.io/api v0.18.6/go.mod h1:eeyxr+cwCjMdLAmr2W3RyDI0VvTawSg/3RFFBEnmZGI= k8s.io/apiextensions-apiserver v0.18.6 h1:vDlk7cyFsDyfwn2rNAO2DbmUbvXy5yT5GE3rrqOzaMo= @@ -466,6 +738,7 @@ k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 h1:v8ud2Up6QK1lNOKFgiIVrZdMg7MpmSnvtrOieolJKoE= k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= sigs.k8s.io/controller-runtime v0.6.2 h1:jkAnfdTYBpFwlmBn3pS5HFO06SfxvnTZ1p5PeEF/zAA= sigs.k8s.io/controller-runtime v0.6.2/go.mod h1:vhcq/rlnENJ09SIRp3EveTaZ0yqH526hjf9iJdbUJ/E= diff --git a/pkg/cloud/cloud.go b/pkg/cloud/cloud.go new file mode 100644 index 00000000..6e8965cd --- /dev/null +++ b/pkg/cloud/cloud.go @@ -0,0 +1,111 @@ +package cloud + +import ( + "fmt" + "os" + "time" + + "github.com/go-logr/logr" + "github.com/sirupsen/logrus" + "go.k6.io/k6/cloudapi" + "go.k6.io/k6/lib" + "go.k6.io/k6/lib/consts" + "go.k6.io/k6/lib/types" + "gopkg.in/guregu/null.v3" +) + +var client *cloudapi.Client + +type InspectOutput struct { + External struct { + Loadimpact struct { + Name string `json:"name"` + ProjectID int64 `json:"projectID"` + } `json:"loadimpact"` + } `json:"ext"` + TotalDuration types.NullDuration `json:"totalDuration"` + MaxVUs uint64 `json:"maxVUs"` + Thresholds map[string][]string `json:"thresholds,omitempty"` +} + +type TestRun struct { + Name string `json:"name"` + ProjectID int64 `json:"project_id,omitempty"` + VUsMax int64 `json:"vus"` + Thresholds map[string][]string `json:"thresholds"` + Duration int64 `json:"duration"` + ProcessThresholds bool `json:"process_thresholds"` + Instances int32 `json:"instances"` +} + +func CreateTestRun(opts InspectOutput, instances int32, host, token string, log logr.Logger) (string, error) { + if len(opts.External.Loadimpact.Name) < 1 { + opts.External.Loadimpact.Name = "k6-operator-test" + } + + cloudConfig := cloudapi.NewConfig() + + if opts.External.Loadimpact.ProjectID > 0 { + cloudConfig.ProjectID = null.NewInt(opts.External.Loadimpact.ProjectID, true) + } + + logger := &logrus.Logger{ + Out: os.Stdout, + Formatter: new(logrus.TextFormatter), + Hooks: make(logrus.LevelHooks), + Level: logrus.InfoLevel, + } + + if opts.Thresholds == nil { + opts.Thresholds = make(map[string][]string) + } + + if len(host) == 0 { + host = cloudConfig.Host.String + } + + client = cloudapi.NewClient(logger, token, host, consts.Version, time.Duration(time.Minute)) + resp, err := createTestRun(client, host, &TestRun{ + Name: opts.External.Loadimpact.Name, + ProjectID: cloudConfig.ProjectID.Int64, + VUsMax: int64(opts.MaxVUs), + Thresholds: opts.Thresholds, + Duration: int64(opts.TotalDuration.TimeDuration().Seconds()), + ProcessThresholds: true, + Instances: instances, + }) + + if err != nil { + return "", err + } + + return resp.ReferenceID, nil +} + +// We cannot use cloudapi.TestRun struct and cloudapi.Client.CreateTestRun call because they're not aware of +// process_thresholds argument; so let's use custom struct and function instead +func createTestRun(client *cloudapi.Client, host string, testRun *TestRun) (*cloudapi.CreateTestRunResponse, error) { + url := host + "/v1/tests" + req, err := client.NewRequest("POST", url, testRun) + if err != nil { + return nil, err + } + + ctrr := cloudapi.CreateTestRunResponse{} + err = client.Do(req, &ctrr) + if err != nil { + return nil, err + } + + if ctrr.ReferenceID == "" { + return nil, fmt.Errorf("failed to get a reference ID") + } + + return &ctrr, nil +} + +func FinishTestRun(refID string) error { + return client.TestFinished(refID, cloudapi.ThresholdResult( + map[string]map[string]bool{}, + ), false, lib.RunStatusFinished) +} diff --git a/pkg/resources/containers/curl.go b/pkg/resources/containers/curl.go index d8cdd157..f8e8bd4c 100644 --- a/pkg/resources/containers/curl.go +++ b/pkg/resources/containers/curl.go @@ -27,6 +27,7 @@ func NewCurlContainer(hostnames []string, image string, command []string, env [] for _, hostname := range hostnames { parts = append(parts, fmt.Sprintf("curl --retry 3 -X PATCH -H 'Content-Type: application/json' http://%s:6565/v1/status -d '%s'", hostname, req)) } + return corev1.Container{ Name: "k6-curl", Image: image, diff --git a/pkg/resources/jobs/initializer.go b/pkg/resources/jobs/initializer.go new file mode 100644 index 00000000..3175bdcf --- /dev/null +++ b/pkg/resources/jobs/initializer.go @@ -0,0 +1,108 @@ +package jobs + +import ( + "fmt" + "strconv" + + "github.com/grafana/k6-operator/api/v1alpha1" + "github.com/grafana/k6-operator/pkg/types" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// NewInitializerJob builds a template used to initializefor creating a starter job +func NewInitializerJob(k6 *v1alpha1.K6, argLine string) (*batchv1.Job, error) { + script, err := types.ParseScript(&k6.Spec) + if err != nil { + return nil, err + } + + var ( + image = "ghcr.io/grafana/operator:latest-runner" + annotations = make(map[string]string) + labels = newLabels(k6.Name) + serviceAccountName = "default" + automountServiceAccountToken = true + ports = append([]corev1.ContainerPort{{ContainerPort: 6565}}, k6.Spec.Ports...) + ) + + if k6.Spec.Runner.Image != "" { + image = k6.Spec.Runner.Image + } + + if k6.Spec.Runner.Metadata.Annotations != nil { + annotations = k6.Spec.Runner.Metadata.Annotations + } + + if k6.Spec.Runner.Metadata.Labels != nil { + for k, v := range k6.Spec.Runner.Metadata.Labels { // Order not specified + if _, ok := labels[k]; !ok { + labels[k] = v + } + } + } + + if k6.Spec.Runner.ServiceAccountName != "" { + serviceAccountName = k6.Spec.Runner.ServiceAccountName + } + + if k6.Spec.Runner.AutomountServiceAccountToken != "" { + automountServiceAccountToken, _ = strconv.ParseBool(k6.Spec.Runner.AutomountServiceAccountToken) + } + + var ( + // k6 allows to run archive command on archives too so type of file here doesn't matter + scriptName = script.FullName() + archiveName = fmt.Sprintf("./%s.archived.tar", script.Filename) + ) + command, istioEnabled := newIstioCommand(k6.Spec.Scuttle.Enabled, []string{"sh", "-c"}) + command = append(command, fmt.Sprintf("k6 archive %s -O %s %s && k6 inspect --execution-requirements %s", + scriptName, archiveName, argLine, + archiveName)) + + env := append(newIstioEnvVar(k6.Spec.Scuttle, istioEnabled), k6.Spec.Runner.Env...) + + return &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-initializer", k6.Name), + Namespace: k6.Namespace, + Labels: labels, + Annotations: annotations, + }, + Spec: batchv1.JobSpec{ + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: annotations, + }, + Spec: corev1.PodSpec{ + AutomountServiceAccountToken: &automountServiceAccountToken, + ServiceAccountName: serviceAccountName, + Affinity: k6.Spec.Runner.Affinity, + NodeSelector: k6.Spec.Runner.NodeSelector, + RestartPolicy: corev1.RestartPolicyNever, + Containers: []corev1.Container{ + { + Image: image, + Name: "k6", + Command: command, + Env: env, + Resources: k6.Spec.Runner.Resources, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "k6-test-volume", + MountPath: "/test", + }, + }, + Ports: ports, + }, + }, + Volumes: []corev1.Volume{ + script.Volume(), + }, + }, + }, + }, + }, nil +} diff --git a/pkg/resources/jobs/initializer_test.go b/pkg/resources/jobs/initializer_test.go new file mode 100644 index 00000000..5a3fdc80 --- /dev/null +++ b/pkg/resources/jobs/initializer_test.go @@ -0,0 +1,113 @@ +package jobs + +import ( + "testing" + + deep "github.com/go-test/deep" + "github.com/grafana/k6-operator/api/v1alpha1" + "github.com/grafana/k6-operator/pkg/types" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestNewInitializerJob(t *testing.T) { + script := &types.Script{ + Name: "test", + Filename: "test.js", + Type: "ConfigMap", + } + + automountServiceAccountToken := true + + expectedOutcome := &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-initializer", + Namespace: "test", + Labels: map[string]string{ + "app": "k6", + "k6_cr": "test", + "label1": "awesome", + }, + Annotations: map[string]string{ + "awesomeAnnotation": "dope", + }, + }, + Spec: batchv1.JobSpec{ + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "k6", + "k6_cr": "test", + "label1": "awesome", + }, + Annotations: map[string]string{ + "awesomeAnnotation": "dope", + }, + }, + Spec: corev1.PodSpec{ + AutomountServiceAccountToken: &automountServiceAccountToken, + ServiceAccountName: "default", + Affinity: nil, + NodeSelector: nil, + RestartPolicy: corev1.RestartPolicyNever, + Containers: []corev1.Container{ + { + Image: "ghcr.io/grafana/operator:latest-runner", + Name: "k6", + Command: []string{ + "sh", "-c", + "k6 archive /test/test.js -O ./test.js.archived.tar --out cloud && k6 inspect --execution-requirements ./test.js.archived.tar", + }, + Env: []corev1.EnvVar{}, + Resources: corev1.ResourceRequirements{}, + VolumeMounts: []corev1.VolumeMount{{ + Name: "k6-test-volume", + MountPath: "/test", + }}, + Ports: []corev1.ContainerPort{{ContainerPort: 6565}}, + }, + }, + Volumes: []corev1.Volume{ + script.Volume(), + }, + }, + }, + }, + } + + k6 := &v1alpha1.K6{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: v1alpha1.K6Spec{ + Script: v1alpha1.K6Script{ + ConfigMap: v1alpha1.K6Configmap{ + Name: "test", + File: "test.js", + }, + }, + Arguments: "--out cloud", + Runner: v1alpha1.Pod{ + Metadata: v1alpha1.PodMetadata{ + Labels: map[string]string{ + "label1": "awesome", + }, + Annotations: map[string]string{ + "awesomeAnnotation": "dope", + }, + }, + }, + }, + } + + job, err := NewInitializerJob(k6, "--out cloud") + if err != nil { + t.Errorf("NewInitializerJob errored, got: %v", err) + } + + if diff := deep.Equal(job, expectedOutcome); diff != nil { + t.Error(diff) + } +} diff --git a/pkg/resources/jobs/runner.go b/pkg/resources/jobs/runner.go index c2da07c3..f6f57199 100644 --- a/pkg/resources/jobs/runner.go +++ b/pkg/resources/jobs/runner.go @@ -1,7 +1,6 @@ package jobs import ( - "errors" "fmt" "strconv" @@ -9,20 +8,14 @@ import ( "github.com/grafana/k6-operator/api/v1alpha1" "github.com/grafana/k6-operator/pkg/segmentation" + "github.com/grafana/k6-operator/pkg/types" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// Internal script type created from Spec.script possible options -type Script struct { - Name string - File string - Type string -} - // NewRunnerJob creates a new k6 job from a CRD -func NewRunnerJob(k6 *v1alpha1.K6, index int) (*batchv1.Job, error) { +func NewRunnerJob(k6 *v1alpha1.K6, index int, testRunId, token string) (*batchv1.Job, error) { name := fmt.Sprintf("%s-%d", k6.Name, index) postCommand := []string{"k6", "run"} @@ -48,8 +41,7 @@ func NewRunnerJob(k6 *v1alpha1.K6, index int) (*batchv1.Job, error) { command = append(command, args...) } - script, err := newScript(k6.Spec) - + script, err := types.ParseScript(&k6.Spec) if err != nil { return nil, err } @@ -61,7 +53,7 @@ func NewRunnerJob(k6 *v1alpha1.K6, index int) (*batchv1.Job, error) { command = append( command, - fmt.Sprintf(script.File), + fmt.Sprintf(script.FullName()), "--address=0.0.0.0:6565") paused := true @@ -73,7 +65,12 @@ func NewRunnerJob(k6 *v1alpha1.K6, index int) (*batchv1.Job, error) { command = append(command, "--paused") } - command = appendFileCheckerCommand(script, command) + // this is a cloud output run + if len(testRunId) > 0 { + command = append(command, "--tag", fmt.Sprintf("instance_id=%d", index)) + } + + command = script.UpdateCommand(command) var ( zero int64 = 0 @@ -91,6 +88,7 @@ func NewRunnerJob(k6 *v1alpha1.K6, index int) (*batchv1.Job, error) { } runnerLabels := newLabels(k6.Name) + runnerLabels["runner"] = "true" if k6.Spec.Runner.Metadata.Labels != nil { for k, v := range k6.Spec.Runner.Metadata.Labels { // Order not specified if _, ok := runnerLabels[k]; !ok { @@ -113,6 +111,18 @@ func NewRunnerJob(k6 *v1alpha1.K6, index int) (*batchv1.Job, error) { ports = append(ports, k6.Spec.Ports...) env := newIstioEnvVar(k6.Spec.Scuttle, istioEnabled) + + // this is a cloud output run + if len(testRunId) > 0 { + env = append(env, corev1.EnvVar{ + Name: "K6_CLOUD_PUSH_REF_ID", + Value: testRunId, + }, corev1.EnvVar{ + Name: "K6_CLOUD_TOKEN", + Value: token, + }) + } + env = append(env, k6.Spec.Runner.Env...) job := &batchv1.Job{ @@ -138,16 +148,20 @@ func NewRunnerJob(k6 *v1alpha1.K6, index int) (*batchv1.Job, error) { NodeSelector: k6.Spec.Runner.NodeSelector, SecurityContext: &k6.Spec.Runner.SecurityContext, Containers: []corev1.Container{{ - Image: image, - Name: "k6", - Command: command, - Env: env, - Resources: k6.Spec.Runner.Resources, - VolumeMounts: newVolumeMountSpec(script), - Ports: ports, + Image: image, + Name: "k6", + Command: command, + Env: env, + Resources: k6.Spec.Runner.Resources, + VolumeMounts: []corev1.VolumeMount{ + script.VolumeMount(), + }, + Ports: ports, }}, TerminationGracePeriodSeconds: &zero, - Volumes: newVolumeSpec(script), + Volumes: []corev1.Volume{ + script.Volume(), + }, }, }, }, @@ -169,6 +183,7 @@ func NewRunnerService(k6 *v1alpha1.K6, index int) (*corev1.Service, error) { } runnerLabels := newLabels(k6.Name) + runnerLabels["runner"] = "true" if k6.Spec.Runner.Metadata.Labels != nil { for k, v := range k6.Spec.Runner.Metadata.Labels { // Order not specified if _, ok := runnerLabels[k]; !ok { @@ -223,85 +238,3 @@ func newAntiAffinity() *corev1.Affinity { }, } } - -func newVolumeMountSpec(s *Script) []corev1.VolumeMount { - if s.Type == "LocalFile" { - return []corev1.VolumeMount{} - } - return []corev1.VolumeMount{{ - Name: "k6-test-volume", - MountPath: "/test", - }} -} - -func newVolumeSpec(s *Script) []corev1.Volume { - switch s.Type { - case "VolumeClaim": - return []corev1.Volume{{ - Name: "k6-test-volume", - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: s.Name, - }, - }, - }} - case "ConfigMap": - return []corev1.Volume{{ - Name: "k6-test-volume", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: s.Name, - }, - }, - }, - }} - default: - return []corev1.Volume{} - } -} - -func newScript(spec v1alpha1.K6Spec) (*Script, error) { - s := &Script{} - s.File = "test.js" - - if spec.Script.VolumeClaim.Name != "" { - s.Name = spec.Script.VolumeClaim.Name - if spec.Script.VolumeClaim.File != "" { - s.File = spec.Script.VolumeClaim.File - } - - s.File = fmt.Sprintf("/test/%s", s.File) - s.Type = "VolumeClaim" - return s, nil - } - - if spec.Script.ConfigMap.Name != "" { - s.Name = spec.Script.ConfigMap.Name - - if spec.Script.ConfigMap.File != "" { - s.File = spec.Script.ConfigMap.File - } - s.File = fmt.Sprintf("/test/%s", s.File) - s.Type = "ConfigMap" - return s, nil - } - - if spec.Script.LocalFile != "" { - s.Name = "LocalFile" - s.File = spec.Script.LocalFile - s.Type = "LocalFile" - return s, nil - } - - return nil, errors.New("ConfigMap, VolumeClaim or LocalFile not provided in script definition") -} - -func appendFileCheckerCommand(s *Script, cmd []string) []string { - if s.Type == "LocalFile" { - joincmd := strings.Join(cmd, " ") - checkCommand := []string{"sh", "-c", fmt.Sprintf("if [ ! -f %v ]; then echo \"LocalFile not found exiting...\"; exit 1; fi;\n%v", s.File, joincmd)} - return checkCommand - } - return cmd -} diff --git a/pkg/resources/jobs/runner_test.go b/pkg/resources/jobs/runner_test.go index 6e9aeec4..b532823c 100644 --- a/pkg/resources/jobs/runner_test.go +++ b/pkg/resources/jobs/runner_test.go @@ -7,17 +7,18 @@ import ( deep "github.com/go-test/deep" "github.com/grafana/k6-operator/api/v1alpha1" + "github.com/grafana/k6-operator/pkg/types" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestNewScriptVolumeClaim(t *testing.T) { - - expectedOutcome := &Script{ - Name: "Test", - File: "/test/thing.js", - Type: "VolumeClaim", + expectedOutcome := &types.Script{ + Name: "Test", + Path: "/test/", + Filename: "thing.js", + Type: "VolumeClaim", } k6 := v1alpha1.K6Spec{ @@ -29,9 +30,9 @@ func TestNewScriptVolumeClaim(t *testing.T) { }, } - script, err := newScript(k6) + script, err := types.ParseScript(&k6) if err != nil { - t.Errorf("NewScript with Volume Claim errored, got: %v, want: %v", err, expectedOutcome) + t.Errorf("NewScript with ConfigMap errored, got: %v, want: %v", err, expectedOutcome) } if !reflect.DeepEqual(script, expectedOutcome) { t.Errorf("NewScript with VolumeClaim failed to return expected output, got: %v, expected: %v", script, expectedOutcome) @@ -39,11 +40,11 @@ func TestNewScriptVolumeClaim(t *testing.T) { } func TestNewScriptConfigMap(t *testing.T) { - - expectedOutcome := &Script{ - Name: "Test", - File: "/test/thing.js", - Type: "ConfigMap", + expectedOutcome := &types.Script{ + Name: "Test", + Path: "/test/", + Filename: "thing.js", + Type: "ConfigMap", } k6 := v1alpha1.K6Spec{ @@ -55,7 +56,7 @@ func TestNewScriptConfigMap(t *testing.T) { }, } - script, err := newScript(k6) + script, err := types.ParseScript(&k6) if err != nil { t.Errorf("NewScript with ConfigMap errored, got: %v, want: %v", err, expectedOutcome) } @@ -66,10 +67,11 @@ func TestNewScriptConfigMap(t *testing.T) { func TestNewScriptLocalFile(t *testing.T) { - expectedOutcome := &Script{ - Name: "LocalFile", - File: "/custom/my_test.js", - Type: "LocalFile", + expectedOutcome := &types.Script{ + Name: "LocalFile", + Path: "/custom/", + Filename: "my_test.js", + Type: "LocalFile", } k6 := v1alpha1.K6Spec{ @@ -78,7 +80,7 @@ func TestNewScriptLocalFile(t *testing.T) { }, } - script, err := newScript(k6) + script, err := types.ParseScript(&k6) if err != nil { t.Errorf("NewScript with LocalFile errored, got: %v, want: %v", err, expectedOutcome) } @@ -88,39 +90,37 @@ func TestNewScriptLocalFile(t *testing.T) { } func TestNewScriptNoScript(t *testing.T) { - k6 := v1alpha1.K6Spec{} - script, err := newScript(k6) + script, err := types.ParseScript(&k6) if err == nil && script != nil { t.Errorf("Expected Error from NewScript, got: %v, want: %v", err, errors.New("configMap, VolumeClaim or LocalFile not provided in script definition")) } } func TestNewVolumeSpecVolumeClaim(t *testing.T) { - - expectedOutcome := []corev1.Volume{{ + expectedOutcome := corev1.Volume{ Name: "k6-test-volume", VolumeSource: corev1.VolumeSource{ PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: "test", }, }, - }} + } - k6 := &Script{ + script := &types.Script{ Type: "VolumeClaim", Name: "test", } - volumeSpec := newVolumeSpec(k6) + volumeSpec := script.Volume() if !reflect.DeepEqual(volumeSpec, expectedOutcome) { t.Errorf("VolumeSpec wasn't as expected, got: %v, expected: %v", volumeSpec, expectedOutcome) } } func TestNewVolumeSpecConfigMap(t *testing.T) { - expectedOutcome := []corev1.Volume{{ + expectedOutcome := corev1.Volume{ Name: "k6-test-volume", VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ @@ -129,27 +129,27 @@ func TestNewVolumeSpecConfigMap(t *testing.T) { }, }, }, - }} + } - k6 := &Script{ + script := &types.Script{ Type: "ConfigMap", Name: "test", } - volumeSpec := newVolumeSpec(k6) + volumeSpec := script.Volume() if !reflect.DeepEqual(volumeSpec, expectedOutcome) { t.Errorf("VolumeSpec wasn't as expected, got: %v, expected: %v", volumeSpec, expectedOutcome) } } func TestNewVolumeSpecNoType(t *testing.T) { - expectedOutcome := []corev1.Volume{} + expectedOutcome := corev1.Volume{} - k6 := &Script{ + script := &types.Script{ Name: "test", } - volumeSpec := newVolumeSpec(k6) + volumeSpec := script.Volume() if !reflect.DeepEqual(volumeSpec, expectedOutcome) { t.Errorf("VolumeSpec wasn't as expected, got: %v, expected: %v", volumeSpec, expectedOutcome) } @@ -193,6 +193,7 @@ func TestNewRunnerService(t *testing.T) { Labels: map[string]string{ "app": "k6", "k6_cr": "test", + "runner": "true", "label1": "awesome", }, Annotations: map[string]string{ @@ -239,24 +240,27 @@ func TestNewRunnerService(t *testing.T) { } func TestNewRunnerJob(t *testing.T) { - script := &Script{ - Name: "test", - File: "thing.js", - Type: "ConfigMap", + script := &types.Script{ + Name: "test", + Filename: "thing.js", + Type: "ConfigMap", } var zero int64 = 0 automountServiceAccountToken := true + expectedLabels := map[string]string{ + "app": "k6", + "k6_cr": "test", + "runner": "true", + "label1": "awesome", + } + expectedOutcome := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: "test-1", Namespace: "test", - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -265,11 +269,7 @@ func TestNewRunnerJob(t *testing.T) { BackoffLimit: new(int32), Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -295,7 +295,9 @@ func TestNewRunnerJob(t *testing.T) { Ports: []corev1.ContainerPort{{ContainerPort: 6565}}, }}, TerminationGracePeriodSeconds: &zero, - Volumes: newVolumeSpec(script), + Volumes: []corev1.Volume{ + script.Volume(), + }, }, }, }, @@ -325,7 +327,7 @@ func TestNewRunnerJob(t *testing.T) { }, } - job, err := NewRunnerJob(k6, 1) + job, err := NewRunnerJob(k6, 1, "", "") if err != nil { t.Errorf("NewRunnerJob errored, got: %v", err) } @@ -335,24 +337,27 @@ func TestNewRunnerJob(t *testing.T) { } func TestNewRunnerJobNoisy(t *testing.T) { - script := &Script{ - Name: "test", - File: "thing.js", - Type: "ConfigMap", + script := &types.Script{ + Name: "test", + Filename: "thing.js", + Type: "ConfigMap", } var zero int64 = 0 automountServiceAccountToken := true + expectedLabels := map[string]string{ + "app": "k6", + "k6_cr": "test", + "runner": "true", + "label1": "awesome", + } + expectedOutcome := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: "test-1", Namespace: "test", - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -361,11 +366,7 @@ func TestNewRunnerJobNoisy(t *testing.T) { BackoffLimit: new(int32), Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -391,7 +392,9 @@ func TestNewRunnerJobNoisy(t *testing.T) { Ports: []corev1.ContainerPort{{ContainerPort: 6565}}, }}, TerminationGracePeriodSeconds: &zero, - Volumes: newVolumeSpec(script), + Volumes: []corev1.Volume{ + script.Volume(), + }, }, }, }, @@ -422,7 +425,7 @@ func TestNewRunnerJobNoisy(t *testing.T) { }, } - job, err := NewRunnerJob(k6, 1) + job, err := NewRunnerJob(k6, 1, "", "") if err != nil { t.Errorf("NewRunnerJob errored, got: %v", err) } @@ -432,24 +435,27 @@ func TestNewRunnerJobNoisy(t *testing.T) { } func TestNewRunnerJobUnpaused(t *testing.T) { - script := &Script{ - Name: "test", - File: "thing.js", - Type: "ConfigMap", + script := &types.Script{ + Name: "test", + Filename: "thing.js", + Type: "ConfigMap", } var zero int64 = 0 automountServiceAccountToken := true + expectedLabels := map[string]string{ + "app": "k6", + "k6_cr": "test", + "runner": "true", + "label1": "awesome", + } + expectedOutcome := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: "test-1", Namespace: "test", - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -458,11 +464,7 @@ func TestNewRunnerJobUnpaused(t *testing.T) { BackoffLimit: new(int32), Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -488,7 +490,9 @@ func TestNewRunnerJobUnpaused(t *testing.T) { Ports: []corev1.ContainerPort{{ContainerPort: 6565}}, }}, TerminationGracePeriodSeconds: &zero, - Volumes: newVolumeSpec(script), + Volumes: []corev1.Volume{ + script.Volume(), + }, }, }, }, @@ -519,7 +523,7 @@ func TestNewRunnerJobUnpaused(t *testing.T) { }, } - job, err := NewRunnerJob(k6, 1) + job, err := NewRunnerJob(k6, 1, "", "") if err != nil { t.Errorf("NewRunnerJob errored, got: %v", err) } @@ -529,24 +533,27 @@ func TestNewRunnerJobUnpaused(t *testing.T) { } func TestNewRunnerJobArguments(t *testing.T) { - script := &Script{ - Name: "test", - File: "thing.js", - Type: "ConfigMap", + script := &types.Script{ + Name: "test", + Filename: "thing.js", + Type: "ConfigMap", } var zero int64 = 0 automountServiceAccountToken := true + expectedLabels := map[string]string{ + "app": "k6", + "k6_cr": "test", + "runner": "true", + "label1": "awesome", + } + expectedOutcome := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: "test-1", Namespace: "test", - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -555,11 +562,7 @@ func TestNewRunnerJobArguments(t *testing.T) { BackoffLimit: new(int32), Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -585,7 +588,9 @@ func TestNewRunnerJobArguments(t *testing.T) { Ports: []corev1.ContainerPort{{ContainerPort: 6565}}, }}, TerminationGracePeriodSeconds: &zero, - Volumes: newVolumeSpec(script), + Volumes: []corev1.Volume{ + script.Volume(), + }, }, }, }, @@ -617,7 +622,7 @@ func TestNewRunnerJobArguments(t *testing.T) { }, } - job, err := NewRunnerJob(k6, 1) + job, err := NewRunnerJob(k6, 1, "", "") if err != nil { t.Errorf("NewRunnerJob errored, got: %v", err) } @@ -627,24 +632,27 @@ func TestNewRunnerJobArguments(t *testing.T) { } func TestNewRunnerJobServiceAccount(t *testing.T) { - script := &Script{ - Name: "test", - File: "thing.js", - Type: "ConfigMap", + script := &types.Script{ + Name: "test", + Filename: "thing.js", + Type: "ConfigMap", } var zero int64 = 0 automountServiceAccountToken := true + expectedLabels := map[string]string{ + "app": "k6", + "k6_cr": "test", + "runner": "true", + "label1": "awesome", + } + expectedOutcome := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: "test-1", Namespace: "test", - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -653,11 +661,7 @@ func TestNewRunnerJobServiceAccount(t *testing.T) { BackoffLimit: new(int32), Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -683,7 +687,9 @@ func TestNewRunnerJobServiceAccount(t *testing.T) { Ports: []corev1.ContainerPort{{ContainerPort: 6565}}, }}, TerminationGracePeriodSeconds: &zero, - Volumes: newVolumeSpec(script), + Volumes: []corev1.Volume{ + script.Volume(), + }, }, }, }, @@ -716,7 +722,7 @@ func TestNewRunnerJobServiceAccount(t *testing.T) { }, } - job, err := NewRunnerJob(k6, 1) + job, err := NewRunnerJob(k6, 1, "", "") if err != nil { t.Errorf("NewRunnerJob errored, got: %v", err) } @@ -726,24 +732,27 @@ func TestNewRunnerJobServiceAccount(t *testing.T) { } func TestNewRunnerJobIstio(t *testing.T) { - script := &Script{ - Name: "test", - File: "thing.js", - Type: "ConfigMap", + script := &types.Script{ + Name: "test", + Filename: "thing.js", + Type: "ConfigMap", } var zero int64 = 0 automountServiceAccountToken := true + expectedLabels := map[string]string{ + "app": "k6", + "k6_cr": "test", + "runner": "true", + "label1": "awesome", + } + expectedOutcome := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: "test-1", Namespace: "test", - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -752,11 +761,7 @@ func TestNewRunnerJobIstio(t *testing.T) { BackoffLimit: new(int32), Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -795,7 +800,9 @@ func TestNewRunnerJobIstio(t *testing.T) { Ports: []corev1.ContainerPort{{ContainerPort: 6565}}, }}, TerminationGracePeriodSeconds: &zero, - Volumes: newVolumeSpec(script), + Volumes: []corev1.Volume{ + script.Volume(), + }, }, }, }, @@ -828,7 +835,7 @@ func TestNewRunnerJobIstio(t *testing.T) { }, } - job, err := NewRunnerJob(k6, 1) + job, err := NewRunnerJob(k6, 1, "", "") if err != nil { t.Errorf("NewRunnerJob errored, got: %v", err) } @@ -837,25 +844,27 @@ func TestNewRunnerJobIstio(t *testing.T) { } } -func TestNewRunnerJobLocalFile(t *testing.T) { - script := &Script{ - Name: "test", - File: "/test/test.js", - Type: "LocalFile", +func TestNewRunnerJobCloud(t *testing.T) { + script := &types.Script{ + Name: "test", + Filename: "thing.js", + Type: "ConfigMap", } var zero int64 = 0 automountServiceAccountToken := true + expectedLabels := map[string]string{ + "app": "k6", + "k6_cr": "test", + "runner": "true", + } + expectedOutcome := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: "test-1", Namespace: "test", - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", - }, + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -864,11 +873,110 @@ func TestNewRunnerJobLocalFile(t *testing.T) { BackoffLimit: new(int32), Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app": "k6", - "k6_cr": "test", - "label1": "awesome", + Labels: expectedLabels, + Annotations: map[string]string{ + "awesomeAnnotation": "dope", }, + }, + Spec: corev1.PodSpec{ + Hostname: "test-1", + RestartPolicy: corev1.RestartPolicyNever, + Affinity: nil, + NodeSelector: nil, + ServiceAccountName: "default", + SecurityContext: &corev1.PodSecurityContext{}, + AutomountServiceAccountToken: &automountServiceAccountToken, + Containers: []corev1.Container{{ + Image: "ghcr.io/grafana/operator:latest-runner", + Name: "k6", + Command: []string{"k6", "run", "--quiet", "--out", "cloud", "/test/test.js", "--address=0.0.0.0:6565", "--paused", "--tag", "instance_id=1"}, + Env: []corev1.EnvVar{ + { + Name: "K6_CLOUD_PUSH_REF_ID", + Value: "testrunid", + }, + { + Name: "K6_CLOUD_TOKEN", + Value: "token", + }, + }, + Resources: corev1.ResourceRequirements{}, + VolumeMounts: []corev1.VolumeMount{{ + Name: "k6-test-volume", + MountPath: "/test", + }}, + Ports: []corev1.ContainerPort{{ContainerPort: 6565}}, + }}, + TerminationGracePeriodSeconds: &zero, + Volumes: []corev1.Volume{ + script.Volume(), + }, + }, + }, + }, + } + k6 := &v1alpha1.K6{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: v1alpha1.K6Spec{ + Script: v1alpha1.K6Script{ + ConfigMap: v1alpha1.K6Configmap{ + Name: "test", + File: "test.js", + }, + }, + Arguments: "--out cloud", + Runner: v1alpha1.Pod{ + Metadata: v1alpha1.PodMetadata{ + Annotations: map[string]string{ + "awesomeAnnotation": "dope", + }, + }, + }, + }, + } + + job, err := NewRunnerJob(k6, 1, "testrunid", "token") + if err != nil { + t.Errorf("NewRunnerJob errored, got: %v", err) + } + if diff := deep.Equal(job, expectedOutcome); diff != nil { + t.Errorf("NewRunnerJob returned unexpected data, diff: %s", diff) + } +} + +func TestNewRunnerJobLocalFile(t *testing.T) { + script := &types.Script{ + Name: "test", + Filename: "/test/test.js", + Type: "LocalFile", + } + + var zero int64 = 0 + automountServiceAccountToken := true + + expectedLabels := map[string]string{ + "app": "k6", + "k6_cr": "test", + "runner": "true", + "label1": "awesome", + } + expectedOutcome := &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-1", + Namespace: "test", + Labels: expectedLabels, + Annotations: map[string]string{ + "awesomeAnnotation": "dope", + }, + }, + Spec: batchv1.JobSpec{ + BackoffLimit: new(int32), + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: expectedLabels, Annotations: map[string]string{ "awesomeAnnotation": "dope", }, @@ -882,16 +990,20 @@ func TestNewRunnerJobLocalFile(t *testing.T) { AutomountServiceAccountToken: &automountServiceAccountToken, SecurityContext: &corev1.PodSecurityContext{}, Containers: []corev1.Container{{ - Image: "ghcr.io/grafana/operator:latest-runner", - Name: "k6", - Command: []string{"sh", "-c", "if [ ! -f /test/test.js ]; then echo \"LocalFile not found exiting...\"; exit 1; fi;\nk6 run --quiet /test/test.js --address=0.0.0.0:6565 --paused"}, - Env: []corev1.EnvVar{}, - Resources: corev1.ResourceRequirements{}, - VolumeMounts: []corev1.VolumeMount{}, - Ports: []corev1.ContainerPort{{ContainerPort: 6565}}, + Image: "ghcr.io/grafana/operator:latest-runner", + Name: "k6", + Command: []string{"sh", "-c", "if [ ! -f /test/test.js ]; then echo \"LocalFile not found exiting...\"; exit 1; fi;\nk6 run --quiet /test/test.js --address=0.0.0.0:6565 --paused"}, + Env: []corev1.EnvVar{}, + Resources: corev1.ResourceRequirements{}, + VolumeMounts: []corev1.VolumeMount{ + script.VolumeMount(), + }, + Ports: []corev1.ContainerPort{{ContainerPort: 6565}}, }}, TerminationGracePeriodSeconds: &zero, - Volumes: newVolumeSpec(script), + Volumes: []corev1.Volume{ + script.Volume(), + }, }, }, }, @@ -921,7 +1033,7 @@ func TestNewRunnerJobLocalFile(t *testing.T) { }, } - job, err := NewRunnerJob(k6, 1) + job, err := NewRunnerJob(k6, 1, "", "") if err != nil { t.Errorf("NewRunnerJob errored, got: %v", err) } diff --git a/pkg/types/types.go b/pkg/types/types.go new file mode 100644 index 00000000..6744776b --- /dev/null +++ b/pkg/types/types.go @@ -0,0 +1,176 @@ +package types + +import ( + "errors" + "fmt" + "path/filepath" + "strings" + + "github.com/grafana/k6-operator/api/v1alpha1" + corev1 "k8s.io/api/core/v1" +) + +// Internal type created to support Spec.script options +type Script struct { + Name string // name of ConfigMap or VolumeClaim or "LocalFile" + Filename string + Path string + Type string // ConfigMap | VolumeClaim | LocalFile +} + +// ParseScript extracts Script data bits from K6 spec and performs basic validation +func ParseScript(spec *v1alpha1.K6Spec) (*Script, error) { + s := &Script{ + Filename: "test.js", + Path: "/test/", + } + + if spec.Script.VolumeClaim.Name != "" { + s.Name = spec.Script.VolumeClaim.Name + if spec.Script.VolumeClaim.File != "" { + s.Filename = spec.Script.VolumeClaim.File + } + + s.Type = "VolumeClaim" + return s, nil + } + + if spec.Script.ConfigMap.Name != "" { + s.Name = spec.Script.ConfigMap.Name + + if spec.Script.ConfigMap.File != "" { + s.Filename = spec.Script.ConfigMap.File + } + + s.Type = "ConfigMap" + return s, nil + } + + if spec.Script.LocalFile != "" { + s.Name = "LocalFile" + s.Type = "LocalFile" + s.Path, s.Filename = filepath.Split(spec.Script.LocalFile) + return s, nil + } + + return nil, errors.New("Script definition should contain one of: ConfigMap, VolumeClaim, LocalFile") +} + +func (s *Script) FullName() string { + return s.Path + s.Filename +} + +// Volume creates a Volume spec for the script +func (s *Script) Volume() corev1.Volume { + switch s.Type { + case "VolumeClaim": + return corev1.Volume{ + Name: "k6-test-volume", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: s.Name, + }, + }, + } + + case "ConfigMap": + return corev1.Volume{ + Name: "k6-test-volume", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: s.Name, + }, + }, + }, + } + default: + return corev1.Volume{} + } +} + +// VolumeMount creates a VolumeMount spec for the script +func (s *Script) VolumeMount() corev1.VolumeMount { + if s.Type == "LocalFile" { + return corev1.VolumeMount{} + } + return corev1.VolumeMount{ + Name: "k6-test-volume", + MountPath: "/test", + } +} + +// UpdateCommand modifies command to check for script existence in case of LocalFile; +// otherwise, command remains unmodified +func (s *Script) UpdateCommand(cmd []string) []string { + if s.Type == "LocalFile" { + joincmd := strings.Join(cmd, " ") + checkCommand := []string{"sh", "-c", fmt.Sprintf("if [ ! -f %v ]; then echo \"LocalFile not found exiting...\"; exit 1; fi;\n%v", s.FullName(), joincmd)} + return checkCommand + } + return cmd +} + +// CLI is an innternal type to support k6 invocation in initialization stage. +// Not all k6 commands allow the same set of arguments so CLI is an object +// meant to contain only the ones fit for the archive call. +// Maybe revise this once crococonf is closer to integration? +type CLI struct { + ArchiveArgs string + // k6-operator doesn't care for most values of CLI arguments to k6, with an exception of cloud output + HasCloudOut bool +} + +func ParseCLI(spec *v1alpha1.K6Spec) *CLI { + lastArgV := func(start int, args []string) (end int) { + var nextArg bool + end = start + for !nextArg && end < len(args) { + if args[end][0] == '-' { + nextArg = true + break + } + end++ + } + return + } + + var cli CLI + + args := strings.Split(spec.Arguments, " ") + i := 0 + for i < len(args) { + args[i] = strings.TrimSpace(args[i]) + if len(args[i]) == 0 { + i++ + continue + } + if args[i][0] == '-' { + end := lastArgV(i+1, args) + + switch args[i] { + case "-o", "--out": + for j := 0; j < end; j++ { + if args[j] == "cloud" { + cli.HasCloudOut = true + } + } + case "-l", "--linger", "--no-usage-report": + // non-archive arguments, so skip them + break + case "--verbose", "-v": + // this argument is acceptable by archive but it'd + // mess up the JSON output of `k6 inspect` + break + default: + if len(cli.ArchiveArgs) > 0 { + cli.ArchiveArgs += " " + } + cli.ArchiveArgs += strings.Join(args[i:end], " ") + } + i = end + } + } + + return &cli +} diff --git a/pkg/types/types_test.go b/pkg/types/types_test.go new file mode 100644 index 00000000..07fc21d9 --- /dev/null +++ b/pkg/types/types_test.go @@ -0,0 +1,88 @@ +package types + +import ( + "testing" + + "github.com/grafana/k6-operator/api/v1alpha1" + "github.com/stretchr/testify/assert" +) + +func Test_ParseCLI(t *testing.T) { + tests := []struct { + name string + argLine string + cli CLI + }{ + { + "EmptyArgs", + "", + CLI{}, + }, + { + "ShortArchiveArgs", + "-u 10 -d 5", + CLI{ + ArchiveArgs: "-u 10 -d 5", + }, + }, + { + "LongArchiveArgs", + "--vus 10 --duration 5", + CLI{ + ArchiveArgs: "--vus 10 --duration 5", + }, + }, + { + "ShortNonArchiveArg", + "-u 10 -d 5 -l", + CLI{ + ArchiveArgs: "-u 10 -d 5", + }, + }, + { + "LongNonArchiveArgs", + "--vus 10 --duration 5 --linger", + CLI{ + ArchiveArgs: "--vus 10 --duration 5", + }, + }, + { + "OutWithoutCloudArgs", + "--vus 10 -o json -o csv", + CLI{ + ArchiveArgs: "--vus 10", + HasCloudOut: false, + }, + }, + { + "OutWithCloudArgs", + "--vus 10 --out json -o csv --out cloud", + CLI{ + ArchiveArgs: "--vus 10", + HasCloudOut: true, + }, + }, + { + "VerboseOutWithCloudArgs", + "--vus 10 --out json -o csv --out cloud --verbose", + CLI{ + ArchiveArgs: "--vus 10", + HasCloudOut: true, + }, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + spec := v1alpha1.K6Spec{ + Arguments: test.argLine, + } + cli := ParseCLI(&spec) + + assert.Equal(t, test.cli.ArchiveArgs, cli.ArchiveArgs) + assert.Equal(t, test.cli.HasCloudOut, cli.HasCloudOut) + }) + } +}