diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0cdb7a..dc792f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,6 +75,7 @@ jobs: uses: golangci/golangci-lint-action@v2 with: version: ${{ env.GOLANGCI_VERSION }} + skip-go-installation: true check-diff: runs-on: ubuntu-18.04 diff --git a/go.mod b/go.mod index b988d3e..7e90ab1 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,16 @@ require ( github.com/google/addlicense v0.0.0-20210428195630-6d92264d7170 github.com/google/go-cmp v0.5.2 github.com/pkg/errors v0.9.1 + github.com/prometheus/client_golang v1.7.1 + github.com/prometheus/common v0.10.0 + github.com/sirupsen/logrus v1.6.0 + github.com/spf13/cobra v1.1.1 + github.com/spf13/pflag v1.0.5 + gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.20.2 k8s.io/apiextensions-apiserver v0.20.1 k8s.io/apimachinery v0.20.2 + k8s.io/client-go v0.20.2 k8s.io/utils v0.0.0-20210111153108-fddb29f9d009 sigs.k8s.io/controller-runtime v0.8.3 ) diff --git a/go.sum b/go.sum index 0cf036f..13cfa29 100644 --- a/go.sum +++ b/go.sum @@ -345,6 +345,7 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -365,6 +366,7 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -496,6 +498,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 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= @@ -510,6 +513,7 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU 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.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= 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= diff --git a/internal/providerScale/README.md b/internal/providerScale/README.md new file mode 100644 index 0000000..53728e7 --- /dev/null +++ b/internal/providerScale/README.md @@ -0,0 +1,34 @@ +# provider-scale + +This tool collects and reports some performance metrics. The details in the [one-pager]. + +### Usage + +When this tool is executed, an end-to-end experiment will run. The inputs of this experiment can be manipulated by using +the command-line options. + +``` +Flags: + --address string Address of Prometheus service (default "http://localhost:9090") + --clean Delete deployed MRs (default true) + -h, --help help for provider-scale + --mrs stringToInt Managed resource templates that will be deployed (default []) + --provider-namespace string Namespace name of provider (default "crossplane-system") + --provider-pod string Pod name of provider + --step-duration duration Step duration between two data points (default 30s) +``` + +The `provider-pod` and `mrs` options are required. + +Example usage: + +``` +provider-scale --mrs ./internal/providerScale/manifests/virtualnetwork.yaml=2 +--mrs ./internal/providerScale/manifests/loadbalancer.yaml=2 +--provider-pod crossplane-provider-jet-azure +--provider-namespace crossplane-system +``` + +With this input, two virtualnetwork & loadbalancer MRs will be deployed to the cluster. + +[one-pager]: https://github.com/crossplane/crossplane/pull/2983 \ No newline at end of file diff --git a/internal/providerScale/cmd/common/common.go b/internal/providerScale/cmd/common/common.go new file mode 100644 index 0000000..47e2a9c --- /dev/null +++ b/internal/providerScale/cmd/common/common.go @@ -0,0 +1,105 @@ +// Copyright 2022 The Crossplane Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +import ( + "fmt" + "os" + "strconv" + "time" + + log "github.com/sirupsen/logrus" + + "github.com/prometheus/common/model" + + "github.com/prometheus/client_golang/api" + v1 "github.com/prometheus/client_golang/api/prometheus/v1" +) + +// Data represents a collected data +type Data struct { + Timestamp time.Time + Value float64 +} + +// Result represents all collected data for a metric +type Result struct { + Data []Data + Metric string + MetricUnit string + Peak, Average float64 +} + +// ConstructPrometheusClient creates a Prometheus API Client +func ConstructPrometheusClient(address string) v1.API { + client, err := api.NewClient(api.Config{ + Address: address, + }) + + if err != nil { + fmt.Printf("Error creating client: %v\n", err) + os.Exit(1) + } + + return v1.NewAPI(client) +} + +// ConstructTimeRange creates a Range object that consists the start time, end time and step duration +func ConstructTimeRange(startTime, endTime time.Time, stepDuration time.Duration) v1.Range { + return v1.Range{ + Start: startTime, + End: endTime, + Step: stepDuration, + } +} + +// ConstructResult creates a Result object from collected data +func ConstructResult(value model.Value, metric, unit string) (*Result, error) { + result := &Result{} + matrix := value.(model.Matrix) + + for _, m := range matrix { + for _, v := range m.Values { + valueNum, err := strconv.ParseFloat(v.Value.String(), 64) + if err != nil { + return nil, err + } + result.Data = append(result.Data, Data{Timestamp: v.Timestamp.Time(), Value: valueNum}) + } + } + + result.Average, result.Peak = CalculateAverageAndPeak(result.Data) + result.Metric = metric + result.MetricUnit = unit + return result, nil +} + +// CalculateAverageAndPeak calculates the average and peak values of related metric +func CalculateAverageAndPeak(data []Data) (float64, float64) { + var sum, peak float64 + for _, d := range data { + sum += d.Value + + if d.Value > peak { + peak = d.Value + } + } + return sum / float64(len(data)), peak +} + +func (r Result) String() { + log.Info(fmt.Sprintf("Average %s: %f %s \n", r.Metric, r.Average, r.MetricUnit)) + log.Info(fmt.Sprintf("Peak %s: %f %s \n", r.Metric, r.Peak, r.MetricUnit)) +} diff --git a/internal/providerScale/cmd/managed/managed.go b/internal/providerScale/cmd/managed/managed.go new file mode 100644 index 0000000..a2bdcd8 --- /dev/null +++ b/internal/providerScale/cmd/managed/managed.go @@ -0,0 +1,302 @@ +// Copyright 2022 The Crossplane Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package managed + +import ( + "bufio" + "context" + "fmt" + "io/ioutil" + "os" + "os/exec" + "strings" + "time" + + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v2" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/crossplane/conformance/internal/providerScale/cmd/common" +) + +// RunExperiment runs the experiment according to command-line inputs. +// Firstly the input manifests are deployed. After the all MRs are ready, time to readiness metrics are calculated. +// Then, by default, all deployed MRs are deleted. +func RunExperiment(mrTemplatePaths map[string]int, clean bool) ([]common.Result, error) { //nolint:gocyclo + var timeToReadinessResults []common.Result //nolint:prealloc + + client := createDynamicClient() + + if err := applyResources(client, mrTemplatePaths); err != nil { + return nil, err + } + + if err := checkReadiness(client, mrTemplatePaths); err != nil { + return nil, err + } + + timeToReadinessResults, err := calculateReadinessDuration(client, mrTemplatePaths) + if err != nil { + return nil, err + } + + if clean { + log.Info("Deleting resources...") + if err := deleteResources(client, mrTemplatePaths); err != nil { + return nil, err + } + log.Info("Checking deletion of resources...") + if err := checkDeletion(client, mrTemplatePaths); err != nil { + return nil, err + } + } + return timeToReadinessResults, nil +} + +func applyResources(client dynamic.Interface, mrTemplatePaths map[string]int) error { + file, err := os.Create(fmt.Sprintf("/tmp/test.yaml")) + if err != nil { + return err + } + if err := file.Close(); err != nil { + return err + } + + for mrPath, count := range mrTemplatePaths { + m, err := readYamlFile(mrPath) + if err != nil { + return err + } + o := prepareUnstructuredObject(m) + + f, err := os.OpenFile("/tmp/test.yaml", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + return err + } + + for i := 1; i <= count; i++ { + o["metadata"].(map[string]interface{})["name"] = fmt.Sprintf("test-%d", i) + + b, err := yaml.Marshal(o) + if err != nil { + return err + } + + if _, err := f.Write(b); err != nil { + return err + } + + if _, err := f.WriteString("\n---\n\n"); err != nil { + return err + } + + log.Info(fmt.Sprintf("%s/%s was successfully created!\n", m["kind"], o["metadata"].(map[string]interface{})["name"])) + } + + cmd := exec.Command("bash", "-c", fmt.Sprintf(`"kubectl" apply -f /tmp/test.yaml`)) // #nosec G204 + stdout, _ := cmd.StdoutPipe() + if err := cmd.Start(); err != nil { + return errors.Wrap(err, "cannot start kubectl") + } + sc := bufio.NewScanner(stdout) + sc.Split(bufio.ScanLines) + for sc.Scan() { + fmt.Println(sc.Text()) + } + if err := cmd.Wait(); err != nil { + return err + } + } + return nil +} + +func deleteResources(client dynamic.Interface, mrTemplatePaths map[string]int) error { + for mrPath := range mrTemplatePaths { + m, err := readYamlFile(mrPath) + if err != nil { + return err + } + + background := metav1.DeletePropagationBackground + if err := client.Resource(prepareGvk(m)).DeleteCollection(context.TODO(), + metav1.DeleteOptions{PropagationPolicy: &background}, metav1.ListOptions{}); err != nil { + return err + } + } + return nil +} + +func checkReadiness(client dynamic.Interface, mrTemplatePaths map[string]int) error { + for mrPath := range mrTemplatePaths { + m, err := readYamlFile(mrPath) + if err != nil { + return err + } + + for { + log.Info("Checking readiness of resources...") + list, err := client.Resource(prepareGvk(m)).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return err + } + if isReady(list) { + break + } + time.Sleep(10 * time.Second) + } + } + return nil +} + +func isReady(list *unstructured.UnstructuredList) bool { + for _, l := range list.Items { + if l.Object["status"] == nil { + return false + } + conditions := l.Object["status"].(map[string]interface{})["conditions"].([]interface{}) + + status := "" + for _, condition := range conditions { + c := condition.(map[string]interface{}) + if c["type"] == "Ready" { + status = c["status"].(string) + } + } + + if status == "False" || status == "" { + return false + } + } + return true +} + +func calculateReadinessDuration(client dynamic.Interface, mrTemplatePaths map[string]int) ([]common.Result, error) { + var results []common.Result //nolint:prealloc + for mrPath := range mrTemplatePaths { + log.Info("Calculating readiness time of resources...") + var result common.Result + + m, err := readYamlFile(mrPath) + if err != nil { + return nil, err + } + + list, err := client.Resource(prepareGvk(m)).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return nil, err + } + for _, l := range list.Items { + readinessTime := metav1.Time{} + creationTimestamp := l.GetCreationTimestamp() + conditions := l.Object["status"].(map[string]interface{})["conditions"].([]interface{}) + + for _, condition := range conditions { + c := condition.(map[string]interface{}) + + if c["type"] == "Ready" && c["status"] == "True" { + t, err := time.Parse(time.RFC3339, c["lastTransitionTime"].(string)) + if err != nil { + return nil, err + } + readinessTime.Time = t + + diff := readinessTime.Sub(creationTimestamp.Time) + result.Data = append(result.Data, common.Data{Value: diff.Seconds()}) + break + } + } + } + result.Metric = fmt.Sprintf("Time to Readiness of %s", m["kind"]) + result.MetricUnit = "seconds" + result.Average, result.Peak = common.CalculateAverageAndPeak(result.Data) + results = append(results, result) + } + return results, nil +} + +func checkDeletion(client dynamic.Interface, mrTemplatePaths map[string]int) error { + for mrPath := range mrTemplatePaths { + m, err := readYamlFile(mrPath) + if err != nil { + return err + } + + for { + log.Info("Checking deletion of resources...") + list, err := client.Resource(prepareGvk(m)).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return err + } + if len(list.Items) == 0 { + break + } + time.Sleep(10 * time.Second) + } + } + return nil +} + +func prepareGvk(m map[interface{}]interface{}) schema.GroupVersionResource { + suffix := "s" + apiVersion := strings.Split(m["apiVersion"].(string), "/") + kind := strings.ToLower(m["kind"].(string)) + + if kind[len(kind)-1] == 'y' { + kind = kind[:len(kind)-1] + suffix = "ies" + } + return schema.GroupVersionResource{ + Group: apiVersion[0], + Version: apiVersion[1], + Resource: fmt.Sprintf("%s%s", kind, suffix), + } +} + +func prepareUnstructuredObject(m map[interface{}]interface{}) map[string]interface{} { + result := map[string]interface{}{} + for k, v := range m { + t, ok := v.(map[interface{}]interface{}) + if ok { + result[k.(string)] = prepareUnstructuredObject(t) + } else { + result[k.(string)] = v + } + } + return result +} + +func readYamlFile(fileName string) (map[interface{}]interface{}, error) { + yamlFile, err := ioutil.ReadFile(fileName) //nolint:gosec + if err != nil { + return nil, err + } + + m := make(map[interface{}]interface{}) + err = yaml.Unmarshal(yamlFile, m) + if err != nil { + return nil, err + } + + return m, nil +} + +func createDynamicClient() dynamic.Interface { + return dynamic.NewForConfigOrDie(ctrl.GetConfigOrDie()) +} diff --git a/internal/providerScale/cmd/quantify.go b/internal/providerScale/cmd/quantify.go new file mode 100644 index 0000000..543705e --- /dev/null +++ b/internal/providerScale/cmd/quantify.go @@ -0,0 +1,134 @@ +// Copyright 2022 The Crossplane Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "context" + "fmt" + "time" + + "github.com/crossplane/conformance/internal/providerScale/cmd/common" + "github.com/crossplane/conformance/internal/providerScale/cmd/managed" + + "github.com/prometheus/common/model" + + log "github.com/sirupsen/logrus" + + "github.com/spf13/cobra" +) + +// QuantifyOptions represents the options of quantify command +type QuantifyOptions struct { + providerPod string + providerNamespace string + mrPaths map[string]int + cmd *cobra.Command + address string + startTime time.Time + endTime time.Time + stepDuration time.Duration + clean bool + nodeIP string +} + +// NewCmdQuantify creates a cobra command +func NewCmdQuantify() *cobra.Command { + o := QuantifyOptions{} + o.cmd = &cobra.Command{ + Use: "provider-scale [flags]", + Short: "This tool collects CPU & Memory Utilization and time to readiness of MRs metrics of providers and " + + "reports them. When you execute this tool an end-to-end experiment will run.", + Example: "provider-scale --mrs ./internal/providerScale/manifests/virtualnetwork.yaml=2 " + + "--mrs ./internal/providerScale/manifests/loadbalancer.yaml=2" + + "--provider-pod crossplane-provider-jet-azure " + + "--provider-namespace crossplane-system", + RunE: o.Run, + } + + o.cmd.Flags().StringVar(&o.providerPod, "provider-pod", "", "Pod name of provider") + o.cmd.Flags().StringVar(&o.providerNamespace, "provider-namespace", "crossplane-system", + "Namespace name of provider") + o.cmd.Flags().StringToIntVar(&o.mrPaths, "mrs", nil, "Managed resource templates that will be deployed") + o.cmd.Flags().StringVar(&o.address, "address", "http://localhost:9090", "Address of Prometheus service") + o.cmd.Flags().DurationVar(&o.stepDuration, "step-duration", 30*time.Second, "Step duration between two data points") + o.cmd.Flags().BoolVar(&o.clean, "clean", true, "Delete deployed MRs") + o.cmd.Flags().StringVar(&o.nodeIP, "node", "", "Node IP") + + if err := o.cmd.MarkFlagRequired("provider-pod"); err != nil { + panic(err) + } + if err := o.cmd.MarkFlagRequired("mrs"); err != nil { + panic(err) + } + + return o.cmd +} + +// Run executes the quantify command's tasks. +func (o *QuantifyOptions) Run(_ *cobra.Command, _ []string) error { + o.startTime = time.Now() + log.Infof("Experiment Started %v\n\n", o.startTime) + timeToReadinessResults, err := managed.RunExperiment(o.mrPaths, o.clean) + if err != nil { + return err + } + o.endTime = time.Now() + log.Infof("\nExperiment Ended %v\n\n", o.endTime) + log.Infof("Results\n------------------------------------------------------------\n") + log.Infof("Experiment Duration: %f seconds\n", o.endTime.Sub(o.startTime).Seconds()) + time.Sleep(60 * time.Second) + queryResultMemory, err := o.CollectData(fmt.Sprintf(`sum(node_namespace_pod_container:container_memory_working_set_bytes{pod="%s", namespace="%s"})`, + o.providerPod, o.providerNamespace)) + if err != nil { + return err + } + memoryResult, err := common.ConstructResult(queryResultMemory, "Memory Working Set", "Bytes") + if err != nil { + return err + } + qureyResultCPURate, err := o.CollectData(fmt.Sprintf(`instance:node_cpu_utilisation:rate5m{instance="%s"} * 100`, o.nodeIP)) + if err != nil { + return err + } + cpuRateResult, err := common.ConstructResult(qureyResultCPURate, "CPU Utilisation", "Rate") + if err != nil { + return err + } + for _, timeToReadinessResult := range timeToReadinessResults { + timeToReadinessResult.String() + } + memoryResult.String() + cpuRateResult.String() + return nil +} + +// CollectData sends query and collect data by using the prometheus client +func (o *QuantifyOptions) CollectData(query string) (model.Value, error) { + client := common.ConstructPrometheusClient(o.address) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // We continue to collect metrics after the tests were ended. Because + // the memory consumption sometimes increase after the tests completed. + r := common.ConstructTimeRange(o.startTime, o.endTime.Add(60*time.Second), o.stepDuration) + result, warnings, err := client.QueryRange(ctx, query, r) + if err != nil { + return nil, err + } + if len(warnings) > 0 { + log.Infof("Warnings: %v\n", warnings) + } + return result, err +} diff --git a/internal/providerScale/main.go b/internal/providerScale/main.go new file mode 100644 index 0000000..838be20 --- /dev/null +++ b/internal/providerScale/main.go @@ -0,0 +1,31 @@ +// Copyright 2022 The Crossplane Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "os" + + "github.com/spf13/pflag" + + "github.com/crossplane/conformance/internal/providerScale/cmd" +) + +func main() { + pflag.CommandLine = pflag.NewFlagSet("provider-scale", pflag.ExitOnError) + root := cmd.NewCmdQuantify() + if err := root.Execute(); err != nil { + os.Exit(1) + } +} diff --git a/internal/providerScale/manifests/ecr.yaml b/internal/providerScale/manifests/ecr.yaml new file mode 100644 index 0000000..621818b --- /dev/null +++ b/internal/providerScale/manifests/ecr.yaml @@ -0,0 +1,12 @@ +apiVersion: ecr.aws.upbound.io/v1beta1 +kind: Repository +metadata: + name: upbound-sample-repository +spec: + forProvider: + region: us-east-1 + imageScanningConfiguration: + - scanOnPush: true + imageTagMutability: "IMMUTABLE" + tags: + key1: value1 \ No newline at end of file diff --git a/internal/providerScale/manifests/loadbalancer.yaml b/internal/providerScale/manifests/loadbalancer.yaml new file mode 100644 index 0000000..0159915 --- /dev/null +++ b/internal/providerScale/manifests/loadbalancer.yaml @@ -0,0 +1,24 @@ +# Copyright 2022 The Crossplane Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: network.azure.jet.crossplane.io/v1alpha2 +kind: LoadBalancer +metadata: + name: test +spec: + forProvider: + location: East US + resourceGroupName: example + providerConfigRef: + name: example diff --git a/internal/providerScale/manifests/virtualnetwork.yaml b/internal/providerScale/manifests/virtualnetwork.yaml new file mode 100644 index 0000000..214755f --- /dev/null +++ b/internal/providerScale/manifests/virtualnetwork.yaml @@ -0,0 +1,32 @@ +# Copyright 2022 The Crossplane Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: network.azure.jet.crossplane.io/v1alpha2 +kind: VirtualNetwork +metadata: + name: test +spec: + forProvider: + addressSpace: + - 10.0.0.0/16 + dnsServers: + - 10.0.0.1 + - 10.0.0.2 + - 10.0.0.3 + location: East US + resourceGroupName: example + tags: + experiment: "2" + providerConfigRef: + name: example