From b32342a34050aa8b706e3aad257eb0d4194b76b7 Mon Sep 17 00:00:00 2001 From: John Hawley Date: Fri, 8 Nov 2024 11:38:20 -0500 Subject: [PATCH 1/4] initial commit based off of istio/isio 1.23 release branch --- go.mod | 148 + go.sum | 383 + .../istio-operator/crds/crd-operator.yaml | 48 + .../pkg/apis/addtoscheme_istio_v1alpha1.go | 24 + operator/pkg/apis/apis.go | 27 + operator/pkg/apis/istio/common.go | 55 + operator/pkg/apis/istio/v1alpha1/common.go | 52 + .../pkg/apis/istio/v1alpha1/deepcopy_test.go | 143 + operator/pkg/apis/istio/v1alpha1/register.go | 51 + .../istio/v1alpha1/testdata/quantity.yaml | 24 + operator/pkg/apis/istio/v1alpha1/types.go | 71 + .../istio/v1alpha1/validation/validation.go | 358 + .../v1alpha1/validation/validation_test.go | 329 + .../apis/istio/v1alpha1/value_types_json.go | 74 + .../apis/istio/v1alpha1/values_types.pb.go | 7445 +++++++++++++++++ .../apis/istio/v1alpha1/values_types.proto | 1439 ++++ .../istio/v1alpha1/zz_generated.deepcopy.go | 89 + operator/pkg/component/component.go | 383 + operator/pkg/controlplane/control_plane.go | 146 + .../pkg/controlplane/control_plane_test.go | 249 + operator/pkg/helm/fs_renderer_test.go | 103 + operator/pkg/helm/helm.go | 259 + operator/pkg/helm/renderer.go | 182 + .../pkg/helm/testdata/addons/a/Chart.yaml | 8 + .../helm/testdata/addons/invalid/a/Chart.yaml | 8 + .../helm/testdata/addons/invalid/b/Chart.yaml | 8 + .../helm/testdata/istio-1.3.0-linux.tar.gz | Bin 0 -> 617 bytes operator/pkg/helm/testdata/render/Chart.yaml | 8 + .../render/templates/fs_template.yaml | 8 + operator/pkg/helm/vfs_renderer_test.go | 37 + operator/pkg/manifest/shared.go | 613 ++ operator/pkg/manifest/shared_test.go | 154 + operator/pkg/metrics/monitoring.go | 210 + operator/pkg/metrics/resource_counts.go | 68 + operator/pkg/metrics/utils.go | 58 + operator/pkg/name/name.go | 226 + operator/pkg/name/name_test.go | 367 + operator/pkg/object/objects.go | 579 ++ operator/pkg/object/objects_test.go | 713 ++ operator/pkg/object/testdata/empty.yaml | 2 + operator/pkg/object/testdata/invalid.yaml | 8 + operator/pkg/object/testdata/malformed.yaml | 17 + .../well-formed-with-comments.out.yaml | 9 + .../testdata/well-formed-with-comments.yaml | 12 + .../testdata/well-formed-with-space.out.yaml | 7 + .../testdata/well-formed-with-space.yaml | 8 + operator/pkg/object/testdata/well-formed.yaml | 22 + operator/pkg/patch/patch.go | 211 + operator/pkg/patch/patch_test.go | 517 ++ operator/pkg/tpath/struct.go | 134 + operator/pkg/tpath/struct_test.go | 162 + operator/pkg/tpath/tree.go | 574 ++ operator/pkg/tpath/tree_test.go | 844 ++ operator/pkg/tpath/util.go | 63 + operator/pkg/tpath/util_test.go | 122 + .../translate/strategic_port_merge_test.go | 189 + operator/pkg/translate/translate.go | 1062 +++ operator/pkg/translate/translate_common.go | 121 + .../pkg/translate/translate_common_test.go | 114 + operator/pkg/translate/translate_test.go | 196 + operator/pkg/translate/translate_value.go | 609 ++ .../pkg/translate/translate_value_test.go | 907 ++ operator/pkg/translate/yaml_tree.go | 57 + operator/pkg/util/clog/clog.go | 109 + operator/pkg/util/common.go | 40 + operator/pkg/util/common_test.go | 110 + operator/pkg/util/errs.go | 146 + operator/pkg/util/errs_test.go | 121 + operator/pkg/util/k8s.go | 119 + operator/pkg/util/k8s_test.go | 165 + operator/pkg/util/label.go | 35 + operator/pkg/util/label_test.go | 49 + operator/pkg/util/merge_iop.go | 267 + operator/pkg/util/merge_iop_test.go | 143 + operator/pkg/util/path.go | 207 + operator/pkg/util/path_test.go | 383 + operator/pkg/util/progress/progress.go | 236 + operator/pkg/util/progress/progress_test.go | 76 + operator/pkg/util/reflect.go | 316 + operator/pkg/util/reflect_test.go | 413 + operator/pkg/util/testdata/overlay-iop.yaml | 104 + .../yaml/input/convention_boolean.yaml | 15 + .../testdata/yaml/input/convention_float.yaml | 15 + .../yaml/input/convention_integer.yaml | 15 + .../util/testdata/yaml/input/yaml_layer1.yaml | 19 + .../yaml/input/yaml_layer1_stdin.yaml | 15 + .../util/testdata/yaml/input/yaml_layer2.yaml | 15 + .../util/testdata/yaml/input/yaml_layer3.yaml | 13 + .../yaml/output/convention_boolean.yaml | 15 + .../yaml/output/convention_float.yaml | 15 + .../yaml/output/convention_integer.yaml | 15 + .../pkg/util/testdata/yaml/output/layer1.yaml | 19 + .../util/testdata/yaml/output/layer1_2.yaml | 20 + .../util/testdata/yaml/output/layer1_2_3.yaml | 20 + .../testdata/yaml/output/layer1_stdin.yaml | 19 + operator/pkg/util/util.go | 159 + operator/pkg/util/util_test.go | 252 + operator/pkg/util/yaml.go | 322 + operator/pkg/util/yaml_test.go | 362 + operator/pkg/validate/common.go | 345 + operator/pkg/validate/validate.go | 233 + operator/pkg/validate/validate_test.go | 202 + operator/pkg/validate/validate_values.go | 71 + operator/pkg/validate/validate_values_test.go | 246 + operator/pkg/version/version.go | 183 + operator/pkg/version/version_test.go | 317 + operator/version/version.go | 56 + 107 files changed, 27131 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 manifests/charts/istio-operator/crds/crd-operator.yaml create mode 100644 operator/pkg/apis/addtoscheme_istio_v1alpha1.go create mode 100644 operator/pkg/apis/apis.go create mode 100644 operator/pkg/apis/istio/common.go create mode 100644 operator/pkg/apis/istio/v1alpha1/common.go create mode 100644 operator/pkg/apis/istio/v1alpha1/deepcopy_test.go create mode 100644 operator/pkg/apis/istio/v1alpha1/register.go create mode 100644 operator/pkg/apis/istio/v1alpha1/testdata/quantity.yaml create mode 100644 operator/pkg/apis/istio/v1alpha1/types.go create mode 100644 operator/pkg/apis/istio/v1alpha1/validation/validation.go create mode 100644 operator/pkg/apis/istio/v1alpha1/validation/validation_test.go create mode 100644 operator/pkg/apis/istio/v1alpha1/value_types_json.go create mode 100644 operator/pkg/apis/istio/v1alpha1/values_types.pb.go create mode 100644 operator/pkg/apis/istio/v1alpha1/values_types.proto create mode 100644 operator/pkg/apis/istio/v1alpha1/zz_generated.deepcopy.go create mode 100644 operator/pkg/component/component.go create mode 100644 operator/pkg/controlplane/control_plane.go create mode 100644 operator/pkg/controlplane/control_plane_test.go create mode 100644 operator/pkg/helm/fs_renderer_test.go create mode 100644 operator/pkg/helm/helm.go create mode 100644 operator/pkg/helm/renderer.go create mode 100644 operator/pkg/helm/testdata/addons/a/Chart.yaml create mode 100644 operator/pkg/helm/testdata/addons/invalid/a/Chart.yaml create mode 100644 operator/pkg/helm/testdata/addons/invalid/b/Chart.yaml create mode 100644 operator/pkg/helm/testdata/istio-1.3.0-linux.tar.gz create mode 100644 operator/pkg/helm/testdata/render/Chart.yaml create mode 100644 operator/pkg/helm/testdata/render/templates/fs_template.yaml create mode 100644 operator/pkg/helm/vfs_renderer_test.go create mode 100644 operator/pkg/manifest/shared.go create mode 100644 operator/pkg/manifest/shared_test.go create mode 100644 operator/pkg/metrics/monitoring.go create mode 100644 operator/pkg/metrics/resource_counts.go create mode 100644 operator/pkg/metrics/utils.go create mode 100644 operator/pkg/name/name.go create mode 100644 operator/pkg/name/name_test.go create mode 100644 operator/pkg/object/objects.go create mode 100644 operator/pkg/object/objects_test.go create mode 100644 operator/pkg/object/testdata/empty.yaml create mode 100644 operator/pkg/object/testdata/invalid.yaml create mode 100644 operator/pkg/object/testdata/malformed.yaml create mode 100644 operator/pkg/object/testdata/well-formed-with-comments.out.yaml create mode 100644 operator/pkg/object/testdata/well-formed-with-comments.yaml create mode 100644 operator/pkg/object/testdata/well-formed-with-space.out.yaml create mode 100644 operator/pkg/object/testdata/well-formed-with-space.yaml create mode 100644 operator/pkg/object/testdata/well-formed.yaml create mode 100644 operator/pkg/patch/patch.go create mode 100644 operator/pkg/patch/patch_test.go create mode 100644 operator/pkg/tpath/struct.go create mode 100644 operator/pkg/tpath/struct_test.go create mode 100644 operator/pkg/tpath/tree.go create mode 100644 operator/pkg/tpath/tree_test.go create mode 100644 operator/pkg/tpath/util.go create mode 100644 operator/pkg/tpath/util_test.go create mode 100644 operator/pkg/translate/strategic_port_merge_test.go create mode 100644 operator/pkg/translate/translate.go create mode 100644 operator/pkg/translate/translate_common.go create mode 100644 operator/pkg/translate/translate_common_test.go create mode 100644 operator/pkg/translate/translate_test.go create mode 100644 operator/pkg/translate/translate_value.go create mode 100644 operator/pkg/translate/translate_value_test.go create mode 100644 operator/pkg/translate/yaml_tree.go create mode 100644 operator/pkg/util/clog/clog.go create mode 100644 operator/pkg/util/common.go create mode 100644 operator/pkg/util/common_test.go create mode 100644 operator/pkg/util/errs.go create mode 100644 operator/pkg/util/errs_test.go create mode 100644 operator/pkg/util/k8s.go create mode 100644 operator/pkg/util/k8s_test.go create mode 100644 operator/pkg/util/label.go create mode 100644 operator/pkg/util/label_test.go create mode 100644 operator/pkg/util/merge_iop.go create mode 100644 operator/pkg/util/merge_iop_test.go create mode 100644 operator/pkg/util/path.go create mode 100644 operator/pkg/util/path_test.go create mode 100644 operator/pkg/util/progress/progress.go create mode 100644 operator/pkg/util/progress/progress_test.go create mode 100644 operator/pkg/util/reflect.go create mode 100644 operator/pkg/util/reflect_test.go create mode 100644 operator/pkg/util/testdata/overlay-iop.yaml create mode 100644 operator/pkg/util/testdata/yaml/input/convention_boolean.yaml create mode 100644 operator/pkg/util/testdata/yaml/input/convention_float.yaml create mode 100644 operator/pkg/util/testdata/yaml/input/convention_integer.yaml create mode 100644 operator/pkg/util/testdata/yaml/input/yaml_layer1.yaml create mode 100644 operator/pkg/util/testdata/yaml/input/yaml_layer1_stdin.yaml create mode 100644 operator/pkg/util/testdata/yaml/input/yaml_layer2.yaml create mode 100644 operator/pkg/util/testdata/yaml/input/yaml_layer3.yaml create mode 100644 operator/pkg/util/testdata/yaml/output/convention_boolean.yaml create mode 100644 operator/pkg/util/testdata/yaml/output/convention_float.yaml create mode 100644 operator/pkg/util/testdata/yaml/output/convention_integer.yaml create mode 100644 operator/pkg/util/testdata/yaml/output/layer1.yaml create mode 100644 operator/pkg/util/testdata/yaml/output/layer1_2.yaml create mode 100644 operator/pkg/util/testdata/yaml/output/layer1_2_3.yaml create mode 100644 operator/pkg/util/testdata/yaml/output/layer1_stdin.yaml create mode 100644 operator/pkg/util/util.go create mode 100644 operator/pkg/util/util_test.go create mode 100644 operator/pkg/util/yaml.go create mode 100644 operator/pkg/util/yaml_test.go create mode 100644 operator/pkg/validate/common.go create mode 100644 operator/pkg/validate/validate.go create mode 100644 operator/pkg/validate/validate_test.go create mode 100644 operator/pkg/validate/validate_values.go create mode 100644 operator/pkg/validate/validate_values_test.go create mode 100644 operator/pkg/version/version.go create mode 100644 operator/pkg/version/version_test.go create mode 100644 operator/version/version.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7671d9a --- /dev/null +++ b/go.mod @@ -0,0 +1,148 @@ +module github.com/jehawley/istio + +go 1.22.0 + +// Client-go does not handle different versions of mergo due to some breaking changes - use the matching version +replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.5 + +require ( + github.com/cheggaaa/pb/v3 v3.1.5 + github.com/evanphx/json-patch/v5 v5.9.0 + github.com/golang/protobuf v1.5.4 + github.com/google/go-cmp v0.6.0 + github.com/hashicorp/go-version v1.7.0 + github.com/kylelemons/godebug v1.1.0 + github.com/prometheus/prometheus v0.54.1 + google.golang.org/protobuf v1.34.2 + gopkg.in/yaml.v2 v2.4.0 + helm.sh/helm/v3 v3.16.1 + istio.io/api v1.24.0 + istio.io/api/123 v0.0.0-00010101000000-000000000000 + istio.io/istio v0.0.0-20241105211601-8825a6b7f8c9 + k8s.io/api v0.31.1 + k8s.io/apimachinery v0.31.1 + k8s.io/client-go v0.31.1 + sigs.k8s.io/controller-runtime v0.19.0 + sigs.k8s.io/yaml v1.4.0 +) + +require ( + cel.dev/expr v0.16.0 // indirect + dario.cat/mergo v1.0.1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/Masterminds/sprig/v3 v3.3.0 // indirect + github.com/VividCortex/ewma v1.2.0 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect + github.com/cyphar/filepath-securejoin v0.3.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.0 // indirect + github.com/envoyproxy/go-control-plane v0.13.1-0.20241009135036-bec043f2e850 // indirect + github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect + github.com/fatih/color v1.17.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/goccy/go-json v0.10.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/cel-go v0.21.0 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/huandu/xstrings v1.5.0 // indirect + github.com/imdario/mergo v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect + github.com/lestrrat-go/blackmagic v1.0.2 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/iter v1.0.2 // indirect + github.com/lestrrat-go/jwx v1.2.30 // indirect + github.com/lestrrat-go/option v1.0.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/spdystream v0.4.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240409071808-615f978279ca // indirect + github.com/prometheus/client_golang v1.20.4 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.60.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rivo/uniseg v0.4.6 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + go.opentelemetry.io/otel v1.30.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.52.0 // indirect + go.opentelemetry.io/otel/metric v1.30.0 // indirect + go.opentelemetry.io/otel/sdk v1.30.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.30.0 // indirect + go.opentelemetry.io/otel/trace v1.30.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect + golang.org/x/time v0.6.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect + google.golang.org/grpc v1.67.1 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + istio.io/client-go v1.24.0-rc.0.0.20241101201152-acb34243979a // indirect + k8s.io/apiextensions-apiserver v0.31.1 // indirect + k8s.io/apiserver v0.31.1 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108 // indirect + k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect + sigs.k8s.io/gateway-api v1.2.0 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/mcs-api v0.1.1-0.20240624222831-d7001fe1d21c // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect +) + +// This is necessary while we still depend on the IstioOperator and related apis, which were removed in 1.24 +// This can be removed once we drop ILM v2 support in GME. +replace istio.io/api/123 => istio.io/api v1.23.3 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2346847 --- /dev/null +++ b/go.sum @@ -0,0 +1,383 @@ +cel.dev/expr v0.16.0 h1:yloc84fytn4zmJX2GU3TkXGsaieaV7dQ057Qs4sIG2Y= +cel.dev/expr v0.16.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= +github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= +github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cheggaaa/pb/v3 v3.1.5 h1:QuuUzeM2WsAqG2gMqtzaWithDJv0i+i6UlnwSCI4QLk= +github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6jM60XI= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cyphar/filepath-securejoin v0.3.1 h1:1V7cHiaW+C+39wEfpH6XlLBQo3j/PciWFrgfCLS8XrE= +github.com/cyphar/filepath-securejoin v0.3.1/go.mod h1:F7i41x/9cBF7lzCrVsYs9fuzwRZm4NQsGTBdpp6mETc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= +github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.13.1-0.20241009135036-bec043f2e850 h1:wXrFiquLgpbofJ80z0RUZxsKKIaRU1JZ6j4HSaibx4E= +github.com/envoyproxy/go-control-plane v0.13.1-0.20241009135036-bec043f2e850/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= +github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= +github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI= +github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A= +github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= +github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= +github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= +github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= +github.com/lestrrat-go/jwx v1.2.30 h1:VKIFrmjYn0z2J51iLPadqoHIVLzvWNa1kCsTqNDHYPA= +github.com/lestrrat-go/jwx v1.2.30/go.mod h1:vMxrwFhunGZ3qddmfmEm2+uced8MSI6QFWGTKygjSzQ= +github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= +github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= +github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo= +github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= +github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= +github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240409071808-615f978279ca h1:ujRGEVWJEoaxQ+8+HMl8YEpGaDAgohgZxJ5S+d2TTFQ= +github.com/planetscale/vtprotobuf v0.6.1-0.20240409071808-615f978279ca/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/prometheus v0.54.1 h1:vKuwQNjnYN2/mDoWfHXDhAsz/68q/dQDb+YbcEqU7MQ= +github.com/prometheus/prometheus v0.54.1/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg= +github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +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/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= +go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0/go.mod h1:KQsVNh4OjgjTG0G6EiNi1jVpnaeeKsKMRwbLN+f1+8M= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 h1:m0yTiGDLUvVYaTFbAvCkVYIYcvwKt3G7OLoN77NUs/8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0/go.mod h1:wBQbT4UekBfegL2nx0Xk1vBcnzyBPsIVm9hRG4fYcr4= +go.opentelemetry.io/otel/exporters/prometheus v0.52.0 h1:kmU3H0b9ufFSi8IQCcxack+sWUblKkFbqWYs6YiACGQ= +go.opentelemetry.io/otel/exporters/prometheus v0.52.0/go.mod h1:+wsAp2+JhuGXX7YRkjlkx6hyWY3ogFPfNA4x3nyiAh0= +go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= +go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= +go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE= +go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg= +go.opentelemetry.io/otel/sdk/metric v1.30.0 h1:QJLT8Pe11jyHBHfSAgYH7kEmT24eX792jZO1bo4BXkM= +go.opentelemetry.io/otel/sdk/metric v1.30.0/go.mod h1:waS6P3YqFNzeP01kuo/MBBYqaoBJl7efRQHOaydhy1Y= +go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= +go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +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/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f h1:jTm13A2itBi3La6yTGqn8bVSrc3ZZ1r8ENHlIXBfnRA= +google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f/go.mod h1:CLGoBuH1VHxAUXVPP8FfPwPEVJB6lz3URE5mY2SuayE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f h1:cUMEy+8oS78BWIH9OWazBkzbr090Od9tWBNtZHkOhf0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +helm.sh/helm/v3 v3.16.1 h1:cER6tI/8PgUAsaJaQCVBUg3VI9KN4oVaZJgY60RIc0c= +helm.sh/helm/v3 v3.16.1/go.mod h1:r+xBHHP20qJeEqtvBXMf7W35QDJnzY/eiEBzt+TfHps= +istio.io/api v1.23.3 h1:+CP0AHz8/+WJ7ZKJLbilHEiqBCi5KLe1Yil9bJI39ow= +istio.io/api v1.23.3/go.mod h1:QPSTGXuIQdnZFEm3myf9NZ5uBMwCdJWUvfj9ZZ+2oBM= +istio.io/api v1.24.0 h1:KH1Xxha9HAFekQILzdHpRF9AB4RY13/Sdi3rmHQWoQI= +istio.io/api v1.24.0/go.mod h1:MQnRok7RZ20/PE56v0LxmoWH0xVxnCQPNuf9O7PAN1I= +istio.io/client-go v1.24.0-rc.0.0.20241101201152-acb34243979a h1:PRcst3KhVrkcTlDAMPFz7Mi3MmELCIknfJuc7FhiqJs= +istio.io/client-go v1.24.0-rc.0.0.20241101201152-acb34243979a/go.mod h1:sCDBDJWQGJQz/1t3CHwUTDE5V7Nk6pFFkqBwhIg+LrI= +istio.io/istio v0.0.0-20241105211601-8825a6b7f8c9 h1:TL8KZXIOBxGUFJYvyqVkBK8B9D1tx2uCrxKNz3BfPtk= +istio.io/istio v0.0.0-20241105211601-8825a6b7f8c9/go.mod h1:WPuuRxVLJBE4gdDc/ZfEn+Rar1azZ2Am6qWBxUfh6r0= +k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= +k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= +k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= +k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= +k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= +k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c= +k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM= +k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= +k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= +k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= +k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108 h1:Q8Z7VlGhcJgBHJHYugJ/K/7iB8a2eSxCyxdVjJp+lLY= +k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI= +k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= +sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/gateway-api v1.2.0 h1:LrToiFwtqKTKZcZtoQPTuo3FxhrrhTgzQG0Te+YGSo8= +sigs.k8s.io/gateway-api v1.2.0/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/mcs-api v0.1.1-0.20240624222831-d7001fe1d21c h1:F7hIEutAxtXDOQX9NXFdvhWmWETu2zmUPHuPPcAez7g= +sigs.k8s.io/mcs-api v0.1.1-0.20240624222831-d7001fe1d21c/go.mod h1:DPFniRsBzCeLB4ANjlPEvQQt9QGIX489d1faK+GPvI4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/manifests/charts/istio-operator/crds/crd-operator.yaml b/manifests/charts/istio-operator/crds/crd-operator.yaml new file mode 100644 index 0000000..93ac1de --- /dev/null +++ b/manifests/charts/istio-operator/crds/crd-operator.yaml @@ -0,0 +1,48 @@ +# SYNC WITH manifests/charts/base/files +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: istiooperators.install.istio.io + labels: + release: istio +spec: + conversion: + strategy: None + group: install.istio.io + names: + kind: IstioOperator + listKind: IstioOperatorList + plural: istiooperators + singular: istiooperator + shortNames: + - iop + - io + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Istio control plane revision + jsonPath: .spec.revision + name: Revision + type: string + - description: IOP current state + jsonPath: .status.status + name: Status + type: string + - description: 'CreationTimestamp is a timestamp representing the server time + when this object was created. It is not guaranteed to be set in happens-before + order across separate operations. Clients may not set this value. It is represented + in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for + lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + subresources: + status: {} + schema: + openAPIV3Schema: + type: object + x-kubernetes-preserve-unknown-fields: true + served: true + storage: true +--- diff --git a/operator/pkg/apis/addtoscheme_istio_v1alpha1.go b/operator/pkg/apis/addtoscheme_istio_v1alpha1.go new file mode 100644 index 0000000..4e51515 --- /dev/null +++ b/operator/pkg/apis/addtoscheme_istio_v1alpha1.go @@ -0,0 +1,24 @@ +// Copyright Istio 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 apis + +import ( + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme) +} diff --git a/operator/pkg/apis/apis.go b/operator/pkg/apis/apis.go new file mode 100644 index 0000000..14bf66d --- /dev/null +++ b/operator/pkg/apis/apis.go @@ -0,0 +1,27 @@ +// Copyright Istio 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 apis + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +// AddToSchemes may be used to add all resources defined in the project to a Scheme +var AddToSchemes runtime.SchemeBuilder + +// AddToScheme adds all Resources to the Scheme +func AddToScheme(s *runtime.Scheme) error { + return AddToSchemes.AddToScheme(s) +} diff --git a/operator/pkg/apis/istio/common.go b/operator/pkg/apis/istio/common.go new file mode 100644 index 0000000..388e1c0 --- /dev/null +++ b/operator/pkg/apis/istio/common.go @@ -0,0 +1,55 @@ +// Copyright Istio 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 istio + +import ( + "fmt" + + "sigs.k8s.io/yaml" + + "istio.io/api/123/operator/v1alpha1" + operator_v1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/validate" +) + +// UnmarshalAndValidateIOPS unmarshals a string containing IstioOperator YAML, validates it, and returns a struct +// representation if successful. In case of validation errors, it returns both the IstioOperatorSpec struct and +// an error, so the caller can decide how to handle it. +func UnmarshalAndValidateIOPS(iopsYAML string) (*v1alpha1.IstioOperatorSpec, error) { + iops := &v1alpha1.IstioOperatorSpec{} + if err := util.UnmarshalWithJSONPB(iopsYAML, iops, false); err != nil { + return nil, fmt.Errorf("could not unmarshal the merged YAML: %s\n\nYAML:\n%s", err, iopsYAML) + } + if errs := validate.CheckIstioOperatorSpec(iops, true); len(errs) != 0 { + return iops, fmt.Errorf(errs.Error()) + } + return iops, nil +} + +// UnmarshalIstioOperator unmarshals a string containing IstioOperator YAML. +func UnmarshalIstioOperator(iopYAML string, allowUnknownField bool) (*operator_v1alpha1.IstioOperator, error) { + iop := &operator_v1alpha1.IstioOperator{} + if allowUnknownField { + if err := yaml.Unmarshal([]byte(iopYAML), iop); err != nil { + return nil, fmt.Errorf("could not unmarshal: %v", err) + } + } else { + if err := yaml.UnmarshalStrict([]byte(iopYAML), iop); err != nil { + return nil, fmt.Errorf("could not unmarshal: %v", err) + } + } + return iop, nil +} diff --git a/operator/pkg/apis/istio/v1alpha1/common.go b/operator/pkg/apis/istio/v1alpha1/common.go new file mode 100644 index 0000000..ab6b809 --- /dev/null +++ b/operator/pkg/apis/istio/v1alpha1/common.go @@ -0,0 +1,52 @@ +// Copyright Istio 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 v1alpha1 + +import ( + "istio.io/api/123/operator/v1alpha1" +) + +const ( + globalKey = "global" + istioNamespaceKey = "istioNamespace" +) + +// Namespace returns the namespace of the containing CR. +func Namespace(iops *v1alpha1.IstioOperatorSpec) string { + if iops.Namespace != "" { + return iops.Namespace + } + if iops.Values == nil { + return "" + } + v := iops.Values.AsMap() + if v[globalKey] == nil { + return "" + } + vg := v[globalKey].(map[string]any) + n := vg[istioNamespaceKey] + if n == nil { + return "" + } + return n.(string) +} + +// SetNamespace returns the namespace of the containing CR. +func SetNamespace(iops *v1alpha1.IstioOperatorSpec, namespace string) { + if namespace != "" { + iops.Namespace = namespace + } + // TODO implement +} diff --git a/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go b/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go new file mode 100644 index 0000000..a8c7839 --- /dev/null +++ b/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go @@ -0,0 +1,143 @@ +// Copyright Istio 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 v1alpha1_test + +import ( + "os" + "testing" + + "github.com/google/go-cmp/cmp" + "google.golang.org/protobuf/testing/protocmp" + "sigs.k8s.io/yaml" + + v1alpha12 "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/apis/istio" + install "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" +) + +// This is to verify that certain proto types handle marshal and unmarshal properly +func TestIstioOperatorSpec_DeepCopy(t *testing.T) { + x := &v1alpha12.ResourceMetricSource{} + err := yaml.UnmarshalStrict([]byte("targetAverageValue: 100m"), x) + t.Log(x) + if err != nil { + t.Fatal(err) + } + y := &v1alpha12.MetricSpec{} + err = yaml.UnmarshalStrict([]byte(` +type: Resource +resource: + targetAverageValue: 100m`), y) + t.Log(y) + if err != nil { + t.Fatal(err) + } + z := &v1alpha12.HorizontalPodAutoscalerSpec{} + err = yaml.UnmarshalStrict([]byte(`metrics: +- type: Resource + resource: + targetAverageValue: 100m`), z) + t.Log(z) + if err != nil { + t.Fatal(err) + } + fa := &install.IstioOperator{} + err = yaml.UnmarshalStrict([]byte(`apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +metadata: + namespace: istio-system + name: example-istiocontrolplane +spec: + profile: demo + components: + pilot: + k8s: + hpaSpec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: istiod + minReplicas: 1 + maxReplicas: 5 + metrics: + - type: Resource + resource: + name: cpu + targetAverageValue: 100m +`), fa) + t.Log(fa) + if err != nil { + t.Error(err) + } + f := &install.IstioOperator{} + err = yaml.UnmarshalStrict([]byte(`apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +metadata: + namespace: istio-system + name: example-istiocontrolplane +spec: + profile: demo + components: + pilot: + k8s: + hpaSpec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: istiod + minReplicas: 1 + maxReplicas: 5 + metrics: + - type: Resource + resource: + name: cpu + targetAverageValue: 100m +`), f) + t.Log(f) + if err != nil { + t.Fatal(err) + } + tests := []struct { + name string + iop string + }{ + { + name: "handles Kubernetes quantity types", + iop: "testdata/quantity.yaml", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := loadResource(t, tt.iop) + gotSpec := m.Spec.DeepCopy() + if diff := cmp.Diff(m.Spec, gotSpec, protocmp.Transform()); diff != "" { + t.Error(diff) + } + }) + } +} + +func loadResource(t *testing.T, filepath string) *install.IstioOperator { + t.Helper() + contents, err := os.ReadFile(filepath) + if err != nil { + t.Fatal(err) + } + resource, err := istio.UnmarshalIstioOperator(string(contents), false) + if err != nil { + t.Fatal(err) + } + return resource +} diff --git a/operator/pkg/apis/istio/v1alpha1/register.go b/operator/pkg/apis/istio/v1alpha1/register.go new file mode 100644 index 0000000..3e86432 --- /dev/null +++ b/operator/pkg/apis/istio/v1alpha1/register.go @@ -0,0 +1,51 @@ +// Copyright Istio 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. + +// NOTE: Boilerplate only. Ignore this file. + +// Package v1alpha1 contains API Schema definitions for the istio v1alpha1 API group +// +k8s:deepcopy-gen=package,register +// +groupName=install.istio.io +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // IstioOperatorGVK is GVK for IstioOperator + IstioOperatorGVK = schema.GroupVersionKind{ + Version: "v1alpha1", + Group: "install.istio.io", + Kind: "IstioOperator", + } + + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = IstioOperatorGVK.GroupVersion() + + IstioOperatorGVR = schema.GroupVersionResource{ + Group: SchemeGroupVersion.Group, + Version: SchemeGroupVersion.Version, + Resource: "istiooperators", + } + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} +) + +// Register the IstioOperator and IstioOperatorList API kind +func init() { + SchemeBuilder.Register(&IstioOperator{}, &IstioOperatorList{}) +} diff --git a/operator/pkg/apis/istio/v1alpha1/testdata/quantity.yaml b/operator/pkg/apis/istio/v1alpha1/testdata/quantity.yaml new file mode 100644 index 0000000..05e33f2 --- /dev/null +++ b/operator/pkg/apis/istio/v1alpha1/testdata/quantity.yaml @@ -0,0 +1,24 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +metadata: + namespace: istio-system + name: example-istiocontrolplane +spec: + profile: demo + components: + pilot: + k8s: + hpaSpec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: istiod + minReplicas: 1 + maxReplicas: 5 + metrics: + - type: Resource + resource: + name: cpu + target: + type: AverageValue + averageValue: 100m diff --git a/operator/pkg/apis/istio/v1alpha1/types.go b/operator/pkg/apis/istio/v1alpha1/types.go new file mode 100644 index 0000000..c584b0e --- /dev/null +++ b/operator/pkg/apis/istio/v1alpha1/types.go @@ -0,0 +1,71 @@ +// Copyright Istio 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. + +// Code generated by kubetype-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + operatorv1alpha1 "istio.io/api/123/operator/v1alpha1" +) + +// +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// IstioOperatorSpec defines the desired installed state of Istio components. +// The spec is a used to define a customization of the default profile values that are supplied with each Istio release. +// Because the spec is a customization API, specifying an empty IstioOperatorSpec results in a default Istio +// component values. +// +// apiVersion: install.istio.io/v1alpha1 +// kind: IstioOperator +// spec: +// profile: default +// hub: gcr.io/istio-testing +// tag: latest +// revision: 1-8-0 +// meshConfig: +// accessLogFile: /dev/stdout +// enableTracing: true +// components: +// egressGateways: +// - name: istio-egressgateway +// enabled: true +// +// +kubetype-gen +// +kubetype-gen:groupVersion=install.istio.io/v1alpha1 +// +k8s:deepcopy-gen=true +type IstioOperator struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Spec defines the implementation of this definition. + // +optional + Spec *operatorv1alpha1.IstioOperatorSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` + + Status *operatorv1alpha1.InstallStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// IstioOperatorSpecList is a collection of IstioOperatorSpecs. +type IstioOperatorList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + Items []IstioOperator `json:"items" protobuf:"bytes,2,rep,name=items"` +} diff --git a/operator/pkg/apis/istio/v1alpha1/validation/validation.go b/operator/pkg/apis/istio/v1alpha1/validation/validation.go new file mode 100644 index 0000000..5acfd35 --- /dev/null +++ b/operator/pkg/apis/istio/v1alpha1/validation/validation.go @@ -0,0 +1,358 @@ +// Copyright Istio 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 validation + +import ( + "errors" + "fmt" + "reflect" + "strings" + "unicode" + + wrappers "google.golang.org/protobuf/types/known/wrapperspb" + "k8s.io/apimachinery/pkg/util/intstr" + + "istio.io/api/123/operator/v1alpha1" + valuesv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" +) + +const ( + validationMethodName = "Validate" +) + +type deprecatedSettings struct { + old string + new string + // In ordered to distinguish between unset for non-pointer values, we need to specify the default value + def any +} + +// ValidateConfig calls validation func for every defined element in Values +func ValidateConfig(failOnMissingValidation bool, iopls *v1alpha1.IstioOperatorSpec) (util.Errors, string) { + var validationErrors util.Errors + var warningMessages []string + iopvalString := util.ToYAMLWithJSONPB(iopls.Values) + values := &valuesv1alpha1.Values{} + if err := util.UnmarshalWithJSONPB(iopvalString, values, true); err != nil { + return util.NewErrs(err), "" + } + + validationErrors = util.AppendErrs(validationErrors, ValidateSubTypes(reflect.ValueOf(values).Elem(), failOnMissingValidation, values, iopls)) + + featureErrors, featureWarningMessages := validateFeatures(values, iopls) + validationErrors = util.AppendErrs(validationErrors, featureErrors) + warningMessages = append(warningMessages, featureWarningMessages...) + + deprecatedErrors, deprecatedWarningMessages := checkDeprecatedSettings(iopls) + if deprecatedErrors != nil { + validationErrors = util.AppendErr(validationErrors, deprecatedErrors) + } + warningMessages = append(warningMessages, deprecatedWarningMessages...) + return validationErrors, strings.Join(warningMessages, "\n") +} + +// Converts from struct paths to helm paths +// Global.Proxy.AccessLogFormat -> global.proxy.accessLogFormat +func firstCharsToLower(s string) string { + // Use a closure here to remember state. + // Hackish but effective. Depends on Map scanning in order and calling + // the closure once per rune. + prev := '.' + return strings.Map( + func(r rune) rune { + if prev == '.' { + prev = r + return unicode.ToLower(r) + } + prev = r + return r + }, + s) +} + +func checkDeprecatedSettings(iop *v1alpha1.IstioOperatorSpec) (util.Errors, []string) { + var errs util.Errors + messages := []string{} + warningSettings := []deprecatedSettings{ + {"Values.global.certificates", "meshConfig.certificates", nil}, + {"Values.global.outboundTrafficPolicy", "meshConfig.outboundTrafficPolicy", nil}, + {"Values.global.localityLbSetting", "meshConfig.localityLbSetting", nil}, + {"Values.global.policyCheckFailOpen", "meshConfig.policyCheckFailOpen", false}, + {"Values.global.enableTracing", "meshConfig.enableTracing", false}, + {"Values.global.proxy.accessLogFormat", "meshConfig.accessLogFormat", ""}, + {"Values.global.proxy.accessLogFile", "meshConfig.accessLogFile", ""}, + {"Values.global.proxy.concurrency", "meshConfig.defaultConfig.concurrency", uint32(0)}, + {"Values.global.proxy.envoyAccessLogService", "meshConfig.defaultConfig.envoyAccessLogService", nil}, + {"Values.global.proxy.envoyAccessLogService.enabled", "meshConfig.enableEnvoyAccessLogService", nil}, + {"Values.global.proxy.envoyMetricsService", "meshConfig.defaultConfig.envoyMetricsService", nil}, + {"Values.global.proxy.protocolDetectionTimeout", "meshConfig.protocolDetectionTimeout", ""}, + {"Values.global.proxy.holdApplicationUntilProxyStarts", "meshConfig.defaultConfig.holdApplicationUntilProxyStarts", false}, + {"Values.pilot.ingress", "meshConfig.ingressService, meshConfig.ingressControllerMode, and meshConfig.ingressClass", nil}, + {"Values.global.mtls.enabled", "the PeerAuthentication resource", nil}, + {"Values.global.mtls.auto", "meshConfig.enableAutoMtls", nil}, + {"Values.global.tracer.lightstep.address", "meshConfig.defaultConfig.tracing.lightstep.address", ""}, + {"Values.global.tracer.lightstep.accessToken", "meshConfig.defaultConfig.tracing.lightstep.accessToken", ""}, + {"Values.global.tracer.zipkin.address", "meshConfig.defaultConfig.tracing.zipkin.address", nil}, + {"Values.global.tracer.datadog.address", "meshConfig.defaultConfig.tracing.datadog.address", ""}, + {"Values.global.meshExpansion.enabled", "Gateway and other Istio networking resources, such as in samples/multicluster/", false}, + {"Values.gateways.istio-ingressgateway.meshExpansionPorts", "components.ingressGateways[name=istio-ingressgateway].k8s.service.ports", nil}, + {"AddonComponents.istiocoredns.Enabled", "the in-proxy DNS capturing (ISTIO_META_DNS_CAPTURE)", false}, + {"Values.istiocoredns.enabled", "the in-proxy DNS capturing (ISTIO_META_DNS_CAPTURE)", false}, + // nolint: lll + {"Values.global.jwtPolicy", "Values.global.jwtPolicy=third-party-jwt. See https://istio.io/latest/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for more information", "third-party-jwt"}, + {"Values.global.centralIstiod", "Values.global.externalIstiod", false}, + {"Values.global.arch", "the affinity of k8s settings", nil}, + } + + failHardSettings := []deprecatedSettings{ + {"Values.grafana.enabled", "the samples/addons/ deployments", false}, + {"Values.tracing.enabled", "the samples/addons/ deployments", false}, + {"Values.kiali.enabled", "the samples/addons/ deployments", false}, + {"Values.prometheus.enabled", "the samples/addons/ deployments", false}, + {"AddonComponents.grafana.Enabled", "the samples/addons/ deployments", false}, + {"AddonComponents.tracing.Enabled", "the samples/addons/ deployments", false}, + {"AddonComponents.kiali.Enabled", "the samples/addons/ deployments", false}, + {"AddonComponents.prometheus.Enabled", "the samples/addons/ deployments", false}, + {"Values.global.tracer.stackdriver.debug", "meshConfig.defaultConfig.tracing.stackdriver.debug", false}, + {"Values.global.tracer.stackdriver.maxNumberOfAttributes", "meshConfig.defaultConfig.tracing.stackdriver.maxNumberOfAttributes", 0}, + {"Values.global.tracer.stackdriver.maxNumberOfAnnotations", "meshConfig.defaultConfig.tracing.stackdriver.maxNumberOfAnnotations", 0}, + {"Values.global.tracer.stackdriver.maxNumberOfMessageEvents", "meshConfig.defaultConfig.tracing.stackdriver.maxNumberOfMessageEvents", 0}, + {"telemetry.v2.prometheus.configOverride", "custom configuration", nil}, + {"telemetry.v2.stackdriver.configOverride", "custom configuration", nil}, + {"telemetry.v2.stackdriver.disableOutbound", "custom configuration", nil}, + {"telemetry.v2.stackdriver.outboundAccessLogging", "custom configuration", nil}, + {"meshConfig.defaultConfig.tracing.stackdriver.debug", "Istio supported tracers", false}, + {"meshConfig.defaultConfig.tracing.stackdriver.maxNumberOfAttributes", "Istio supported tracers", 0}, + {"meshConfig.defaultConfig.tracing.stackdriver.maxNumberOfAnnotations", "Istio supported tracers", 0}, + {"meshConfig.defaultConfig.tracing.stackdriver.maxNumberOfMessageEvents", "Istio supported tracers", 0}, + } + + for _, d := range warningSettings { + v, f, _ := tpath.GetFromStructPath(iop, d.old) + if f { + switch t := v.(type) { + // need to do conversion for bool value defined in IstioOperator component spec. + case *wrappers.BoolValue: + v = t.Value + } + if v != d.def { + messages = append(messages, fmt.Sprintf("! %s is deprecated; use %s instead", firstCharsToLower(d.old), d.new)) + } + } + } + for _, d := range failHardSettings { + v, f, _ := tpath.GetFromStructPath(iop, d.old) + if f { + switch t := v.(type) { + // need to do conversion for bool value defined in IstioOperator component spec. + case *wrappers.BoolValue: + v = t.Value + } + if v != d.def { + ms := fmt.Sprintf("! %s is deprecated; use %s instead", firstCharsToLower(d.old), d.new) + errs = util.AppendErr(errs, errors.New(ms+"\n")) + } + } + } + return errs, messages +} + +type FeatureValidator func(*valuesv1alpha1.Values, *v1alpha1.IstioOperatorSpec) (util.Errors, []string) + +// validateFeatures check whether the config semantically make sense. For example, feature X and feature Y can't be enabled together. +func validateFeatures(values *valuesv1alpha1.Values, spec *v1alpha1.IstioOperatorSpec) (errs util.Errors, warnings []string) { + validators := []FeatureValidator{ + CheckServicePorts, + CheckAutoScaleAndReplicaCount, + } + + for _, validator := range validators { + newErrs, newWarnings := validator(values, spec) + errs = util.AppendErrs(errs, newErrs) + warnings = append(warnings, newWarnings...) + } + + return +} + +// CheckAutoScaleAndReplicaCount warns when autoscaleEnabled is true and k8s replicaCount is set. +func CheckAutoScaleAndReplicaCount(values *valuesv1alpha1.Values, spec *v1alpha1.IstioOperatorSpec) (errs util.Errors, warnings []string) { + if values.GetPilot().GetAutoscaleEnabled().GetValue() && spec.GetComponents().GetPilot().GetK8S().GetReplicaCount() > 1 { + warnings = append(warnings, + "components.pilot.k8s.replicaCount should not be set when values.pilot.autoscaleEnabled is true") + } + + validateGateways := func(gateways []*v1alpha1.GatewaySpec, gwType string) { + const format = "components.%sGateways[name=%s].k8s.replicaCount should not be set when values.gateways.istio-%sgateway.autoscaleEnabled is true" + for _, gw := range gateways { + if gw.GetK8S().GetReplicaCount() != 0 { + warnings = append(warnings, fmt.Sprintf(format, gwType, gw.Name, gwType)) + } + } + } + + if values.GetGateways().GetIstioIngressgateway().GetAutoscaleEnabled().GetValue() { + validateGateways(spec.GetComponents().GetIngressGateways(), "ingress") + } + + if values.GetGateways().GetIstioEgressgateway().GetAutoscaleEnabled().GetValue() { + validateGateways(spec.GetComponents().GetEgressGateways(), "egress") + } + + return +} + +// CheckServicePorts validates Service ports. Specifically, this currently +// asserts that all ports will bind to a port number greater than 1024 when not +// running as root. +func CheckServicePorts(values *valuesv1alpha1.Values, spec *v1alpha1.IstioOperatorSpec) (errs util.Errors, warnings []string) { + if !values.GetGateways().GetIstioIngressgateway().GetRunAsRoot().GetValue() { + errs = util.AppendErrs(errs, validateGateways(spec.GetComponents().GetIngressGateways(), "istio-ingressgateway")) + } + if !values.GetGateways().GetIstioEgressgateway().GetRunAsRoot().GetValue() { + errs = util.AppendErrs(errs, validateGateways(spec.GetComponents().GetEgressGateways(), "istio-egressgateway")) + } + for _, raw := range values.GetGateways().GetIstioIngressgateway().GetIngressPorts() { + p := raw.AsMap() + var tp int + if p["targetPort"] != nil { + t, ok := p["targetPort"].(float64) + if !ok { + continue + } + tp = int(t) + } + + rport, ok := p["port"].(float64) + if !ok { + continue + } + portnum := int(rport) + if tp == 0 && portnum > 1024 { + // Target port defaults to port. If its >1024, it is safe. + continue + } + if tp < 1024 { + // nolint: lll + errs = util.AppendErr(errs, fmt.Errorf("port %v is invalid: targetPort is set to %v, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true", portnum, tp)) + } + } + return +} + +func validateGateways(gw []*v1alpha1.GatewaySpec, name string) util.Errors { + // nolint: lll + format := "port %v/%v in gateway %v invalid: targetPort is set to %d, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.%s.runAsRoot=true" + var errs util.Errors + for _, gw := range gw { + for _, p := range gw.GetK8S().GetService().GetPorts() { + tp := 0 + if p == nil { + continue + } + if p.TargetPort != nil && p.TargetPort.Type == int64(intstr.String) { + // Do not validate named ports + continue + } + if p.TargetPort != nil && p.TargetPort.Type == int64(intstr.Int) { + tp = int(p.TargetPort.IntVal.GetValue()) + } + if tp == 0 && p.Port > 1024 { + // Target port defaults to port. If its >1024, it is safe. + continue + } + if tp < 1024 { + errs = util.AppendErr(errs, fmt.Errorf(format, p.Name, p.Port, gw.Name, tp, name)) + } + } + } + return errs +} + +func ValidateSubTypes(e reflect.Value, failOnMissingValidation bool, values *valuesv1alpha1.Values, iopls *v1alpha1.IstioOperatorSpec) util.Errors { + // Dealing with receiver pointer and receiver value + ptr := e + k := e.Kind() + if k == reflect.Ptr || k == reflect.Interface { + e = e.Elem() + } + if !e.IsValid() { + return nil + } + // check for method on value + method := e.MethodByName(validationMethodName) + if !method.IsValid() { + method = ptr.MethodByName(validationMethodName) + } + + var validationErrors util.Errors + if util.IsNilOrInvalidValue(method) { + if failOnMissingValidation { + validationErrors = append(validationErrors, fmt.Errorf("type %s is missing Validation method", e.Type().String())) + } + } else { + r := method.Call([]reflect.Value{reflect.ValueOf(failOnMissingValidation), reflect.ValueOf(values), reflect.ValueOf(iopls)})[0].Interface().(util.Errors) + if len(r) != 0 { + validationErrors = append(validationErrors, r...) + } + } + // If it is not a struct nothing to do, returning previously collected validation errors + if e.Kind() != reflect.Struct { + return validationErrors + } + for i := 0; i < e.NumField(); i++ { + // Corner case of a slice of something, if something is defined type, then process it recursively. + if e.Field(i).Kind() == reflect.Slice { + validationErrors = append(validationErrors, processSlice(e.Field(i), failOnMissingValidation, values, iopls)...) + continue + } + if e.Field(i).Kind() == reflect.Map { + validationErrors = append(validationErrors, processMap(e.Field(i), failOnMissingValidation, values, iopls)...) + continue + } + // Validation is not required if it is not a defined type + if e.Field(i).Kind() != reflect.Interface && e.Field(i).Kind() != reflect.Ptr { + continue + } + val := e.Field(i).Elem() + if util.IsNilOrInvalidValue(val) { + continue + } + validationErrors = append(validationErrors, ValidateSubTypes(e.Field(i), failOnMissingValidation, values, iopls)...) + } + + return validationErrors +} + +func processSlice(e reflect.Value, failOnMissingValidation bool, values *valuesv1alpha1.Values, iopls *v1alpha1.IstioOperatorSpec) util.Errors { + var validationErrors util.Errors + for i := 0; i < e.Len(); i++ { + validationErrors = append(validationErrors, ValidateSubTypes(e.Index(i), failOnMissingValidation, values, iopls)...) + } + + return validationErrors +} + +func processMap(e reflect.Value, failOnMissingValidation bool, values *valuesv1alpha1.Values, iopls *v1alpha1.IstioOperatorSpec) util.Errors { + var validationErrors util.Errors + for _, k := range e.MapKeys() { + v := e.MapIndex(k) + validationErrors = append(validationErrors, ValidateSubTypes(v, failOnMissingValidation, values, iopls)...) + } + + return validationErrors +} diff --git a/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go b/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go new file mode 100644 index 0000000..baf116a --- /dev/null +++ b/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go @@ -0,0 +1,329 @@ +// Copyright Istio 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 validation_test + +import ( + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + wrappers "google.golang.org/protobuf/types/known/wrapperspb" + + v1alpha12 "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1/validation" + "github.com/jehawley/istio/operator/pkg/helm" + "github.com/jehawley/istio/operator/pkg/manifest" + "github.com/jehawley/istio/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util/clog" + "istio.io/istio/pkg/test/env" +) + +const operatorSubdirFilePath = "manifests" + +// nolint: lll +func TestValidateConfig(t *testing.T) { + tests := []struct { + name string + value *v1alpha12.IstioOperatorSpec + values string + errors string + warnings string + }{ + { + name: "addons", + value: &v1alpha12.IstioOperatorSpec{ + AddonComponents: map[string]*v1alpha12.ExternalComponentSpec{ + "grafana": { + Enabled: &wrappers.BoolValue{Value: true}, + }, + }, + Values: util.MustStruct(map[string]any{ + "grafana": map[string]any{ + "enabled": true, + }, + }), + }, + errors: `! values.grafana.enabled is deprecated; use the samples/addons/ deployments instead +, ! addonComponents.grafana.enabled is deprecated; use the samples/addons/ deployments instead +`, + }, + { + name: "global", + value: &v1alpha12.IstioOperatorSpec{ + Values: util.MustStruct(map[string]any{ + "global": map[string]any{ + "localityLbSetting": map[string]any{"foo": "bar"}, + }, + }), + }, + warnings: `! values.global.localityLbSetting is deprecated; use meshConfig.localityLbSetting instead`, + }, + { + name: "unset target port", + values: ` +components: + ingressGateways: + - name: istio-ingressgateway + enabled: true + - name: cluster-local-gateway + enabled: true + k8s: + service: + type: ClusterIP + ports: + - port: 15020 + name: status-port + - port: 80 + name: http2 +`, + errors: `port http2/80 in gateway cluster-local-gateway invalid: targetPort is set to 0, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true`, + }, + { + name: "explicitly invalid target port", + values: ` +components: + ingressGateways: + - name: istio-ingressgateway + enabled: true + - name: cluster-local-gateway + enabled: true + k8s: + service: + type: ClusterIP + ports: + - port: 15020 + name: status-port + - port: 80 + name: http2 + targetPort: 90 +`, + errors: `port http2/80 in gateway cluster-local-gateway invalid: targetPort is set to 90, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true`, + }, + { + name: "explicitly invalid target port for egress", + values: ` +components: + egressGateways: + - name: egress-gateway + enabled: true + k8s: + service: + type: ClusterIP + ports: + - port: 15020 + name: status-port + - port: 80 + name: http2 + targetPort: 90 +`, + errors: `port http2/80 in gateway egress-gateway invalid: targetPort is set to 90, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-egressgateway.runAsRoot=true`, + }, + { + name: "low target port with root", + values: ` +components: + ingressGateways: + - name: istio-ingressgateway + enabled: true + - name: cluster-local-gateway + enabled: true + k8s: + service: + type: ClusterIP + ports: + - port: 15020 + name: status-port + - port: 80 + name: http2 + targetPort: 90 +values: + gateways: + istio-ingressgateway: + runAsRoot: true +`, + errors: ``, + }, + { + name: "legacy values ports config empty targetPort", + values: ` +values: + gateways: + istio-ingressgateway: + ingressPorts: + - name: http + port: 80 +`, + errors: `port 80 is invalid: targetPort is set to 0, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true`, + }, + { + name: "legacy values ports config explicit targetPort", + values: ` +values: + gateways: + istio-ingressgateway: + ingressPorts: + - name: http + port: 80 + targetPort: 90 +`, + errors: `port 80 is invalid: targetPort is set to 90, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true`, + }, + { + name: "legacy values ports valid", + values: ` +values: + gateways: + istio-ingressgateway: + ingressPorts: + - name: http + port: 80 + targetPort: 8080 +`, + errors: ``, + }, + { + name: "replicaCount set when autoscaleEnabled is true", + values: ` +values: + pilot: + autoscaleEnabled: true + gateways: + istio-ingressgateway: + autoscaleEnabled: true + istio-egressgateway: + autoscaleEnabled: true +components: + pilot: + k8s: + replicaCount: 2 + ingressGateways: + - name: istio-ingressgateway + enabled: true + k8s: + replicaCount: 2 + egressGateways: + - name: istio-egressgateway + enabled: true + k8s: + replicaCount: 2 +`, + warnings: strings.TrimSpace(` +components.pilot.k8s.replicaCount should not be set when values.pilot.autoscaleEnabled is true +components.ingressGateways[name=istio-ingressgateway].k8s.replicaCount should not be set when values.gateways.istio-ingressgateway.autoscaleEnabled is true +components.egressGateways[name=istio-egressgateway].k8s.replicaCount should not be set when values.gateways.istio-egressgateway.autoscaleEnabled is true +`), + }, + { + name: "pilot.k8s.replicaCount is default value set when autoscaleEnabled is true", + values: ` +values: + pilot: + autoscaleEnabled: true + gateways: + istio-ingressgateway: + autoscaleEnabled: true + istio-egressgateway: + autoscaleEnabled: true +components: + pilot: + k8s: + replicaCount: 1 +`, + warnings: strings.TrimSpace(``), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + iop := tt.value + if tt.values != "" { + iop = &v1alpha12.IstioOperatorSpec{} + if err := util.UnmarshalWithJSONPB(tt.values, iop, true); err != nil { + t.Fatal(err) + } + } + err, warnings := validation.ValidateConfig(false, iop) + if tt.errors != err.String() { + t.Fatalf("expected errors: \n%q\n got: \n%q\n", tt.errors, err.String()) + } + if tt.warnings != warnings { + t.Fatalf("expected warnings: \n%q\n got \n%q\n", tt.warnings, warnings) + } + }) + } +} + +func TestValidateProfiles(t *testing.T) { + manifests := filepath.Join(env.IstioSrc, operatorSubdirFilePath) + profiles, err := helm.ListProfiles(manifests) + if err != nil { + t.Fatal(err) + } + if len(profiles) < 2 { + // Just ensure we find some profiles, in case this code breaks + t.Fatalf("Maybe have failed getting profiles, got %v", profiles) + } + l := clog.NewConsoleLogger(os.Stdout, os.Stderr, nil) + for _, tt := range profiles { + t.Run(tt, func(t *testing.T) { + _, s, err := manifest.GenIOPFromProfile(tt, "", []string{"installPackagePath=" + manifests}, false, false, nil, l) + if err != nil { + t.Fatal(err) + } + verr, warnings := validation.ValidateConfig(false, s.Spec) + if verr != nil { + t.Fatalf("got error validating: %v", verr) + } + if warnings != "" { + t.Fatalf("got warning validating: %v", warnings) + } + }) + } +} + +func TestValidate(t *testing.T) { + tests := []struct { + name string + toValidate *v1alpha1.Values + validated bool + }{ + { + name: "Empty struct", + toValidate: &v1alpha1.Values{}, + validated: true, + }, + { + name: "With CNI defined", + toValidate: &v1alpha1.Values{ + Cni: &v1alpha1.CNIConfig{ + Enabled: &wrappers.BoolValue{Value: true}, + }, + }, + validated: true, + }, + } + + for _, tt := range tests { + err := validation.ValidateSubTypes(reflect.ValueOf(tt.toValidate).Elem(), false, tt.toValidate, nil) + if len(err) != 0 && tt.validated { + t.Fatalf("Test %s failed with errors: %+v but supposed to succeed", tt.name, err) + } + if len(err) == 0 && !tt.validated { + t.Fatalf("Test %s failed as it is supposed to fail but succeeded", tt.name) + } + } +} diff --git a/operator/pkg/apis/istio/v1alpha1/value_types_json.go b/operator/pkg/apis/istio/v1alpha1/value_types_json.go new file mode 100644 index 0000000..ee956ef --- /dev/null +++ b/operator/pkg/apis/istio/v1alpha1/value_types_json.go @@ -0,0 +1,74 @@ +// Copyright Istio 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. + +// Code generated by protoc-gen-golang. DO NOT EDIT. +// source: operator/v1alpha1/operator.proto + +// Configuration affecting Istio control plane installation version and shape. + +package v1alpha1 + +import ( + "encoding/json" + + github_com_golang_protobuf_jsonpb "github.com/golang/protobuf/jsonpb" + "google.golang.org/protobuf/types/known/wrapperspb" + "k8s.io/apimachinery/pkg/util/intstr" +) + +var _ github_com_golang_protobuf_jsonpb.JSONPBUnmarshaler = &IntOrString{} + +// UnmarshalJSON implements the json.Unmarshaller interface. +func (this *IntOrString) UnmarshalJSON(value []byte) error { + if value[0] == '"' { + this.Type = int64(intstr.String) + var s string + err := json.Unmarshal(value, &s) + if err != nil { + return err + } + this.StrVal = &wrapperspb.StringValue{Value: s} + return nil + } + this.Type = int64(intstr.Int) + var s int32 + err := json.Unmarshal(value, &s) + if err != nil { + return err + } + this.IntVal = &wrapperspb.Int32Value{Value: s} + return nil +} + +func (this *IntOrString) MarshalJSONPB(_ *github_com_golang_protobuf_jsonpb.Marshaler) ([]byte, error) { + return this.MarshalJSON() +} + +func (this *IntOrString) MarshalJSON() ([]byte, error) { + if this.IntVal != nil { + return json.Marshal(this.IntVal.GetValue()) + } + return json.Marshal(this.StrVal.GetValue()) +} + +func (this *IntOrString) UnmarshalJSONPB(_ *github_com_golang_protobuf_jsonpb.Unmarshaler, value []byte) error { + return this.UnmarshalJSON(value) +} + +func (this *IntOrString) ToKubernetes() intstr.IntOrString { + if this.IntVal != nil { + return intstr.FromInt32(this.GetIntVal().GetValue()) + } + return intstr.FromString(this.GetStrVal().GetValue()) +} diff --git a/operator/pkg/apis/istio/v1alpha1/values_types.pb.go b/operator/pkg/apis/istio/v1alpha1/values_types.pb.go new file mode 100644 index 0000000..7fa31b0 --- /dev/null +++ b/operator/pkg/apis/istio/v1alpha1/values_types.pb.go @@ -0,0 +1,7445 @@ +// Copyright Istio 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.2 +// protoc (unknown) +// source: pkg/apis/istio/v1alpha1/values_types.proto + +package v1alpha1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + _ "google.golang.org/protobuf/types/known/anypb" + durationpb "google.golang.org/protobuf/types/known/durationpb" + structpb "google.golang.org/protobuf/types/known/structpb" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" + v1 "k8s.io/api/core/v1" + v11 "k8s.io/apimachinery/pkg/apis/meta/v1" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Mode for the ingress controller. +type IngressControllerMode int32 + +const ( + // Unspecified Istio ingress controller. + IngressControllerMode_UNSPECIFIED IngressControllerMode = 0 + // Selects all Ingress resources, with or without Istio annotation. + IngressControllerMode_DEFAULT IngressControllerMode = 1 + // Selects only resources with istio annotation. + IngressControllerMode_STRICT IngressControllerMode = 2 + // No ingress or sync. + IngressControllerMode_OFF IngressControllerMode = 3 +) + +// Enum value maps for IngressControllerMode. +var ( + IngressControllerMode_name = map[int32]string{ + 0: "UNSPECIFIED", + 1: "DEFAULT", + 2: "STRICT", + 3: "OFF", + } + IngressControllerMode_value = map[string]int32{ + "UNSPECIFIED": 0, + "DEFAULT": 1, + "STRICT": 2, + "OFF": 3, + } +) + +func (x IngressControllerMode) Enum() *IngressControllerMode { + p := new(IngressControllerMode) + *p = x + return p +} + +func (x IngressControllerMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (IngressControllerMode) Descriptor() protoreflect.EnumDescriptor { + return file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[0].Descriptor() +} + +func (IngressControllerMode) Type() protoreflect.EnumType { + return &file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[0] +} + +func (x IngressControllerMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use IngressControllerMode.Descriptor instead. +func (IngressControllerMode) EnumDescriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{0} +} + +// Specifies which tracer to use. +type Tracer int32 + +const ( + Tracer_zipkin Tracer = 0 + Tracer_lightstep Tracer = 1 + Tracer_datadog Tracer = 2 + Tracer_stackdriver Tracer = 3 + Tracer_openCensusAgent Tracer = 4 + Tracer_none Tracer = 5 +) + +// Enum value maps for Tracer. +var ( + Tracer_name = map[int32]string{ + 0: "zipkin", + 1: "lightstep", + 2: "datadog", + 3: "stackdriver", + 4: "openCensusAgent", + 5: "none", + } + Tracer_value = map[string]int32{ + "zipkin": 0, + "lightstep": 1, + "datadog": 2, + "stackdriver": 3, + "openCensusAgent": 4, + "none": 5, + } +) + +func (x Tracer) Enum() *Tracer { + p := new(Tracer) + *p = x + return p +} + +func (x Tracer) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Tracer) Descriptor() protoreflect.EnumDescriptor { + return file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[1].Descriptor() +} + +func (Tracer) Type() protoreflect.EnumType { + return &file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[1] +} + +func (x Tracer) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Tracer.Descriptor instead. +func (Tracer) EnumDescriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{1} +} + +// Specifies the sidecar's default behavior when handling outbound traffic from the application. +type OutboundTrafficPolicyConfig_Mode int32 + +const ( + // Outbound traffic to unknown destinations will be allowed, in case there are no services or ServiceEntries for the destination port + OutboundTrafficPolicyConfig_ALLOW_ANY OutboundTrafficPolicyConfig_Mode = 0 + // Restrict outbound traffic to services defined in the service registry as well as those defined through ServiceEntries + OutboundTrafficPolicyConfig_REGISTRY_ONLY OutboundTrafficPolicyConfig_Mode = 1 +) + +// Enum value maps for OutboundTrafficPolicyConfig_Mode. +var ( + OutboundTrafficPolicyConfig_Mode_name = map[int32]string{ + 0: "ALLOW_ANY", + 1: "REGISTRY_ONLY", + } + OutboundTrafficPolicyConfig_Mode_value = map[string]int32{ + "ALLOW_ANY": 0, + "REGISTRY_ONLY": 1, + } +) + +func (x OutboundTrafficPolicyConfig_Mode) Enum() *OutboundTrafficPolicyConfig_Mode { + p := new(OutboundTrafficPolicyConfig_Mode) + *p = x + return p +} + +func (x OutboundTrafficPolicyConfig_Mode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (OutboundTrafficPolicyConfig_Mode) Descriptor() protoreflect.EnumDescriptor { + return file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[2].Descriptor() +} + +func (OutboundTrafficPolicyConfig_Mode) Type() protoreflect.EnumType { + return &file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[2] +} + +func (x OutboundTrafficPolicyConfig_Mode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use OutboundTrafficPolicyConfig_Mode.Descriptor instead. +func (OutboundTrafficPolicyConfig_Mode) EnumDescriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{20, 0} +} + +// ArchConfig specifies the pod scheduling target architecture(amd64, ppc64le, s390x, arm64) +// for all the Istio control plane components. +type ArchConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Sets pod scheduling weight for amd64 arch + Amd64 uint32 `protobuf:"varint,1,opt,name=amd64,proto3" json:"amd64,omitempty"` + // Sets pod scheduling weight for ppc64le arch. + Ppc64Le uint32 `protobuf:"varint,2,opt,name=ppc64le,proto3" json:"ppc64le,omitempty"` + // Sets pod scheduling weight for s390x arch. + S390X uint32 `protobuf:"varint,3,opt,name=s390x,proto3" json:"s390x,omitempty"` + // Sets pod scheduling weight for arm64 arch. + Arm64 uint32 `protobuf:"varint,4,opt,name=arm64,proto3" json:"arm64,omitempty"` +} + +func (x *ArchConfig) Reset() { + *x = ArchConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ArchConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ArchConfig) ProtoMessage() {} + +func (x *ArchConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ArchConfig.ProtoReflect.Descriptor instead. +func (*ArchConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{0} +} + +func (x *ArchConfig) GetAmd64() uint32 { + if x != nil { + return x.Amd64 + } + return 0 +} + +func (x *ArchConfig) GetPpc64Le() uint32 { + if x != nil { + return x.Ppc64Le + } + return 0 +} + +func (x *ArchConfig) GetS390X() uint32 { + if x != nil { + return x.S390X + } + return 0 +} + +func (x *ArchConfig) GetArm64() uint32 { + if x != nil { + return x.Arm64 + } + return 0 +} + +// Configuration for CNI. +type CNIConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether CNI is installed. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // Hub to pull the container image from. Image will be `Hub/Image:Tag-Variant`. + Hub string `protobuf:"bytes,2,opt,name=hub,proto3" json:"hub,omitempty"` + // The container image tag to pull. Image will be `Hub/Image:Tag-Variant`. + Tag *structpb.Value `protobuf:"bytes,3,opt,name=tag,proto3" json:"tag,omitempty"` + // The container image variant to pull. Options are "debug" or "distroless". Unset will use the default for the given version. + Variant string `protobuf:"bytes,29,opt,name=variant,proto3" json:"variant,omitempty"` + // Image name to pull from. Image will be `Hub/Image:Tag-Variant`. + // If Image contains a "/", it will replace the entire `image` in the pod. + Image string `protobuf:"bytes,4,opt,name=image,proto3" json:"image,omitempty"` + // Specifies the image pull policy. one of Always, Never, IfNotPresent. + // Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. + // + // More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + PullPolicy string `protobuf:"bytes,5,opt,name=pullPolicy,proto3" json:"pullPolicy,omitempty"` + // The directory path within the cluster node's filesystem where the CNI binaries are to be installed. Typically /var/lib/cni/bin. + CniBinDir string `protobuf:"bytes,6,opt,name=cniBinDir,proto3" json:"cniBinDir,omitempty"` + // The directory path within the cluster node's filesystem where the CNI configuration files are to be installed. Typically /etc/cni/net.d. + CniConfDir string `protobuf:"bytes,7,opt,name=cniConfDir,proto3" json:"cniConfDir,omitempty"` + // The name of the CNI plugin configuration file. Defaults to istio-cni.conf. + CniConfFileName string `protobuf:"bytes,8,opt,name=cniConfFileName,proto3" json:"cniConfFileName,omitempty"` + // The directory path within the cluster node's filesystem where network namespaces are located. + // Defaults to '/var/run/netns', in minikube/docker/others can be '/var/run/docker/netns'. + CniNetnsDir string `protobuf:"bytes,31,opt,name=cniNetnsDir,proto3" json:"cniNetnsDir,omitempty"` + // List of namespaces that should be ignored by the CNI plugin. + ExcludeNamespaces []string `protobuf:"bytes,9,rep,name=excludeNamespaces,proto3" json:"excludeNamespaces,omitempty"` + // K8s affinity to set on the istio-cni Pods. Can be used to exclude istio-cni from being scheduled on specified nodes. + Affinity *v1.Affinity `protobuf:"bytes,20,opt,name=affinity,proto3" json:"affinity,omitempty"` + // Additional annotations to apply to the istio-cni Pods. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + PodAnnotations *structpb.Struct `protobuf:"bytes,10,opt,name=podAnnotations,proto3" json:"podAnnotations,omitempty"` + // PodSecurityPolicy cluster role. No longer used anywhere. + PspClusterRole string `protobuf:"bytes,11,opt,name=psp_cluster_role,json=pspClusterRole,proto3" json:"psp_cluster_role,omitempty"` + // DEPRECATED. Configuration log level of istio-cni binary. By default, istio-cni sends all logs to the UDS server. + // To see the logs, change global.logging.level to cni:debug. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + LogLevel string `protobuf:"bytes,12,opt,name=logLevel,proto3" json:"logLevel,omitempty"` + // Same as `global.logging.level`, but will override it if set + Logging *GlobalLoggingConfig `protobuf:"bytes,25,opt,name=logging,proto3" json:"logging,omitempty"` + // Configuration for the CNI Repair controller. + Repair *CNIRepairConfig `protobuf:"bytes,13,opt,name=repair,proto3" json:"repair,omitempty"` + // Configure the plugin as a chained CNI plugin. When true, the configuration is added to the CNI chain; when false, + // the configuration is added as a standalone file in the CNI configuration directory. + Chained *wrapperspb.BoolValue `protobuf:"bytes,14,opt,name=chained,proto3" json:"chained,omitempty"` + // The resource quotas configration for the CNI DaemonSet. + ResourceQuotas *ResourceQuotas `protobuf:"bytes,16,opt,name=resource_quotas,json=resourceQuotas,proto3" json:"resource_quotas,omitempty"` + // The k8s resource requests and limits for the istio-cni Pods. + Resources *Resources `protobuf:"bytes,17,opt,name=resources,proto3" json:"resources,omitempty"` + // No longer used for CNI. See: https://github.com/istio/istio/issues/49004 + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Privileged *wrapperspb.BoolValue `protobuf:"bytes,18,opt,name=privileged,proto3" json:"privileged,omitempty"` + // The Container seccompProfile + // + // See: https://kubernetes.io/docs/tutorials/security/seccomp/ + SeccompProfile *v1.SeccompProfile `protobuf:"bytes,19,opt,name=seccompProfile,proto3" json:"seccompProfile,omitempty"` + // Configuration for Istio Ambient. + Ambient *CNIAmbientConfig `protobuf:"bytes,21,opt,name=ambient,proto3" json:"ambient,omitempty"` + // Specifies the CNI provider. Can be either "default" or "multus". When set to "multus", an additional + // NetworkAttachmentDefinition resource is deployed to the cluster to allow the istio-cni plugin to be + // invoked in a cluster using the Multus CNI plugin. + Provider string `protobuf:"bytes,22,opt,name=provider,proto3" json:"provider,omitempty"` + // The number of pods that can be unavailable during a rolling update of the CNI DaemonSet (see + // `updateStrategy.rollingUpdate.maxUnavailable` here: + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/daemon-set-v1/#DaemonSetSpec). + // May be specified as a number of pods or as a percent of the total number + // of pods at the start of the update. + RollingMaxUnavailable *IntOrString `protobuf:"bytes,23,opt,name=rollingMaxUnavailable,proto3" json:"rollingMaxUnavailable,omitempty"` +} + +func (x *CNIConfig) Reset() { + *x = CNIConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CNIConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CNIConfig) ProtoMessage() {} + +func (x *CNIConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CNIConfig.ProtoReflect.Descriptor instead. +func (*CNIConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{1} +} + +func (x *CNIConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *CNIConfig) GetHub() string { + if x != nil { + return x.Hub + } + return "" +} + +func (x *CNIConfig) GetTag() *structpb.Value { + if x != nil { + return x.Tag + } + return nil +} + +func (x *CNIConfig) GetVariant() string { + if x != nil { + return x.Variant + } + return "" +} + +func (x *CNIConfig) GetImage() string { + if x != nil { + return x.Image + } + return "" +} + +func (x *CNIConfig) GetPullPolicy() string { + if x != nil { + return x.PullPolicy + } + return "" +} + +func (x *CNIConfig) GetCniBinDir() string { + if x != nil { + return x.CniBinDir + } + return "" +} + +func (x *CNIConfig) GetCniConfDir() string { + if x != nil { + return x.CniConfDir + } + return "" +} + +func (x *CNIConfig) GetCniConfFileName() string { + if x != nil { + return x.CniConfFileName + } + return "" +} + +func (x *CNIConfig) GetCniNetnsDir() string { + if x != nil { + return x.CniNetnsDir + } + return "" +} + +func (x *CNIConfig) GetExcludeNamespaces() []string { + if x != nil { + return x.ExcludeNamespaces + } + return nil +} + +func (x *CNIConfig) GetAffinity() *v1.Affinity { + if x != nil { + return x.Affinity + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *CNIConfig) GetPodAnnotations() *structpb.Struct { + if x != nil { + return x.PodAnnotations + } + return nil +} + +func (x *CNIConfig) GetPspClusterRole() string { + if x != nil { + return x.PspClusterRole + } + return "" +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *CNIConfig) GetLogLevel() string { + if x != nil { + return x.LogLevel + } + return "" +} + +func (x *CNIConfig) GetLogging() *GlobalLoggingConfig { + if x != nil { + return x.Logging + } + return nil +} + +func (x *CNIConfig) GetRepair() *CNIRepairConfig { + if x != nil { + return x.Repair + } + return nil +} + +func (x *CNIConfig) GetChained() *wrapperspb.BoolValue { + if x != nil { + return x.Chained + } + return nil +} + +func (x *CNIConfig) GetResourceQuotas() *ResourceQuotas { + if x != nil { + return x.ResourceQuotas + } + return nil +} + +func (x *CNIConfig) GetResources() *Resources { + if x != nil { + return x.Resources + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *CNIConfig) GetPrivileged() *wrapperspb.BoolValue { + if x != nil { + return x.Privileged + } + return nil +} + +func (x *CNIConfig) GetSeccompProfile() *v1.SeccompProfile { + if x != nil { + return x.SeccompProfile + } + return nil +} + +func (x *CNIConfig) GetAmbient() *CNIAmbientConfig { + if x != nil { + return x.Ambient + } + return nil +} + +func (x *CNIConfig) GetProvider() string { + if x != nil { + return x.Provider + } + return "" +} + +func (x *CNIConfig) GetRollingMaxUnavailable() *IntOrString { + if x != nil { + return x.RollingMaxUnavailable + } + return nil +} + +type CNIUsageConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether CNI should be used. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Chained *wrapperspb.BoolValue `protobuf:"bytes,2,opt,name=chained,proto3" json:"chained,omitempty"` + // Specifies the CNI provider. Can be either "default" or "multus". When set to "multus", an annotation + // `k8s.v1.cni.cncf.io/networks` is set on injected pods to point to a NetworkAttachmentDefinition + Provider string `protobuf:"bytes,3,opt,name=provider,proto3" json:"provider,omitempty"` +} + +func (x *CNIUsageConfig) Reset() { + *x = CNIUsageConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CNIUsageConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CNIUsageConfig) ProtoMessage() {} + +func (x *CNIUsageConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CNIUsageConfig.ProtoReflect.Descriptor instead. +func (*CNIUsageConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{2} +} + +func (x *CNIUsageConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *CNIUsageConfig) GetChained() *wrapperspb.BoolValue { + if x != nil { + return x.Chained + } + return nil +} + +func (x *CNIUsageConfig) GetProvider() string { + if x != nil { + return x.Provider + } + return "" +} + +type CNIAmbientConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether ambient redirection is enabled + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // The directory path containing the configuration files for Ambient. Defaults to /etc/ambient-config. + ConfigDir string `protobuf:"bytes,3,opt,name=configDir,proto3" json:"configDir,omitempty"` + // If enabled, and ambient is enabled, DNS redirection will be enabled. + DnsCapture *wrapperspb.BoolValue `protobuf:"bytes,5,opt,name=dnsCapture,proto3" json:"dnsCapture,omitempty"` + // UNSTABLE: If enabled, and ambient is enabled, enables ipv6 support + Ipv6 *wrapperspb.BoolValue `protobuf:"bytes,7,opt,name=ipv6,proto3" json:"ipv6,omitempty"` +} + +func (x *CNIAmbientConfig) Reset() { + *x = CNIAmbientConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CNIAmbientConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CNIAmbientConfig) ProtoMessage() {} + +func (x *CNIAmbientConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CNIAmbientConfig.ProtoReflect.Descriptor instead. +func (*CNIAmbientConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{3} +} + +func (x *CNIAmbientConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *CNIAmbientConfig) GetConfigDir() string { + if x != nil { + return x.ConfigDir + } + return "" +} + +func (x *CNIAmbientConfig) GetDnsCapture() *wrapperspb.BoolValue { + if x != nil { + return x.DnsCapture + } + return nil +} + +func (x *CNIAmbientConfig) GetIpv6() *wrapperspb.BoolValue { + if x != nil { + return x.Ipv6 + } + return nil +} + +type CNIRepairConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether repair behavior is enabled. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // Hub to pull the container image from. Image will be `Hub/Image:Tag-Variant`. + Hub string `protobuf:"bytes,2,opt,name=hub,proto3" json:"hub,omitempty"` + // The container image tag to pull. Image will be `Hub/Image:Tag-Variant`. + Tag *structpb.Value `protobuf:"bytes,3,opt,name=tag,proto3" json:"tag,omitempty"` + // Image name to pull from. Image will be `Hub/Image:Tag-Variant`. + // If Image contains a "/", it will replace the entire `image` in the pod. + Image string `protobuf:"bytes,4,opt,name=image,proto3" json:"image,omitempty"` + // The Repair controller has 3 modes (labelPods, deletePods, and repairPods). Pick which one meets your use cases. Note only one may be used. + // The mode defines the action the controller will take when a pod is detected as broken. + // If labelPods is true, the controller will label all broken pods with =. + // This is only capable of identifying broken pods; the user is responsible for fixing them (generally, by deleting them). + // Note this gives the DaemonSet a relatively high privilege, as modifying pod metadata/status can have wider impacts. + LabelPods bool `protobuf:"varint,5,opt,name=labelPods,proto3" json:"labelPods,omitempty"` + // The Repair controller has 3 modes (labelPods, deletePods, and repairPods). Pick which one meets your use cases. Note only one may be used. + // The mode defines the action the controller will take when a pod is detected as broken. + // If repairPods is true, the controller will dynamically repair any broken pod by setting up the pod networking configuration even after it has started. + // Note the pod will be crashlooping, so this may take a few minutes to become fully functional based on when the retry occurs. + // This requires no RBAC privilege, but will require the CNI agent to run as a privileged pod. + RepairPods bool `protobuf:"varint,11,opt,name=repairPods,proto3" json:"repairPods,omitempty"` + // No longer used. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + CreateEvents string `protobuf:"bytes,6,opt,name=createEvents,proto3" json:"createEvents,omitempty"` + // The Repair controller has 3 modes (labelPods, deletePods, and repairPods). Pick which one meets your use cases. Note only one may be used. + // The mode defines the action the controller will take when a pod is detected as broken. + // If deletePods is true, the controller will delete the broken pod. The pod will then be rescheduled, hopefully onto a node that is fully ready. + // Note this gives the DaemonSet a relatively high privilege, as it can delete any Pod. + DeletePods bool `protobuf:"varint,7,opt,name=deletePods,proto3" json:"deletePods,omitempty"` + // The label key to apply to a broken pod when the controller is in labelPods mode. + BrokenPodLabelKey string `protobuf:"bytes,8,opt,name=brokenPodLabelKey,proto3" json:"brokenPodLabelKey,omitempty"` + // The label value to apply to a broken pod when the controller is in labelPods mode. + BrokenPodLabelValue string `protobuf:"bytes,9,opt,name=brokenPodLabelValue,proto3" json:"brokenPodLabelValue,omitempty"` + // The name of the init container to use for the repairPods mode. + InitContainerName string `protobuf:"bytes,10,opt,name=initContainerName,proto3" json:"initContainerName,omitempty"` +} + +func (x *CNIRepairConfig) Reset() { + *x = CNIRepairConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CNIRepairConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CNIRepairConfig) ProtoMessage() {} + +func (x *CNIRepairConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CNIRepairConfig.ProtoReflect.Descriptor instead. +func (*CNIRepairConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{4} +} + +func (x *CNIRepairConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *CNIRepairConfig) GetHub() string { + if x != nil { + return x.Hub + } + return "" +} + +func (x *CNIRepairConfig) GetTag() *structpb.Value { + if x != nil { + return x.Tag + } + return nil +} + +func (x *CNIRepairConfig) GetImage() string { + if x != nil { + return x.Image + } + return "" +} + +func (x *CNIRepairConfig) GetLabelPods() bool { + if x != nil { + return x.LabelPods + } + return false +} + +func (x *CNIRepairConfig) GetRepairPods() bool { + if x != nil { + return x.RepairPods + } + return false +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *CNIRepairConfig) GetCreateEvents() string { + if x != nil { + return x.CreateEvents + } + return "" +} + +func (x *CNIRepairConfig) GetDeletePods() bool { + if x != nil { + return x.DeletePods + } + return false +} + +func (x *CNIRepairConfig) GetBrokenPodLabelKey() string { + if x != nil { + return x.BrokenPodLabelKey + } + return "" +} + +func (x *CNIRepairConfig) GetBrokenPodLabelValue() string { + if x != nil { + return x.BrokenPodLabelValue + } + return "" +} + +func (x *CNIRepairConfig) GetInitContainerName() string { + if x != nil { + return x.InitContainerName + } + return "" +} + +// Configuration for the resource quotas for the CNI DaemonSet. +type ResourceQuotas struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether to create resource quotas or not for the CNI DaemonSet. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // The hard limit on the number of pods in the namespace where the CNI DaemonSet is deployed. + Pods int64 `protobuf:"varint,2,opt,name=pods,proto3" json:"pods,omitempty"` +} + +func (x *ResourceQuotas) Reset() { + *x = ResourceQuotas{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResourceQuotas) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResourceQuotas) ProtoMessage() {} + +func (x *ResourceQuotas) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResourceQuotas.ProtoReflect.Descriptor instead. +func (*ResourceQuotas) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{5} +} + +func (x *ResourceQuotas) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *ResourceQuotas) GetPods() int64 { + if x != nil { + return x.Pods + } + return 0 +} + +// Configuration for CPU or memory target utilization for HorizontalPodAutoscaler target. +type TargetUtilizationConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // K8s utilization setting for HorizontalPodAutoscaler target. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + TargetAverageUtilization int32 `protobuf:"varint,1,opt,name=targetAverageUtilization,proto3" json:"targetAverageUtilization,omitempty"` +} + +func (x *TargetUtilizationConfig) Reset() { + *x = TargetUtilizationConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TargetUtilizationConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TargetUtilizationConfig) ProtoMessage() {} + +func (x *TargetUtilizationConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TargetUtilizationConfig.ProtoReflect.Descriptor instead. +func (*TargetUtilizationConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{6} +} + +func (x *TargetUtilizationConfig) GetTargetAverageUtilization() int32 { + if x != nil { + return x.TargetAverageUtilization + } + return 0 +} + +// Compute resources required by a container. +type Resources struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The maximum amount of compute resources allowed. + // More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + Limits map[string]string `protobuf:"bytes,1,rep,name=limits,proto3" json:"limits,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // The minimum amount of compute resources required. If Requests is omitted for a container, + // it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. + // More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + Requests map[string]string `protobuf:"bytes,2,rep,name=requests,proto3" json:"requests,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *Resources) Reset() { + *x = Resources{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resources) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resources) ProtoMessage() {} + +func (x *Resources) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resources.ProtoReflect.Descriptor instead. +func (*Resources) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{7} +} + +func (x *Resources) GetLimits() map[string]string { + if x != nil { + return x.Limits + } + return nil +} + +func (x *Resources) GetRequests() map[string]string { + if x != nil { + return x.Requests + } + return nil +} + +// Mirrors ServiceAccount for unmarshaling. +type ServiceAccount struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Annotations *structpb.Struct `protobuf:"bytes,1,opt,name=annotations,proto3" json:"annotations,omitempty"` +} + +func (x *ServiceAccount) Reset() { + *x = ServiceAccount{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServiceAccount) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServiceAccount) ProtoMessage() {} + +func (x *ServiceAccount) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServiceAccount.ProtoReflect.Descriptor instead. +func (*ServiceAccount) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{8} +} + +func (x *ServiceAccount) GetAnnotations() *structpb.Struct { + if x != nil { + return x.Annotations + } + return nil +} + +// DefaultPodDisruptionBudgetConfig specifies the default pod disruption budget configuration. +// +// See https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ +type DefaultPodDisruptionBudgetConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether a PodDisruptionBudget with a default minAvailable value of 1 is created for each deployment. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` +} + +func (x *DefaultPodDisruptionBudgetConfig) Reset() { + *x = DefaultPodDisruptionBudgetConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DefaultPodDisruptionBudgetConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DefaultPodDisruptionBudgetConfig) ProtoMessage() {} + +func (x *DefaultPodDisruptionBudgetConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DefaultPodDisruptionBudgetConfig.ProtoReflect.Descriptor instead. +func (*DefaultPodDisruptionBudgetConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{9} +} + +func (x *DefaultPodDisruptionBudgetConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +// DefaultResourcesConfig specifies the default k8s resources settings for all Istio control plane components. +type DefaultResourcesConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // k8s resources settings. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + Requests *ResourcesRequestsConfig `protobuf:"bytes,1,opt,name=requests,proto3" json:"requests,omitempty"` +} + +func (x *DefaultResourcesConfig) Reset() { + *x = DefaultResourcesConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DefaultResourcesConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DefaultResourcesConfig) ProtoMessage() {} + +func (x *DefaultResourcesConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DefaultResourcesConfig.ProtoReflect.Descriptor instead. +func (*DefaultResourcesConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{10} +} + +func (x *DefaultResourcesConfig) GetRequests() *ResourcesRequestsConfig { + if x != nil { + return x.Requests + } + return nil +} + +// Configuration for an egress gateway. +type EgressGatewayConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether auto scaling with a HorizontalPodAutoscaler is enabled. + AutoscaleEnabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=autoscaleEnabled,proto3" json:"autoscaleEnabled,omitempty"` + // maxReplicas setting for HorizontalPodAutoscaler. + AutoscaleMax uint32 `protobuf:"varint,2,opt,name=autoscaleMax,proto3" json:"autoscaleMax,omitempty"` + // minReplicas setting for HorizontalPodAutoscaler. + AutoscaleMin uint32 `protobuf:"varint,3,opt,name=autoscaleMin,proto3" json:"autoscaleMin,omitempty"` + // K8s memory utilization setting for HorizontalPodAutoscaler target. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Memory *TargetUtilizationConfig `protobuf:"bytes,4,opt,name=memory,proto3" json:"memory,omitempty"` + // K8s utilization setting for HorizontalPodAutoscaler target. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Cpu *TargetUtilizationConfig `protobuf:"bytes,5,opt,name=cpu,proto3" json:"cpu,omitempty"` + CustomService *wrapperspb.BoolValue `protobuf:"bytes,6,opt,name=customService,proto3" json:"customService,omitempty"` + // Controls whether an egress gateway is enabled. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,7,opt,name=enabled,proto3" json:"enabled,omitempty"` + // Environment variables passed to the proxy container. + Env *structpb.Struct `protobuf:"bytes,8,opt,name=env,proto3" json:"env,omitempty"` + Labels map[string]string `protobuf:"bytes,9,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Name string `protobuf:"bytes,25,opt,name=name,proto3" json:"name,omitempty"` + // K8s node selector. + // + // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + NodeSelector *structpb.Struct `protobuf:"bytes,10,opt,name=nodeSelector,proto3" json:"nodeSelector,omitempty"` + // K8s annotations for pods. + // + // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + PodAnnotations *structpb.Struct `protobuf:"bytes,11,opt,name=podAnnotations,proto3" json:"podAnnotations,omitempty"` + // Pod anti-affinity label selector. + // + // Specify the pod anti-affinity that allows you to constrain which nodes + // your pod is eligible to be scheduled based on labels on pods that are + // already running on the node rather than based on labels on nodes. + // There are currently two types of anti-affinity: + // + // "requiredDuringSchedulingIgnoredDuringExecution" + // "preferredDuringSchedulingIgnoredDuringExecution" + // + // which denote “hard” vs. “soft” requirements, you can define your values + // in "podAntiAffinityLabelSelector" and "podAntiAffinityTermLabelSelector" + // correspondingly. + // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + // + // Examples: + // podAntiAffinityLabelSelector: + // - key: security + // operator: In + // values: S1,S2 + // topologyKey: "kubernetes.io/hostname" + // This pod anti-affinity rule says that the pod requires not to be scheduled + // onto a node if that node is already running a pod with label having key + // “security” and value “S1”. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + PodAntiAffinityLabelSelector []*v11.LabelSelector `protobuf:"bytes,12,rep,name=podAntiAffinityLabelSelector,proto3" json:"podAntiAffinityLabelSelector,omitempty"` + // See PodAntiAffinityLabelSelector. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + PodAntiAffinityTermLabelSelector []*v11.LabelSelector `protobuf:"bytes,13,rep,name=podAntiAffinityTermLabelSelector,proto3" json:"podAntiAffinityTermLabelSelector,omitempty"` + // Ports Configuration for the egress gateway service. + Ports []*PortsConfig `protobuf:"bytes,14,rep,name=ports,proto3" json:"ports,omitempty"` + // K8s resources settings. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Resources *Resources `protobuf:"bytes,15,opt,name=resources,proto3" json:"resources,omitempty"` + // Config for secret volume mounts. + SecretVolumes []*SecretVolume `protobuf:"bytes,16,rep,name=secretVolumes,proto3" json:"secretVolumes,omitempty"` + // Annotations to add to the egress gateway service. + ServiceAnnotations *structpb.Struct `protobuf:"bytes,17,opt,name=serviceAnnotations,proto3" json:"serviceAnnotations,omitempty"` + // Service type. + // + // See https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + Type string `protobuf:"bytes,18,opt,name=type,proto3" json:"type,omitempty"` + // Enables cross-cluster access using SNI matching. + Zvpn *ZeroVPNConfig `protobuf:"bytes,19,opt,name=zvpn,proto3" json:"zvpn,omitempty"` + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Tolerations []*v1.Toleration `protobuf:"bytes,20,rep,name=tolerations,proto3" json:"tolerations,omitempty"` + // K8s rolling update strategy + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + RollingMaxSurge *IntOrString `protobuf:"bytes,21,opt,name=rollingMaxSurge,proto3" json:"rollingMaxSurge,omitempty"` + // The number of pods that can be unavailable during a rolling update (see + // `strategy.rollingUpdate.maxUnavailable` here: + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/#DeploymentSpec). + // May be specified as a number of pods or as a percent of the total number + // of pods at the start of the update. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + RollingMaxUnavailable *IntOrString `protobuf:"bytes,22,opt,name=rollingMaxUnavailable,proto3" json:"rollingMaxUnavailable,omitempty"` + ConfigVolumes []*structpb.Struct `protobuf:"bytes,23,rep,name=configVolumes,proto3" json:"configVolumes,omitempty"` + AdditionalContainers []*structpb.Struct `protobuf:"bytes,24,rep,name=additionalContainers,proto3" json:"additionalContainers,omitempty"` + RunAsRoot *wrapperspb.BoolValue `protobuf:"bytes,26,opt,name=runAsRoot,proto3" json:"runAsRoot,omitempty"` + // The injection template to use for the gateway. If not set, no injection will be performed. + InjectionTemplate string `protobuf:"bytes,27,opt,name=injectionTemplate,proto3" json:"injectionTemplate,omitempty"` + ServiceAccount *ServiceAccount `protobuf:"bytes,28,opt,name=serviceAccount,proto3" json:"serviceAccount,omitempty"` + // Defines which IP family to use for single stack or the order of IP families for dual-stack. + // Valid list items are "IPv4", "IPv6". + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + IpFamilies []string `protobuf:"bytes,29,rep,name=ipFamilies,proto3" json:"ipFamilies,omitempty"` + // Controls whether Services are configured to use IPv4, IPv6, or both. Valid options + // are PreferDualStack, RequireDualStack, and SingleStack. + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + IpFamilyPolicy string `protobuf:"bytes,30,opt,name=ipFamilyPolicy,proto3" json:"ipFamilyPolicy,omitempty"` // Next available 31. +} + +func (x *EgressGatewayConfig) Reset() { + *x = EgressGatewayConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EgressGatewayConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EgressGatewayConfig) ProtoMessage() {} + +func (x *EgressGatewayConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EgressGatewayConfig.ProtoReflect.Descriptor instead. +func (*EgressGatewayConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{11} +} + +func (x *EgressGatewayConfig) GetAutoscaleEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.AutoscaleEnabled + } + return nil +} + +func (x *EgressGatewayConfig) GetAutoscaleMax() uint32 { + if x != nil { + return x.AutoscaleMax + } + return 0 +} + +func (x *EgressGatewayConfig) GetAutoscaleMin() uint32 { + if x != nil { + return x.AutoscaleMin + } + return 0 +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *EgressGatewayConfig) GetMemory() *TargetUtilizationConfig { + if x != nil { + return x.Memory + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *EgressGatewayConfig) GetCpu() *TargetUtilizationConfig { + if x != nil { + return x.Cpu + } + return nil +} + +func (x *EgressGatewayConfig) GetCustomService() *wrapperspb.BoolValue { + if x != nil { + return x.CustomService + } + return nil +} + +func (x *EgressGatewayConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *EgressGatewayConfig) GetEnv() *structpb.Struct { + if x != nil { + return x.Env + } + return nil +} + +func (x *EgressGatewayConfig) GetLabels() map[string]string { + if x != nil { + return x.Labels + } + return nil +} + +func (x *EgressGatewayConfig) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *EgressGatewayConfig) GetNodeSelector() *structpb.Struct { + if x != nil { + return x.NodeSelector + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *EgressGatewayConfig) GetPodAnnotations() *structpb.Struct { + if x != nil { + return x.PodAnnotations + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *EgressGatewayConfig) GetPodAntiAffinityLabelSelector() []*v11.LabelSelector { + if x != nil { + return x.PodAntiAffinityLabelSelector + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *EgressGatewayConfig) GetPodAntiAffinityTermLabelSelector() []*v11.LabelSelector { + if x != nil { + return x.PodAntiAffinityTermLabelSelector + } + return nil +} + +func (x *EgressGatewayConfig) GetPorts() []*PortsConfig { + if x != nil { + return x.Ports + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *EgressGatewayConfig) GetResources() *Resources { + if x != nil { + return x.Resources + } + return nil +} + +func (x *EgressGatewayConfig) GetSecretVolumes() []*SecretVolume { + if x != nil { + return x.SecretVolumes + } + return nil +} + +func (x *EgressGatewayConfig) GetServiceAnnotations() *structpb.Struct { + if x != nil { + return x.ServiceAnnotations + } + return nil +} + +func (x *EgressGatewayConfig) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *EgressGatewayConfig) GetZvpn() *ZeroVPNConfig { + if x != nil { + return x.Zvpn + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *EgressGatewayConfig) GetTolerations() []*v1.Toleration { + if x != nil { + return x.Tolerations + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *EgressGatewayConfig) GetRollingMaxSurge() *IntOrString { + if x != nil { + return x.RollingMaxSurge + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *EgressGatewayConfig) GetRollingMaxUnavailable() *IntOrString { + if x != nil { + return x.RollingMaxUnavailable + } + return nil +} + +func (x *EgressGatewayConfig) GetConfigVolumes() []*structpb.Struct { + if x != nil { + return x.ConfigVolumes + } + return nil +} + +func (x *EgressGatewayConfig) GetAdditionalContainers() []*structpb.Struct { + if x != nil { + return x.AdditionalContainers + } + return nil +} + +func (x *EgressGatewayConfig) GetRunAsRoot() *wrapperspb.BoolValue { + if x != nil { + return x.RunAsRoot + } + return nil +} + +func (x *EgressGatewayConfig) GetInjectionTemplate() string { + if x != nil { + return x.InjectionTemplate + } + return "" +} + +func (x *EgressGatewayConfig) GetServiceAccount() *ServiceAccount { + if x != nil { + return x.ServiceAccount + } + return nil +} + +func (x *EgressGatewayConfig) GetIpFamilies() []string { + if x != nil { + return x.IpFamilies + } + return nil +} + +func (x *EgressGatewayConfig) GetIpFamilyPolicy() string { + if x != nil { + return x.IpFamilyPolicy + } + return "" +} + +// Configuration for gateways. +type GatewaysConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Configuration for an egress gateway. + IstioEgressgateway *EgressGatewayConfig `protobuf:"bytes,1,opt,name=istio_egressgateway,json=istio-egressgateway,proto3" json:"istio_egressgateway,omitempty"` + // Controls whether any gateways are enabled. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,2,opt,name=enabled,proto3" json:"enabled,omitempty"` + // Configuration for an ingress gateway. + IstioIngressgateway *IngressGatewayConfig `protobuf:"bytes,4,opt,name=istio_ingressgateway,json=istio-ingressgateway,proto3" json:"istio_ingressgateway,omitempty"` + SecurityContext *structpb.Value `protobuf:"bytes,10,opt,name=securityContext,proto3" json:"securityContext,omitempty"` + SeccompProfile *structpb.Value `protobuf:"bytes,12,opt,name=seccompProfile,proto3" json:"seccompProfile,omitempty"` +} + +func (x *GatewaysConfig) Reset() { + *x = GatewaysConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GatewaysConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GatewaysConfig) ProtoMessage() {} + +func (x *GatewaysConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GatewaysConfig.ProtoReflect.Descriptor instead. +func (*GatewaysConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{12} +} + +func (x *GatewaysConfig) GetIstioEgressgateway() *EgressGatewayConfig { + if x != nil { + return x.IstioEgressgateway + } + return nil +} + +func (x *GatewaysConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *GatewaysConfig) GetIstioIngressgateway() *IngressGatewayConfig { + if x != nil { + return x.IstioIngressgateway + } + return nil +} + +func (x *GatewaysConfig) GetSecurityContext() *structpb.Value { + if x != nil { + return x.SecurityContext + } + return nil +} + +func (x *GatewaysConfig) GetSeccompProfile() *structpb.Value { + if x != nil { + return x.SeccompProfile + } + return nil +} + +// Global Configuration for Istio components. +type GlobalConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Specifies pod scheduling arch(amd64, ppc64le, s390x, arm64) and weight as follows: + // + // 0 - Never scheduled + // 1 - Least preferred + // 2 - No preference + // 3 - Most preferred + // + // Deprecated: replaced by the affinity k8s settings which allows architecture nodeAffinity configuration of this behavior. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Arch *ArchConfig `protobuf:"bytes,1,opt,name=arch,proto3" json:"arch,omitempty"` + // List of certSigners to allow "approve" action in the ClusterRole + CertSigners []string `protobuf:"bytes,68,rep,name=certSigners,proto3" json:"certSigners,omitempty"` + // Controls whether the server-side validation is enabled. + ConfigValidation *wrapperspb.BoolValue `protobuf:"bytes,3,opt,name=configValidation,proto3" json:"configValidation,omitempty"` + // Default k8s node selector for all the Istio control plane components + // + // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + DefaultNodeSelector *structpb.Struct `protobuf:"bytes,6,opt,name=defaultNodeSelector,proto3" json:"defaultNodeSelector,omitempty"` + // Specifies the default pod disruption budget configuration. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + DefaultPodDisruptionBudget *DefaultPodDisruptionBudgetConfig `protobuf:"bytes,7,opt,name=defaultPodDisruptionBudget,proto3" json:"defaultPodDisruptionBudget,omitempty"` + // Default k8s resources settings for all Istio control plane components. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + DefaultResources *DefaultResourcesConfig `protobuf:"bytes,9,opt,name=defaultResources,proto3" json:"defaultResources,omitempty"` + // Default node tolerations to be applied to all deployments so that all pods can be + // scheduled to nodes with matching taints. Each component can overwrite + // these default values by adding its tolerations block in the relevant section below + // and setting the desired values. + // Configure this field in case that all pods of Istio control plane are expected to + // be scheduled to particular nodes with specified taints. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + DefaultTolerations []*v1.Toleration `protobuf:"bytes,55,rep,name=defaultTolerations,proto3" json:"defaultTolerations,omitempty"` + // Specifies the docker hub for Istio images. + Hub string `protobuf:"bytes,12,opt,name=hub,proto3" json:"hub,omitempty"` + // Specifies the image pull policy for the Istio images. one of Always, Never, IfNotPresent. + // Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. + // + // More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + ImagePullPolicy string `protobuf:"bytes,13,opt,name=imagePullPolicy,proto3" json:"imagePullPolicy,omitempty"` + // ImagePullSecrets for the control plane ServiceAccount, list of secrets in the same namespace + // to use for pulling any images in pods that reference this ServiceAccount. + // Must be set for any cluster configured with private docker registry. + ImagePullSecrets []string `protobuf:"bytes,37,rep,name=imagePullSecrets,proto3" json:"imagePullSecrets,omitempty"` + // Specifies the default namespace for the Istio control plane components. + IstioNamespace string `protobuf:"bytes,14,opt,name=istioNamespace,proto3" json:"istioNamespace,omitempty"` + // Specifies whether istio components should output logs in json format by adding --log_as_json argument to each container. + LogAsJson *wrapperspb.BoolValue `protobuf:"bytes,36,opt,name=logAsJson,proto3" json:"logAsJson,omitempty"` + // Specifies the global logging level settings for the Istio control plane components. + Logging *GlobalLoggingConfig `protobuf:"bytes,17,opt,name=logging,proto3" json:"logging,omitempty"` + // The Mesh Identifier. It should be unique within the scope where + // meshes will interact with each other, but it is not required to be + // globally/universally unique. For example, if any of the following are true, + // then two meshes must have different Mesh IDs: + // - Meshes will have their telemetry aggregated in one place + // - Meshes will be federated together + // - Policy will be written referencing one mesh from the other + // + // If an administrator expects that any of these conditions may become true in + // the future, they should ensure their meshes have different Mesh IDs + // assigned. + // + // Within a multicluster mesh, each cluster must be (manually or auto) + // configured to have the same Mesh ID value. If an existing cluster 'joins' a + // multicluster mesh, it will need to be migrated to the new mesh ID. Details + // of migration TBD, and it may be a disruptive operation to change the Mesh + // ID post-install. + // + // If the mesh admin does not specify a value, Istio will use the value of the + // mesh's Trust Domain. The best practice is to select a proper Trust Domain + // value. + MeshID string `protobuf:"bytes,53,opt,name=meshID,proto3" json:"meshID,omitempty"` + // Configure the mesh networks to be used by the Split Horizon EDS. + // + // The following example defines two networks with different endpoints association methods. + // For `network1` all endpoints that their IP belongs to the provided CIDR range will be + // mapped to network1. The gateway for this network example is specified by its public IP + // address and port. + // The second network, `network2`, in this example is defined differently with all endpoints + // retrieved through the specified Multi-Cluster registry being mapped to network2. The + // gateway is also defined differently with the name of the gateway service on the remote + // cluster. The public IP for the gateway will be determined from that remote service (only + // LoadBalancer gateway service type is currently supported, for a NodePort type gateway service, + // it still need to be configured manually). + // + // meshNetworks: + // + // network1: + // endpoints: + // - fromCidr: "192.168.0.1/24" + // gateways: + // - address: 1.1.1.1 + // port: 80 + // network2: + // endpoints: + // - fromRegistry: reg1 + // gateways: + // - registryServiceName: istio-ingressgateway.istio-system.svc.cluster.local + // port: 443 + MeshNetworks *structpb.Struct `protobuf:"bytes,19,opt,name=meshNetworks,proto3" json:"meshNetworks,omitempty"` + // Specifies the Configuration for Istio mesh across multiple clusters through Istio gateways. + MultiCluster *MultiClusterConfig `protobuf:"bytes,22,opt,name=multiCluster,proto3" json:"multiCluster,omitempty"` + // Network defines the network this cluster belong to. This name + // corresponds to the networks in the map of mesh networks. + Network string `protobuf:"bytes,39,opt,name=network,proto3" json:"network,omitempty"` + // Custom DNS config for the pod to resolve names of services in other + // clusters. Use this to add additional search domains, and other settings. + // see https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#dns-config + // This does not apply to gateway pods as they typically need a different + // set of DNS settings than the normal application pods (e.g. in multicluster scenarios). + PodDNSSearchNamespaces []string `protobuf:"bytes,43,rep,name=podDNSSearchNamespaces,proto3" json:"podDNSSearchNamespaces,omitempty"` + // Controls whether the creation of the sidecar injector ConfigMap should be skipped. + // Defaults to false. When set to true, the sidecar injector ConfigMap will not be created. + OmitSidecarInjectorConfigMap *wrapperspb.BoolValue `protobuf:"bytes,38,opt,name=omitSidecarInjectorConfigMap,proto3" json:"omitSidecarInjectorConfigMap,omitempty"` + // Controls whether the WebhookConfiguration resource(s) should be created. The current behavior + // of Istiod is to manage its own webhook configurations. + // When this option is set to true, Istio Operator, instead of webhooks, manages the + // webhook configurations. When this option is set as false, webhooks manage their + // own webhook configurations. + OperatorManageWebhooks *wrapperspb.BoolValue `protobuf:"bytes,41,opt,name=operatorManageWebhooks,proto3" json:"operatorManageWebhooks,omitempty"` + // Specifies the k8s priorityClassName for the istio control plane components. + // + // See https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + PriorityClassName string `protobuf:"bytes,27,opt,name=priorityClassName,proto3" json:"priorityClassName,omitempty"` + // Specifies how proxies are configured within Istio. + Proxy *ProxyConfig `protobuf:"bytes,28,opt,name=proxy,proto3" json:"proxy,omitempty"` + // Specifies the Configuration for proxy_init container which sets the pods' networking to intercept the inbound/outbound traffic. + ProxyInit *ProxyInitConfig `protobuf:"bytes,29,opt,name=proxy_init,proto3" json:"proxy_init,omitempty"` + // Specifies the Configuration for the SecretDiscoveryService instead of using K8S secrets to mount the certificates. + Sds *SDSConfig `protobuf:"bytes,30,opt,name=sds,proto3" json:"sds,omitempty"` + // Specifies the tag for the Istio docker images. + Tag *structpb.Value `protobuf:"bytes,31,opt,name=tag,proto3" json:"tag,omitempty"` + // The variant of the Istio container images to use. Options are "debug" or "distroless". Unset will use the default for the given version. + Variant string `protobuf:"bytes,67,opt,name=variant,proto3" json:"variant,omitempty"` + // Specifies the Configuration for each of the supported tracers. + Tracer *TracerConfig `protobuf:"bytes,33,opt,name=tracer,proto3" json:"tracer,omitempty"` + // Specifies the Istio control plane’s pilot Pod IP address or remote cluster DNS resolvable hostname. + RemotePilotAddress string `protobuf:"bytes,48,opt,name=remotePilotAddress,proto3" json:"remotePilotAddress,omitempty"` + // Specifies the configution of istiod + Istiod *IstiodConfig `protobuf:"bytes,54,opt,name=istiod,proto3" json:"istiod,omitempty"` + // Configure the Pilot certificate provider. + // Currently, four providers are supported: "kubernetes", "istiod", "custom" and "none". + PilotCertProvider string `protobuf:"bytes,56,opt,name=pilotCertProvider,proto3" json:"pilotCertProvider,omitempty"` + // Configure the policy for validating JWT. + // This is deprecated and has no effect. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + JwtPolicy string `protobuf:"bytes,57,opt,name=jwtPolicy,proto3" json:"jwtPolicy,omitempty"` + // Specifies the configuration for Security Token Service. + Sts *STSConfig `protobuf:"bytes,58,opt,name=sts,proto3" json:"sts,omitempty"` + // Configures the revision this control plane is a part of + Revision string `protobuf:"bytes,59,opt,name=revision,proto3" json:"revision,omitempty"` + // Controls whether the in-cluster MTLS key and certs are loaded from the secret volume mounts. + MountMtlsCerts *wrapperspb.BoolValue `protobuf:"bytes,60,opt,name=mountMtlsCerts,proto3" json:"mountMtlsCerts,omitempty"` + // The address of the CA for CSR. + CaAddress string `protobuf:"bytes,61,opt,name=caAddress,proto3" json:"caAddress,omitempty"` + // Controls whether one external istiod is enabled. + ExternalIstiod *wrapperspb.BoolValue `protobuf:"bytes,62,opt,name=externalIstiod,proto3" json:"externalIstiod,omitempty"` + // Controls whether a remote cluster is the config cluster for an external istiod + ConfigCluster *wrapperspb.BoolValue `protobuf:"bytes,64,opt,name=configCluster,proto3" json:"configCluster,omitempty"` + // The name of the CA for workloads. + // For example, when caName=GkeWorkloadCertificate, GKE workload certificates + // will be used as the certificates for workloads. + // The default value is "" and when caName="", the CA will be configured by other + // mechanisms (e.g., environmental variable CA_PROVIDER). + CaName string `protobuf:"bytes,65,opt,name=caName,proto3" json:"caName,omitempty"` + // TODO: remove this? + // No longer used. + Autoscalingv2API *wrapperspb.BoolValue `protobuf:"bytes,66,opt,name=autoscalingv2API,proto3" json:"autoscalingv2API,omitempty"` + // Platform in which Istio is deployed. Possible values are: "openshift" and "gcp" + // An empty value means it is a vanilla Kubernetes distribution, therefore no special + // treatment will be considered. + Platform string `protobuf:"bytes,69,opt,name=platform,proto3" json:"platform,omitempty"` + // Defines which IP family to use for single stack or the order of IP families for dual-stack. + // Valid list items are "IPv4", "IPv6". + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + IpFamilies []string `protobuf:"bytes,70,rep,name=ipFamilies,proto3" json:"ipFamilies,omitempty"` + // Controls whether Services are configured to use IPv4, IPv6, or both. Valid options + // are PreferDualStack, RequireDualStack, and SingleStack. + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + IpFamilyPolicy string `protobuf:"bytes,71,opt,name=ipFamilyPolicy,proto3" json:"ipFamilyPolicy,omitempty"` // The next available key is 72 +} + +func (x *GlobalConfig) Reset() { + *x = GlobalConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GlobalConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GlobalConfig) ProtoMessage() {} + +func (x *GlobalConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GlobalConfig.ProtoReflect.Descriptor instead. +func (*GlobalConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{13} +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *GlobalConfig) GetArch() *ArchConfig { + if x != nil { + return x.Arch + } + return nil +} + +func (x *GlobalConfig) GetCertSigners() []string { + if x != nil { + return x.CertSigners + } + return nil +} + +func (x *GlobalConfig) GetConfigValidation() *wrapperspb.BoolValue { + if x != nil { + return x.ConfigValidation + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *GlobalConfig) GetDefaultNodeSelector() *structpb.Struct { + if x != nil { + return x.DefaultNodeSelector + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *GlobalConfig) GetDefaultPodDisruptionBudget() *DefaultPodDisruptionBudgetConfig { + if x != nil { + return x.DefaultPodDisruptionBudget + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *GlobalConfig) GetDefaultResources() *DefaultResourcesConfig { + if x != nil { + return x.DefaultResources + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *GlobalConfig) GetDefaultTolerations() []*v1.Toleration { + if x != nil { + return x.DefaultTolerations + } + return nil +} + +func (x *GlobalConfig) GetHub() string { + if x != nil { + return x.Hub + } + return "" +} + +func (x *GlobalConfig) GetImagePullPolicy() string { + if x != nil { + return x.ImagePullPolicy + } + return "" +} + +func (x *GlobalConfig) GetImagePullSecrets() []string { + if x != nil { + return x.ImagePullSecrets + } + return nil +} + +func (x *GlobalConfig) GetIstioNamespace() string { + if x != nil { + return x.IstioNamespace + } + return "" +} + +func (x *GlobalConfig) GetLogAsJson() *wrapperspb.BoolValue { + if x != nil { + return x.LogAsJson + } + return nil +} + +func (x *GlobalConfig) GetLogging() *GlobalLoggingConfig { + if x != nil { + return x.Logging + } + return nil +} + +func (x *GlobalConfig) GetMeshID() string { + if x != nil { + return x.MeshID + } + return "" +} + +func (x *GlobalConfig) GetMeshNetworks() *structpb.Struct { + if x != nil { + return x.MeshNetworks + } + return nil +} + +func (x *GlobalConfig) GetMultiCluster() *MultiClusterConfig { + if x != nil { + return x.MultiCluster + } + return nil +} + +func (x *GlobalConfig) GetNetwork() string { + if x != nil { + return x.Network + } + return "" +} + +func (x *GlobalConfig) GetPodDNSSearchNamespaces() []string { + if x != nil { + return x.PodDNSSearchNamespaces + } + return nil +} + +func (x *GlobalConfig) GetOmitSidecarInjectorConfigMap() *wrapperspb.BoolValue { + if x != nil { + return x.OmitSidecarInjectorConfigMap + } + return nil +} + +func (x *GlobalConfig) GetOperatorManageWebhooks() *wrapperspb.BoolValue { + if x != nil { + return x.OperatorManageWebhooks + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *GlobalConfig) GetPriorityClassName() string { + if x != nil { + return x.PriorityClassName + } + return "" +} + +func (x *GlobalConfig) GetProxy() *ProxyConfig { + if x != nil { + return x.Proxy + } + return nil +} + +func (x *GlobalConfig) GetProxyInit() *ProxyInitConfig { + if x != nil { + return x.ProxyInit + } + return nil +} + +func (x *GlobalConfig) GetSds() *SDSConfig { + if x != nil { + return x.Sds + } + return nil +} + +func (x *GlobalConfig) GetTag() *structpb.Value { + if x != nil { + return x.Tag + } + return nil +} + +func (x *GlobalConfig) GetVariant() string { + if x != nil { + return x.Variant + } + return "" +} + +func (x *GlobalConfig) GetTracer() *TracerConfig { + if x != nil { + return x.Tracer + } + return nil +} + +func (x *GlobalConfig) GetRemotePilotAddress() string { + if x != nil { + return x.RemotePilotAddress + } + return "" +} + +func (x *GlobalConfig) GetIstiod() *IstiodConfig { + if x != nil { + return x.Istiod + } + return nil +} + +func (x *GlobalConfig) GetPilotCertProvider() string { + if x != nil { + return x.PilotCertProvider + } + return "" +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *GlobalConfig) GetJwtPolicy() string { + if x != nil { + return x.JwtPolicy + } + return "" +} + +func (x *GlobalConfig) GetSts() *STSConfig { + if x != nil { + return x.Sts + } + return nil +} + +func (x *GlobalConfig) GetRevision() string { + if x != nil { + return x.Revision + } + return "" +} + +func (x *GlobalConfig) GetMountMtlsCerts() *wrapperspb.BoolValue { + if x != nil { + return x.MountMtlsCerts + } + return nil +} + +func (x *GlobalConfig) GetCaAddress() string { + if x != nil { + return x.CaAddress + } + return "" +} + +func (x *GlobalConfig) GetExternalIstiod() *wrapperspb.BoolValue { + if x != nil { + return x.ExternalIstiod + } + return nil +} + +func (x *GlobalConfig) GetConfigCluster() *wrapperspb.BoolValue { + if x != nil { + return x.ConfigCluster + } + return nil +} + +func (x *GlobalConfig) GetCaName() string { + if x != nil { + return x.CaName + } + return "" +} + +func (x *GlobalConfig) GetAutoscalingv2API() *wrapperspb.BoolValue { + if x != nil { + return x.Autoscalingv2API + } + return nil +} + +func (x *GlobalConfig) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *GlobalConfig) GetIpFamilies() []string { + if x != nil { + return x.IpFamilies + } + return nil +} + +func (x *GlobalConfig) GetIpFamilyPolicy() string { + if x != nil { + return x.IpFamilyPolicy + } + return "" +} + +// Configuration for Security Token Service (STS) server. +// +// See https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-16 +type STSConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ServicePort uint32 `protobuf:"varint,1,opt,name=servicePort,proto3" json:"servicePort,omitempty"` +} + +func (x *STSConfig) Reset() { + *x = STSConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *STSConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*STSConfig) ProtoMessage() {} + +func (x *STSConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use STSConfig.ProtoReflect.Descriptor instead. +func (*STSConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{14} +} + +func (x *STSConfig) GetServicePort() uint32 { + if x != nil { + return x.ServicePort + } + return 0 +} + +type IstiodConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // If enabled, istiod will perform config analysis + EnableAnalysis *wrapperspb.BoolValue `protobuf:"bytes,2,opt,name=enableAnalysis,proto3" json:"enableAnalysis,omitempty"` +} + +func (x *IstiodConfig) Reset() { + *x = IstiodConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IstiodConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IstiodConfig) ProtoMessage() {} + +func (x *IstiodConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IstiodConfig.ProtoReflect.Descriptor instead. +func (*IstiodConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{15} +} + +func (x *IstiodConfig) GetEnableAnalysis() *wrapperspb.BoolValue { + if x != nil { + return x.EnableAnalysis + } + return nil +} + +// GlobalLoggingConfig specifies the global logging level settings for the Istio control plane components. +type GlobalLoggingConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Comma-separated minimum per-scope logging level of messages to output, in the form of :,: + // The control plane has different scopes depending on component, but can configure default log level across all components + // If empty, default scope and level will be used as configured in code + Level string `protobuf:"bytes,1,opt,name=level,proto3" json:"level,omitempty"` +} + +func (x *GlobalLoggingConfig) Reset() { + *x = GlobalLoggingConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GlobalLoggingConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GlobalLoggingConfig) ProtoMessage() {} + +func (x *GlobalLoggingConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GlobalLoggingConfig.ProtoReflect.Descriptor instead. +func (*GlobalLoggingConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{16} +} + +func (x *GlobalLoggingConfig) GetLevel() string { + if x != nil { + return x.Level + } + return "" +} + +// Configuration for an ingress gateway. +type IngressGatewayConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether auto scaling with a HorizontalPodAutoscaler is enabled. + AutoscaleEnabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=autoscaleEnabled,proto3" json:"autoscaleEnabled,omitempty"` + // maxReplicas setting for HorizontalPodAutoscaler. + AutoscaleMax uint32 `protobuf:"varint,2,opt,name=autoscaleMax,proto3" json:"autoscaleMax,omitempty"` + // minReplicas setting for HorizontalPodAutoscaler. + AutoscaleMin uint32 `protobuf:"varint,3,opt,name=autoscaleMin,proto3" json:"autoscaleMin,omitempty"` + // K8s memory utilization setting for HorizontalPodAutoscaler target. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Memory *TargetUtilizationConfig `protobuf:"bytes,4,opt,name=memory,proto3" json:"memory,omitempty"` + // K8s cpu utilization setting for HorizontalPodAutoscaler target. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Cpu *TargetUtilizationConfig `protobuf:"bytes,5,opt,name=cpu,proto3" json:"cpu,omitempty"` + CustomService *wrapperspb.BoolValue `protobuf:"bytes,6,opt,name=customService,proto3" json:"customService,omitempty"` + // Controls whether an ingress gateway is enabled. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,10,opt,name=enabled,proto3" json:"enabled,omitempty"` + // Environment variables passed to the proxy container. + Env *structpb.Struct `protobuf:"bytes,11,opt,name=env,proto3" json:"env,omitempty"` + Labels map[string]string `protobuf:"bytes,15,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + LoadBalancerIP string `protobuf:"bytes,16,opt,name=loadBalancerIP,proto3" json:"loadBalancerIP,omitempty"` + LoadBalancerSourceRanges []string `protobuf:"bytes,17,rep,name=loadBalancerSourceRanges,proto3" json:"loadBalancerSourceRanges,omitempty"` + Name string `protobuf:"bytes,44,opt,name=name,proto3" json:"name,omitempty"` + // K8s node selector. + // + // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + NodeSelector *structpb.Struct `protobuf:"bytes,19,opt,name=nodeSelector,proto3" json:"nodeSelector,omitempty"` + // K8s annotations for pods. + // + // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + PodAnnotations *structpb.Struct `protobuf:"bytes,20,opt,name=podAnnotations,proto3" json:"podAnnotations,omitempty"` + // See EgressGatewayConfig. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + PodAntiAffinityLabelSelector []*v11.LabelSelector `protobuf:"bytes,21,rep,name=podAntiAffinityLabelSelector,proto3" json:"podAntiAffinityLabelSelector,omitempty"` + // See EgressGatewayConfig. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + PodAntiAffinityTermLabelSelector []*v11.LabelSelector `protobuf:"bytes,22,rep,name=podAntiAffinityTermLabelSelector,proto3" json:"podAntiAffinityTermLabelSelector,omitempty"` + // Port Configuration for the ingress gateway. + Ports []*PortsConfig `protobuf:"bytes,23,rep,name=ports,proto3" json:"ports,omitempty"` + // Number of replicas for the ingress gateway Deployment. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + ReplicaCount uint32 `protobuf:"varint,24,opt,name=replicaCount,proto3" json:"replicaCount,omitempty"` + // K8s resources settings. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Resources *structpb.Struct `protobuf:"bytes,25,opt,name=resources,proto3" json:"resources,omitempty"` + // Config for secret volume mounts. + SecretVolumes []*SecretVolume `protobuf:"bytes,27,rep,name=secretVolumes,proto3" json:"secretVolumes,omitempty"` + // Annotations to add to the egress gateway service. + ServiceAnnotations *structpb.Struct `protobuf:"bytes,28,opt,name=serviceAnnotations,proto3" json:"serviceAnnotations,omitempty"` + // Service type. + // + // See https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + Type string `protobuf:"bytes,29,opt,name=type,proto3" json:"type,omitempty"` + // Enables cross-cluster access using SNI matching. + Zvpn *IngressGatewayZvpnConfig `protobuf:"bytes,30,opt,name=zvpn,proto3" json:"zvpn,omitempty"` + // K8s rolling update strategy + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + RollingMaxSurge *IntOrString `protobuf:"bytes,31,opt,name=rollingMaxSurge,proto3" json:"rollingMaxSurge,omitempty"` + // The number of pods that can be unavailable during a rolling update (see + // `strategy.rollingUpdate.maxUnavailable` here: + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/#DeploymentSpec). + // May be specified as a number of pods or as a percent of the total number + // of pods at the start of the update. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + RollingMaxUnavailable *IntOrString `protobuf:"bytes,32,opt,name=rollingMaxUnavailable,proto3" json:"rollingMaxUnavailable,omitempty"` + ExternalTrafficPolicy string `protobuf:"bytes,34,opt,name=externalTrafficPolicy,proto3" json:"externalTrafficPolicy,omitempty"` + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Tolerations []*v1.Toleration `protobuf:"bytes,35,rep,name=tolerations,proto3" json:"tolerations,omitempty"` + IngressPorts []*structpb.Struct `protobuf:"bytes,36,rep,name=ingressPorts,proto3" json:"ingressPorts,omitempty"` + AdditionalContainers []*structpb.Struct `protobuf:"bytes,37,rep,name=additionalContainers,proto3" json:"additionalContainers,omitempty"` + ConfigVolumes []*structpb.Struct `protobuf:"bytes,38,rep,name=configVolumes,proto3" json:"configVolumes,omitempty"` + RunAsRoot *wrapperspb.BoolValue `protobuf:"bytes,45,opt,name=runAsRoot,proto3" json:"runAsRoot,omitempty"` + // The injection template to use for the gateway. If not set, no injection will be performed. + InjectionTemplate string `protobuf:"bytes,46,opt,name=injectionTemplate,proto3" json:"injectionTemplate,omitempty"` + ServiceAccount *ServiceAccount `protobuf:"bytes,47,opt,name=serviceAccount,proto3" json:"serviceAccount,omitempty"` + // Defines which IP family to use for single stack or the order of IP families for dual-stack. + // Valid list items are "IPv4", "IPv6". + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + IpFamilies []string `protobuf:"bytes,48,rep,name=ipFamilies,proto3" json:"ipFamilies,omitempty"` + // Controls whether Services are configured to use IPv4, IPv6, or both. Valid options + // are PreferDualStack, RequireDualStack, and SingleStack. + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + IpFamilyPolicy string `protobuf:"bytes,49,opt,name=ipFamilyPolicy,proto3" json:"ipFamilyPolicy,omitempty"` // Next available 50. +} + +func (x *IngressGatewayConfig) Reset() { + *x = IngressGatewayConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IngressGatewayConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IngressGatewayConfig) ProtoMessage() {} + +func (x *IngressGatewayConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IngressGatewayConfig.ProtoReflect.Descriptor instead. +func (*IngressGatewayConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{17} +} + +func (x *IngressGatewayConfig) GetAutoscaleEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.AutoscaleEnabled + } + return nil +} + +func (x *IngressGatewayConfig) GetAutoscaleMax() uint32 { + if x != nil { + return x.AutoscaleMax + } + return 0 +} + +func (x *IngressGatewayConfig) GetAutoscaleMin() uint32 { + if x != nil { + return x.AutoscaleMin + } + return 0 +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *IngressGatewayConfig) GetMemory() *TargetUtilizationConfig { + if x != nil { + return x.Memory + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *IngressGatewayConfig) GetCpu() *TargetUtilizationConfig { + if x != nil { + return x.Cpu + } + return nil +} + +func (x *IngressGatewayConfig) GetCustomService() *wrapperspb.BoolValue { + if x != nil { + return x.CustomService + } + return nil +} + +func (x *IngressGatewayConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *IngressGatewayConfig) GetEnv() *structpb.Struct { + if x != nil { + return x.Env + } + return nil +} + +func (x *IngressGatewayConfig) GetLabels() map[string]string { + if x != nil { + return x.Labels + } + return nil +} + +func (x *IngressGatewayConfig) GetLoadBalancerIP() string { + if x != nil { + return x.LoadBalancerIP + } + return "" +} + +func (x *IngressGatewayConfig) GetLoadBalancerSourceRanges() []string { + if x != nil { + return x.LoadBalancerSourceRanges + } + return nil +} + +func (x *IngressGatewayConfig) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *IngressGatewayConfig) GetNodeSelector() *structpb.Struct { + if x != nil { + return x.NodeSelector + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *IngressGatewayConfig) GetPodAnnotations() *structpb.Struct { + if x != nil { + return x.PodAnnotations + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *IngressGatewayConfig) GetPodAntiAffinityLabelSelector() []*v11.LabelSelector { + if x != nil { + return x.PodAntiAffinityLabelSelector + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *IngressGatewayConfig) GetPodAntiAffinityTermLabelSelector() []*v11.LabelSelector { + if x != nil { + return x.PodAntiAffinityTermLabelSelector + } + return nil +} + +func (x *IngressGatewayConfig) GetPorts() []*PortsConfig { + if x != nil { + return x.Ports + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *IngressGatewayConfig) GetReplicaCount() uint32 { + if x != nil { + return x.ReplicaCount + } + return 0 +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *IngressGatewayConfig) GetResources() *structpb.Struct { + if x != nil { + return x.Resources + } + return nil +} + +func (x *IngressGatewayConfig) GetSecretVolumes() []*SecretVolume { + if x != nil { + return x.SecretVolumes + } + return nil +} + +func (x *IngressGatewayConfig) GetServiceAnnotations() *structpb.Struct { + if x != nil { + return x.ServiceAnnotations + } + return nil +} + +func (x *IngressGatewayConfig) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *IngressGatewayConfig) GetZvpn() *IngressGatewayZvpnConfig { + if x != nil { + return x.Zvpn + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *IngressGatewayConfig) GetRollingMaxSurge() *IntOrString { + if x != nil { + return x.RollingMaxSurge + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *IngressGatewayConfig) GetRollingMaxUnavailable() *IntOrString { + if x != nil { + return x.RollingMaxUnavailable + } + return nil +} + +func (x *IngressGatewayConfig) GetExternalTrafficPolicy() string { + if x != nil { + return x.ExternalTrafficPolicy + } + return "" +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *IngressGatewayConfig) GetTolerations() []*v1.Toleration { + if x != nil { + return x.Tolerations + } + return nil +} + +func (x *IngressGatewayConfig) GetIngressPorts() []*structpb.Struct { + if x != nil { + return x.IngressPorts + } + return nil +} + +func (x *IngressGatewayConfig) GetAdditionalContainers() []*structpb.Struct { + if x != nil { + return x.AdditionalContainers + } + return nil +} + +func (x *IngressGatewayConfig) GetConfigVolumes() []*structpb.Struct { + if x != nil { + return x.ConfigVolumes + } + return nil +} + +func (x *IngressGatewayConfig) GetRunAsRoot() *wrapperspb.BoolValue { + if x != nil { + return x.RunAsRoot + } + return nil +} + +func (x *IngressGatewayConfig) GetInjectionTemplate() string { + if x != nil { + return x.InjectionTemplate + } + return "" +} + +func (x *IngressGatewayConfig) GetServiceAccount() *ServiceAccount { + if x != nil { + return x.ServiceAccount + } + return nil +} + +func (x *IngressGatewayConfig) GetIpFamilies() []string { + if x != nil { + return x.IpFamilies + } + return nil +} + +func (x *IngressGatewayConfig) GetIpFamilyPolicy() string { + if x != nil { + return x.IpFamilyPolicy + } + return "" +} + +// IngressGatewayZvpnConfig enables cross-cluster access using SNI matching. +type IngressGatewayZvpnConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether ZeroVPN is enabled. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + Suffix string `protobuf:"bytes,2,opt,name=suffix,proto3" json:"suffix,omitempty"` +} + +func (x *IngressGatewayZvpnConfig) Reset() { + *x = IngressGatewayZvpnConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IngressGatewayZvpnConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IngressGatewayZvpnConfig) ProtoMessage() {} + +func (x *IngressGatewayZvpnConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IngressGatewayZvpnConfig.ProtoReflect.Descriptor instead. +func (*IngressGatewayZvpnConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{18} +} + +func (x *IngressGatewayZvpnConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *IngressGatewayZvpnConfig) GetSuffix() string { + if x != nil { + return x.Suffix + } + return "" +} + +// MultiClusterConfig specifies the Configuration for Istio mesh across multiple clusters through the istio gateways. +type MultiClusterConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Enables the connection between two kubernetes clusters via their respective ingressgateway services. + // Use if the pods in each cluster cannot directly talk to one another. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // The name of the cluster this installation will run in. This is required for sidecar injection + // to properly label proxies + ClusterName string `protobuf:"bytes,2,opt,name=clusterName,proto3" json:"clusterName,omitempty"` + // The suffix for global service names. + GlobalDomainSuffix string `protobuf:"bytes,3,opt,name=globalDomainSuffix,proto3" json:"globalDomainSuffix,omitempty"` + // Enable envoy filter to translate `globalDomainSuffix` to cluster local suffix for cross cluster communication. + IncludeEnvoyFilter *wrapperspb.BoolValue `protobuf:"bytes,4,opt,name=includeEnvoyFilter,proto3" json:"includeEnvoyFilter,omitempty"` +} + +func (x *MultiClusterConfig) Reset() { + *x = MultiClusterConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MultiClusterConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MultiClusterConfig) ProtoMessage() {} + +func (x *MultiClusterConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MultiClusterConfig.ProtoReflect.Descriptor instead. +func (*MultiClusterConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{19} +} + +func (x *MultiClusterConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *MultiClusterConfig) GetClusterName() string { + if x != nil { + return x.ClusterName + } + return "" +} + +func (x *MultiClusterConfig) GetGlobalDomainSuffix() string { + if x != nil { + return x.GlobalDomainSuffix + } + return "" +} + +func (x *MultiClusterConfig) GetIncludeEnvoyFilter() *wrapperspb.BoolValue { + if x != nil { + return x.IncludeEnvoyFilter + } + return nil +} + +// OutboundTrafficPolicyConfig controls the default behavior of the sidecar for handling outbound traffic from the application. +type OutboundTrafficPolicyConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Mode OutboundTrafficPolicyConfig_Mode `protobuf:"varint,2,opt,name=mode,proto3,enum=v1alpha1.OutboundTrafficPolicyConfig_Mode" json:"mode,omitempty"` +} + +func (x *OutboundTrafficPolicyConfig) Reset() { + *x = OutboundTrafficPolicyConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OutboundTrafficPolicyConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OutboundTrafficPolicyConfig) ProtoMessage() {} + +func (x *OutboundTrafficPolicyConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OutboundTrafficPolicyConfig.ProtoReflect.Descriptor instead. +func (*OutboundTrafficPolicyConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{20} +} + +func (x *OutboundTrafficPolicyConfig) GetMode() OutboundTrafficPolicyConfig_Mode { + if x != nil { + return x.Mode + } + return OutboundTrafficPolicyConfig_ALLOW_ANY +} + +// Configuration for Pilot. +type PilotConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether Pilot is enabled. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // Controls whether a HorizontalPodAutoscaler is installed for Pilot. + AutoscaleEnabled *wrapperspb.BoolValue `protobuf:"bytes,2,opt,name=autoscaleEnabled,proto3" json:"autoscaleEnabled,omitempty"` + // Minimum number of replicas in the HorizontalPodAutoscaler for Pilot. + AutoscaleMin uint32 `protobuf:"varint,3,opt,name=autoscaleMin,proto3" json:"autoscaleMin,omitempty"` + // Maximum number of replicas in the HorizontalPodAutoscaler for Pilot. + AutoscaleMax uint32 `protobuf:"varint,4,opt,name=autoscaleMax,proto3" json:"autoscaleMax,omitempty"` + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior + AutoscaleBehavior *structpb.Struct `protobuf:"bytes,40,opt,name=autoscaleBehavior,proto3" json:"autoscaleBehavior,omitempty"` + // Number of replicas in the Pilot Deployment. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + ReplicaCount uint32 `protobuf:"varint,5,opt,name=replicaCount,proto3" json:"replicaCount,omitempty"` + // Image name used for Pilot. + // + // This can be set either to image name if hub is also set, or can be set to the full hub:name string. + // + // Examples: custom-pilot, docker.io/someuser:custom-pilot + Image string `protobuf:"bytes,6,opt,name=image,proto3" json:"image,omitempty"` + // Trace sampling fraction. + // + // Used to set the fraction of time that traces are sampled. Higher values are more accurate but add CPU overhead. + // + // Allowed values: 0.0 to 1.0 + TraceSampling float64 `protobuf:"fixed64,8,opt,name=traceSampling,proto3" json:"traceSampling,omitempty"` + // K8s resources settings. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Resources *Resources `protobuf:"bytes,9,opt,name=resources,proto3" json:"resources,omitempty"` + // Target CPU utilization used in HorizontalPodAutoscaler. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Cpu *TargetUtilizationConfig `protobuf:"bytes,11,opt,name=cpu,proto3" json:"cpu,omitempty"` + // K8s node selector. + // + // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + NodeSelector *structpb.Struct `protobuf:"bytes,12,opt,name=nodeSelector,proto3" json:"nodeSelector,omitempty"` + // Maximum duration that a sidecar can be connected to a pilot. + // + // This setting balances out load across pilot instances, but adds some resource overhead. + // + // Examples: 300s, 30m, 1h + KeepaliveMaxServerConnectionAge *durationpb.Duration `protobuf:"bytes,13,opt,name=keepaliveMaxServerConnectionAge,proto3" json:"keepaliveMaxServerConnectionAge,omitempty"` + // Labels that are added to Pilot deployment. + // + // See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + DeploymentLabels *structpb.Struct `protobuf:"bytes,14,opt,name=deploymentLabels,proto3" json:"deploymentLabels,omitempty"` + // Labels that are added to Pilot pods. + // + // See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + PodLabels *structpb.Struct `protobuf:"bytes,36,opt,name=podLabels,proto3" json:"podLabels,omitempty"` + // Configuration settings passed to Pilot as a ConfigMap. + // + // This controls whether the mesh config map, generated from values.yaml is generated. + // If false, pilot wil use default values or user-supplied values, in that order of preference. + ConfigMap *wrapperspb.BoolValue `protobuf:"bytes,18,opt,name=configMap,proto3" json:"configMap,omitempty"` + // Environment variables passed to the Pilot container. + // + // Examples: + // env: + // + // ENV_VAR_1: value1 + // ENV_VAR_2: value2 + Env *structpb.Struct `protobuf:"bytes,21,opt,name=env,proto3" json:"env,omitempty"` + // K8s affinity to set on the Pilot Pods. + Affinity *v1.Affinity `protobuf:"bytes,22,opt,name=affinity,proto3" json:"affinity,omitempty"` + // K8s rolling update strategy + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + RollingMaxSurge *IntOrString `protobuf:"bytes,24,opt,name=rollingMaxSurge,proto3" json:"rollingMaxSurge,omitempty"` + // The number of pods that can be unavailable during a rolling update (see + // `strategy.rollingUpdate.maxUnavailable` here: + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/#DeploymentSpec). + // May be specified as a number of pods or as a percent of the total number + // of pods at the start of the update. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + RollingMaxUnavailable *IntOrString `protobuf:"bytes,25,opt,name=rollingMaxUnavailable,proto3" json:"rollingMaxUnavailable,omitempty"` + // The node tolerations to be applied to the Pilot deployment so that it can be + // scheduled to particular nodes with matching taints. + // More info: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Tolerations []*v1.Toleration `protobuf:"bytes,26,rep,name=tolerations,proto3" json:"tolerations,omitempty"` + // K8s annotations for pods. + // + // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + PodAnnotations *structpb.Struct `protobuf:"bytes,30,opt,name=podAnnotations,proto3" json:"podAnnotations,omitempty"` + // K8s annotations for the Service. + // + // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ServiceAnnotations *structpb.Struct `protobuf:"bytes,37,opt,name=serviceAnnotations,proto3" json:"serviceAnnotations,omitempty"` + // K8s annotations for the service account + ServiceAccountAnnotations *structpb.Struct `protobuf:"bytes,56,opt,name=serviceAccountAnnotations,proto3" json:"serviceAccountAnnotations,omitempty"` + // Specifies an extra root certificate in PEM format. This certificate will be trusted + // by pilot when resolving JWKS URIs. + JwksResolverExtraRootCA string `protobuf:"bytes,32,opt,name=jwksResolverExtraRootCA,proto3" json:"jwksResolverExtraRootCA,omitempty"` + // Hub to pull the container image from. Image will be `Hub/Image:Tag-Variant`. + Hub string `protobuf:"bytes,34,opt,name=hub,proto3" json:"hub,omitempty"` + // The container image tag to pull. Image will be `Hub/Image:Tag-Variant`. + Tag *structpb.Value `protobuf:"bytes,35,opt,name=tag,proto3" json:"tag,omitempty"` + // The container image variant to pull. Options are "debug" or "distroless". Unset will use the default for the given version. + Variant string `protobuf:"bytes,39,opt,name=variant,proto3" json:"variant,omitempty"` + // The seccompProfile for the Pilot container. + // + // See: https://kubernetes.io/docs/tutorials/security/seccomp/ + SeccompProfile *v1.SeccompProfile `protobuf:"bytes,38,opt,name=seccompProfile,proto3" json:"seccompProfile,omitempty"` + // The k8s topologySpreadConstraints for the Pilot pods. + TopologySpreadConstraints []*v1.TopologySpreadConstraint `protobuf:"bytes,41,rep,name=topologySpreadConstraints,proto3" json:"topologySpreadConstraints,omitempty"` + // Additional container arguments for the Pilot container. + ExtraContainerArgs []*structpb.Struct `protobuf:"bytes,42,rep,name=extraContainerArgs,proto3" json:"extraContainerArgs,omitempty"` + // Additional volumeMounts to add to the Pilot container. + VolumeMounts []*v1.VolumeMount `protobuf:"bytes,49,rep,name=volumeMounts,proto3" json:"volumeMounts,omitempty"` + // Additional volumes to add to the Pilot Pod. + Volumes []*v1.Volume `protobuf:"bytes,51,rep,name=volumes,proto3" json:"volumes,omitempty"` + // Defines which IP family to use for single stack or the order of IP families for dual-stack. + // Valid list items are "IPv4", "IPv6". + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + IpFamilies []string `protobuf:"bytes,52,rep,name=ipFamilies,proto3" json:"ipFamilies,omitempty"` + // Controls whether Services are configured to use IPv4, IPv6, or both. Valid options + // are PreferDualStack, RequireDualStack, and SingleStack. + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + IpFamilyPolicy string `protobuf:"bytes,53,opt,name=ipFamilyPolicy,proto3" json:"ipFamilyPolicy,omitempty"` + // Target memory utilization used in HorizontalPodAutoscaler. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Memory *TargetUtilizationConfig `protobuf:"bytes,54,opt,name=memory,proto3" json:"memory,omitempty"` + // Configures whether to use an existing CNI installation for workloads + Cni *CNIUsageConfig `protobuf:"bytes,55,opt,name=cni,proto3" json:"cni,omitempty"` + Taint *PilotTaintControllerConfig `protobuf:"bytes,57,opt,name=taint,proto3" json:"taint,omitempty"` + // If set, `istiod` will allow connections from trusted node proxy ztunnels + // in the provided namespace. + TrustedZtunnelNamespace string `protobuf:"bytes,59,opt,name=trustedZtunnelNamespace,proto3" json:"trustedZtunnelNamespace,omitempty"` +} + +func (x *PilotConfig) Reset() { + *x = PilotConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PilotConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PilotConfig) ProtoMessage() {} + +func (x *PilotConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PilotConfig.ProtoReflect.Descriptor instead. +func (*PilotConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{21} +} + +func (x *PilotConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *PilotConfig) GetAutoscaleEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.AutoscaleEnabled + } + return nil +} + +func (x *PilotConfig) GetAutoscaleMin() uint32 { + if x != nil { + return x.AutoscaleMin + } + return 0 +} + +func (x *PilotConfig) GetAutoscaleMax() uint32 { + if x != nil { + return x.AutoscaleMax + } + return 0 +} + +func (x *PilotConfig) GetAutoscaleBehavior() *structpb.Struct { + if x != nil { + return x.AutoscaleBehavior + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *PilotConfig) GetReplicaCount() uint32 { + if x != nil { + return x.ReplicaCount + } + return 0 +} + +func (x *PilotConfig) GetImage() string { + if x != nil { + return x.Image + } + return "" +} + +func (x *PilotConfig) GetTraceSampling() float64 { + if x != nil { + return x.TraceSampling + } + return 0 +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *PilotConfig) GetResources() *Resources { + if x != nil { + return x.Resources + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *PilotConfig) GetCpu() *TargetUtilizationConfig { + if x != nil { + return x.Cpu + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *PilotConfig) GetNodeSelector() *structpb.Struct { + if x != nil { + return x.NodeSelector + } + return nil +} + +func (x *PilotConfig) GetKeepaliveMaxServerConnectionAge() *durationpb.Duration { + if x != nil { + return x.KeepaliveMaxServerConnectionAge + } + return nil +} + +func (x *PilotConfig) GetDeploymentLabels() *structpb.Struct { + if x != nil { + return x.DeploymentLabels + } + return nil +} + +func (x *PilotConfig) GetPodLabels() *structpb.Struct { + if x != nil { + return x.PodLabels + } + return nil +} + +func (x *PilotConfig) GetConfigMap() *wrapperspb.BoolValue { + if x != nil { + return x.ConfigMap + } + return nil +} + +func (x *PilotConfig) GetEnv() *structpb.Struct { + if x != nil { + return x.Env + } + return nil +} + +func (x *PilotConfig) GetAffinity() *v1.Affinity { + if x != nil { + return x.Affinity + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *PilotConfig) GetRollingMaxSurge() *IntOrString { + if x != nil { + return x.RollingMaxSurge + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *PilotConfig) GetRollingMaxUnavailable() *IntOrString { + if x != nil { + return x.RollingMaxUnavailable + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *PilotConfig) GetTolerations() []*v1.Toleration { + if x != nil { + return x.Tolerations + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *PilotConfig) GetPodAnnotations() *structpb.Struct { + if x != nil { + return x.PodAnnotations + } + return nil +} + +func (x *PilotConfig) GetServiceAnnotations() *structpb.Struct { + if x != nil { + return x.ServiceAnnotations + } + return nil +} + +func (x *PilotConfig) GetServiceAccountAnnotations() *structpb.Struct { + if x != nil { + return x.ServiceAccountAnnotations + } + return nil +} + +func (x *PilotConfig) GetJwksResolverExtraRootCA() string { + if x != nil { + return x.JwksResolverExtraRootCA + } + return "" +} + +func (x *PilotConfig) GetHub() string { + if x != nil { + return x.Hub + } + return "" +} + +func (x *PilotConfig) GetTag() *structpb.Value { + if x != nil { + return x.Tag + } + return nil +} + +func (x *PilotConfig) GetVariant() string { + if x != nil { + return x.Variant + } + return "" +} + +func (x *PilotConfig) GetSeccompProfile() *v1.SeccompProfile { + if x != nil { + return x.SeccompProfile + } + return nil +} + +func (x *PilotConfig) GetTopologySpreadConstraints() []*v1.TopologySpreadConstraint { + if x != nil { + return x.TopologySpreadConstraints + } + return nil +} + +func (x *PilotConfig) GetExtraContainerArgs() []*structpb.Struct { + if x != nil { + return x.ExtraContainerArgs + } + return nil +} + +func (x *PilotConfig) GetVolumeMounts() []*v1.VolumeMount { + if x != nil { + return x.VolumeMounts + } + return nil +} + +func (x *PilotConfig) GetVolumes() []*v1.Volume { + if x != nil { + return x.Volumes + } + return nil +} + +func (x *PilotConfig) GetIpFamilies() []string { + if x != nil { + return x.IpFamilies + } + return nil +} + +func (x *PilotConfig) GetIpFamilyPolicy() string { + if x != nil { + return x.IpFamilyPolicy + } + return "" +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *PilotConfig) GetMemory() *TargetUtilizationConfig { + if x != nil { + return x.Memory + } + return nil +} + +func (x *PilotConfig) GetCni() *CNIUsageConfig { + if x != nil { + return x.Cni + } + return nil +} + +func (x *PilotConfig) GetTaint() *PilotTaintControllerConfig { + if x != nil { + return x.Taint + } + return nil +} + +func (x *PilotConfig) GetTrustedZtunnelNamespace() string { + if x != nil { + return x.TrustedZtunnelNamespace + } + return "" +} + +type PilotTaintControllerConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Enable the untaint controller for new nodes. This aims to solve a race for CNI installation on + // new nodes. For this to work, the newly added nodes need to have the istio CNI taint as they are + // added to the cluster. This is usually done by configuring the cluster infra provider. + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // The namespace of the CNI daemonset, incase it's not the same as istiod. + Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` +} + +func (x *PilotTaintControllerConfig) Reset() { + *x = PilotTaintControllerConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PilotTaintControllerConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PilotTaintControllerConfig) ProtoMessage() {} + +func (x *PilotTaintControllerConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PilotTaintControllerConfig.ProtoReflect.Descriptor instead. +func (*PilotTaintControllerConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{22} +} + +func (x *PilotTaintControllerConfig) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *PilotTaintControllerConfig) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +// Controls legacy k8s ingress. Only one pilot profile should enable ingress support. +type PilotIngressConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Sets the type ingress service for Pilot. + // + // If empty, node-port is assumed. + // + // Allowed values: node-port, istio-ingressgateway, ingress + IngressService string `protobuf:"bytes,1,opt,name=ingressService,proto3" json:"ingressService,omitempty"` + IngressControllerMode IngressControllerMode `protobuf:"varint,2,opt,name=ingressControllerMode,proto3,enum=v1alpha1.IngressControllerMode" json:"ingressControllerMode,omitempty"` + // If mode is STRICT, this value must be set on "kubernetes.io/ingress.class" annotation to activate. + IngressClass string `protobuf:"bytes,3,opt,name=ingressClass,proto3" json:"ingressClass,omitempty"` +} + +func (x *PilotIngressConfig) Reset() { + *x = PilotIngressConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PilotIngressConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PilotIngressConfig) ProtoMessage() {} + +func (x *PilotIngressConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PilotIngressConfig.ProtoReflect.Descriptor instead. +func (*PilotIngressConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{23} +} + +func (x *PilotIngressConfig) GetIngressService() string { + if x != nil { + return x.IngressService + } + return "" +} + +func (x *PilotIngressConfig) GetIngressControllerMode() IngressControllerMode { + if x != nil { + return x.IngressControllerMode + } + return IngressControllerMode_UNSPECIFIED +} + +func (x *PilotIngressConfig) GetIngressClass() string { + if x != nil { + return x.IngressClass + } + return "" +} + +// Controls whether Istio policy is applied to Pilot. +type PilotPolicyConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether Istio policy is applied to Pilot. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` +} + +func (x *PilotPolicyConfig) Reset() { + *x = PilotPolicyConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PilotPolicyConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PilotPolicyConfig) ProtoMessage() {} + +func (x *PilotPolicyConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PilotPolicyConfig.ProtoReflect.Descriptor instead. +func (*PilotPolicyConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{24} +} + +func (x *PilotPolicyConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +// Controls telemetry configuration +type TelemetryConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether telemetry is exported for Pilot. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // Configuration for Telemetry v2. + V2 *TelemetryV2Config `protobuf:"bytes,3,opt,name=v2,proto3" json:"v2,omitempty"` +} + +func (x *TelemetryConfig) Reset() { + *x = TelemetryConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TelemetryConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TelemetryConfig) ProtoMessage() {} + +func (x *TelemetryConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TelemetryConfig.ProtoReflect.Descriptor instead. +func (*TelemetryConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{25} +} + +func (x *TelemetryConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *TelemetryConfig) GetV2() *TelemetryV2Config { + if x != nil { + return x.V2 + } + return nil +} + +// Controls whether pilot will configure telemetry v2. +type TelemetryV2Config struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether pilot will configure telemetry v2. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // Telemetry v2 settings for prometheus. + Prometheus *TelemetryV2PrometheusConfig `protobuf:"bytes,2,opt,name=prometheus,proto3" json:"prometheus,omitempty"` + // Telemetry v2 settings for stackdriver. + Stackdriver *TelemetryV2StackDriverConfig `protobuf:"bytes,3,opt,name=stackdriver,proto3" json:"stackdriver,omitempty"` +} + +func (x *TelemetryV2Config) Reset() { + *x = TelemetryV2Config{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TelemetryV2Config) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TelemetryV2Config) ProtoMessage() {} + +func (x *TelemetryV2Config) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TelemetryV2Config.ProtoReflect.Descriptor instead. +func (*TelemetryV2Config) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{26} +} + +func (x *TelemetryV2Config) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *TelemetryV2Config) GetPrometheus() *TelemetryV2PrometheusConfig { + if x != nil { + return x.Prometheus + } + return nil +} + +func (x *TelemetryV2Config) GetStackdriver() *TelemetryV2StackDriverConfig { + if x != nil { + return x.Stackdriver + } + return nil +} + +// Controls telemetry v2 prometheus settings. +type TelemetryV2PrometheusConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether stats envoyfilter would be enabled or not. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` +} + +func (x *TelemetryV2PrometheusConfig) Reset() { + *x = TelemetryV2PrometheusConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TelemetryV2PrometheusConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TelemetryV2PrometheusConfig) ProtoMessage() {} + +func (x *TelemetryV2PrometheusConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TelemetryV2PrometheusConfig.ProtoReflect.Descriptor instead. +func (*TelemetryV2PrometheusConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{27} +} + +func (x *TelemetryV2PrometheusConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +// TelemetryV2StackDriverConfig controls telemetry v2 stackdriver settings. +type TelemetryV2StackDriverConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` +} + +func (x *TelemetryV2StackDriverConfig) Reset() { + *x = TelemetryV2StackDriverConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TelemetryV2StackDriverConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TelemetryV2StackDriverConfig) ProtoMessage() {} + +func (x *TelemetryV2StackDriverConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TelemetryV2StackDriverConfig.ProtoReflect.Descriptor instead. +func (*TelemetryV2StackDriverConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{28} +} + +func (x *TelemetryV2StackDriverConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +// Configuration for a port. +type PortsConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Port name. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Port number. + Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` + // NodePort number. + NodePort int32 `protobuf:"varint,3,opt,name=nodePort,proto3" json:"nodePort,omitempty"` + // Target port number. + TargetPort int32 `protobuf:"varint,4,opt,name=targetPort,proto3" json:"targetPort,omitempty"` + // Protocol name. + Protocol string `protobuf:"bytes,5,opt,name=protocol,proto3" json:"protocol,omitempty"` +} + +func (x *PortsConfig) Reset() { + *x = PortsConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PortsConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PortsConfig) ProtoMessage() {} + +func (x *PortsConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PortsConfig.ProtoReflect.Descriptor instead. +func (*PortsConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{29} +} + +func (x *PortsConfig) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *PortsConfig) GetPort() int32 { + if x != nil { + return x.Port + } + return 0 +} + +func (x *PortsConfig) GetNodePort() int32 { + if x != nil { + return x.NodePort + } + return 0 +} + +func (x *PortsConfig) GetTargetPort() int32 { + if x != nil { + return x.TargetPort + } + return 0 +} + +func (x *PortsConfig) GetProtocol() string { + if x != nil { + return x.Protocol + } + return "" +} + +// Configuration for Proxy. +type ProxyConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls the 'policy' in the sidecar injector. + AutoInject string `protobuf:"bytes,4,opt,name=autoInject,proto3" json:"autoInject,omitempty"` + // Domain for the cluster, default: "cluster.local". + // + // K8s allows this to be customized, see https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/ + ClusterDomain string `protobuf:"bytes,5,opt,name=clusterDomain,proto3" json:"clusterDomain,omitempty"` + // Per Component log level for proxy, applies to gateways and sidecars. + // + // If a component level is not set, then the global "logLevel" will be used. If left empty, "misc:error" is used. + ComponentLogLevel string `protobuf:"bytes,6,opt,name=componentLogLevel,proto3" json:"componentLogLevel,omitempty"` + // Enables core dumps for newly injected sidecars. + // + // If set, newly injected sidecars will have core dumps enabled. + EnableCoreDump *wrapperspb.BoolValue `protobuf:"bytes,9,opt,name=enableCoreDump,proto3" json:"enableCoreDump,omitempty"` + // Specifies the Istio ingress ports not to capture. + ExcludeInboundPorts string `protobuf:"bytes,12,opt,name=excludeInboundPorts,proto3" json:"excludeInboundPorts,omitempty"` + // Lists the excluded IP ranges of Istio egress traffic that the sidecar captures. + ExcludeIPRanges string `protobuf:"bytes,13,opt,name=excludeIPRanges,proto3" json:"excludeIPRanges,omitempty"` + // Image name or path for the proxy, default: "proxyv2". + // + // If registry or tag are not specified, global.hub and global.tag are used. + // + // Examples: my-proxy (uses global.hub/tag), docker.io/myrepo/my-proxy:v1.0.0 + Image string `protobuf:"bytes,14,opt,name=image,proto3" json:"image,omitempty"` + // Lists the IP ranges of Istio egress traffic that the sidecar captures. + // + // Example: "172.30.0.0/16,172.20.0.0/16" + // This would only capture egress traffic on those two IP Ranges, all other outbound traffic would # be allowed by the sidecar." + IncludeIPRanges string `protobuf:"bytes,16,opt,name=includeIPRanges,proto3" json:"includeIPRanges,omitempty"` + // Log level for proxy, applies to gateways and sidecars. If left empty, "warning" is used. Expected values are: trace\|debug\|info\|warning\|error\|critical\|off + LogLevel string `protobuf:"bytes,18,opt,name=logLevel,proto3" json:"logLevel,omitempty"` + // Path to the file to which the proxy will write outlier detection logs. + // + // Example: "/dev/stdout" + // This would write the logs to standard output. + OutlierLogPath string `protobuf:"bytes,42,opt,name=outlierLogPath,proto3" json:"outlierLogPath,omitempty"` + // Enables privileged securityContext for the istio-proxy container. + // + // See https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + Privileged *wrapperspb.BoolValue `protobuf:"bytes,19,opt,name=privileged,proto3" json:"privileged,omitempty"` + // Sets the initial delay for readiness probes in seconds. + ReadinessInitialDelaySeconds uint32 `protobuf:"varint,20,opt,name=readinessInitialDelaySeconds,proto3" json:"readinessInitialDelaySeconds,omitempty"` + // Sets the interval between readiness probes in seconds. + ReadinessPeriodSeconds uint32 `protobuf:"varint,21,opt,name=readinessPeriodSeconds,proto3" json:"readinessPeriodSeconds,omitempty"` + // Sets the number of successive failed probes before indicating readiness failure. + ReadinessFailureThreshold uint32 `protobuf:"varint,22,opt,name=readinessFailureThreshold,proto3" json:"readinessFailureThreshold,omitempty"` + // Configures the startup probe for the istio-proxy container. + StartupProbe *StartupProbe `protobuf:"bytes,41,opt,name=startupProbe,proto3" json:"startupProbe,omitempty"` + // Default port used for the Pilot agent's health checks. + StatusPort uint32 `protobuf:"varint,23,opt,name=statusPort,proto3" json:"statusPort,omitempty"` + // K8s resources settings. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Resources *Resources `protobuf:"bytes,24,opt,name=resources,proto3" json:"resources,omitempty"` + // Specify which tracer to use. One of: zipkin, lightstep, datadog, stackdriver. + // If using stackdriver tracer outside GCP, set env GOOGLE_APPLICATION_CREDENTIALS to the GCP credential file. + Tracer Tracer `protobuf:"varint,25,opt,name=tracer,proto3,enum=v1alpha1.Tracer" json:"tracer,omitempty"` + // A comma separated list of outbound ports to be excluded from redirection to Envoy. + ExcludeOutboundPorts string `protobuf:"bytes,28,opt,name=excludeOutboundPorts,proto3" json:"excludeOutboundPorts,omitempty"` + // The k8s lifecycle hooks definition (pod.spec.containers.lifecycle) for the proxy container. + // More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + Lifecycle *v1.Lifecycle `protobuf:"bytes,36,opt,name=lifecycle,proto3" json:"lifecycle,omitempty"` + // Controls if sidecar is injected at the front of the container list and blocks the start of the other containers until the proxy is ready + // + // Deprecated: replaced by ProxyConfig setting which allows per-pod configuration of this behavior. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + HoldApplicationUntilProxyStarts *wrapperspb.BoolValue `protobuf:"bytes,37,opt,name=holdApplicationUntilProxyStarts,proto3" json:"holdApplicationUntilProxyStarts,omitempty"` + // A comma separated list of inbound ports for which traffic is to be redirected to Envoy. + // The wildcard character '*' can be used to configure redirection for all ports. + IncludeInboundPorts string `protobuf:"bytes,38,opt,name=includeInboundPorts,proto3" json:"includeInboundPorts,omitempty"` + // A comma separated list of outbound ports for which traffic is to be redirected to Envoy, regardless of the destination IP. + IncludeOutboundPorts string `protobuf:"bytes,39,opt,name=includeOutboundPorts,proto3" json:"includeOutboundPorts,omitempty"` +} + +func (x *ProxyConfig) Reset() { + *x = ProxyConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProxyConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProxyConfig) ProtoMessage() {} + +func (x *ProxyConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProxyConfig.ProtoReflect.Descriptor instead. +func (*ProxyConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{30} +} + +func (x *ProxyConfig) GetAutoInject() string { + if x != nil { + return x.AutoInject + } + return "" +} + +func (x *ProxyConfig) GetClusterDomain() string { + if x != nil { + return x.ClusterDomain + } + return "" +} + +func (x *ProxyConfig) GetComponentLogLevel() string { + if x != nil { + return x.ComponentLogLevel + } + return "" +} + +func (x *ProxyConfig) GetEnableCoreDump() *wrapperspb.BoolValue { + if x != nil { + return x.EnableCoreDump + } + return nil +} + +func (x *ProxyConfig) GetExcludeInboundPorts() string { + if x != nil { + return x.ExcludeInboundPorts + } + return "" +} + +func (x *ProxyConfig) GetExcludeIPRanges() string { + if x != nil { + return x.ExcludeIPRanges + } + return "" +} + +func (x *ProxyConfig) GetImage() string { + if x != nil { + return x.Image + } + return "" +} + +func (x *ProxyConfig) GetIncludeIPRanges() string { + if x != nil { + return x.IncludeIPRanges + } + return "" +} + +func (x *ProxyConfig) GetLogLevel() string { + if x != nil { + return x.LogLevel + } + return "" +} + +func (x *ProxyConfig) GetOutlierLogPath() string { + if x != nil { + return x.OutlierLogPath + } + return "" +} + +func (x *ProxyConfig) GetPrivileged() *wrapperspb.BoolValue { + if x != nil { + return x.Privileged + } + return nil +} + +func (x *ProxyConfig) GetReadinessInitialDelaySeconds() uint32 { + if x != nil { + return x.ReadinessInitialDelaySeconds + } + return 0 +} + +func (x *ProxyConfig) GetReadinessPeriodSeconds() uint32 { + if x != nil { + return x.ReadinessPeriodSeconds + } + return 0 +} + +func (x *ProxyConfig) GetReadinessFailureThreshold() uint32 { + if x != nil { + return x.ReadinessFailureThreshold + } + return 0 +} + +func (x *ProxyConfig) GetStartupProbe() *StartupProbe { + if x != nil { + return x.StartupProbe + } + return nil +} + +func (x *ProxyConfig) GetStatusPort() uint32 { + if x != nil { + return x.StatusPort + } + return 0 +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *ProxyConfig) GetResources() *Resources { + if x != nil { + return x.Resources + } + return nil +} + +func (x *ProxyConfig) GetTracer() Tracer { + if x != nil { + return x.Tracer + } + return Tracer_zipkin +} + +func (x *ProxyConfig) GetExcludeOutboundPorts() string { + if x != nil { + return x.ExcludeOutboundPorts + } + return "" +} + +func (x *ProxyConfig) GetLifecycle() *v1.Lifecycle { + if x != nil { + return x.Lifecycle + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *ProxyConfig) GetHoldApplicationUntilProxyStarts() *wrapperspb.BoolValue { + if x != nil { + return x.HoldApplicationUntilProxyStarts + } + return nil +} + +func (x *ProxyConfig) GetIncludeInboundPorts() string { + if x != nil { + return x.IncludeInboundPorts + } + return "" +} + +func (x *ProxyConfig) GetIncludeOutboundPorts() string { + if x != nil { + return x.IncludeOutboundPorts + } + return "" +} + +type StartupProbe struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Enables or disables a startup probe. + // For optimal startup times, changing this should be tied to the readiness probe values. + // + // If the probe is enabled, it is recommended to have delay=0s,period=15s,failureThreshold=4. + // This ensures the pod is marked ready immediately after the startup probe passes (which has a 1s poll interval), + // and doesn't spam the readiness endpoint too much + // + // If the probe is disabled, it is recommended to have delay=1s,period=2s,failureThreshold=30. + // This ensures the startup is reasonable fast (polling every 2s). 1s delay is used since the startup is not often ready instantly. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // Minimum consecutive failures for the probe to be considered failed after having succeeded. + FailureThreshold uint32 `protobuf:"varint,2,opt,name=failureThreshold,proto3" json:"failureThreshold,omitempty"` +} + +func (x *StartupProbe) Reset() { + *x = StartupProbe{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartupProbe) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartupProbe) ProtoMessage() {} + +func (x *StartupProbe) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartupProbe.ProtoReflect.Descriptor instead. +func (*StartupProbe) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{31} +} + +func (x *StartupProbe) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *StartupProbe) GetFailureThreshold() uint32 { + if x != nil { + return x.FailureThreshold + } + return 0 +} + +// Configuration for proxy_init container which sets the pods' networking to intercept the inbound/outbound traffic. +type ProxyInitConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Specifies the image for the proxy_init container. + Image string `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` + // K8s resources settings. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Resources *Resources `protobuf:"bytes,5,opt,name=resources,proto3" json:"resources,omitempty"` +} + +func (x *ProxyInitConfig) Reset() { + *x = ProxyInitConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProxyInitConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProxyInitConfig) ProtoMessage() {} + +func (x *ProxyInitConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProxyInitConfig.ProtoReflect.Descriptor instead. +func (*ProxyInitConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{32} +} + +func (x *ProxyInitConfig) GetImage() string { + if x != nil { + return x.Image + } + return "" +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *ProxyInitConfig) GetResources() *Resources { + if x != nil { + return x.Resources + } + return nil +} + +// Configuration for K8s resource requests. +type ResourcesRequestsConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // CPU requests. + Cpu string `protobuf:"bytes,1,opt,name=cpu,proto3" json:"cpu,omitempty"` + // Memory requests. + Memory string `protobuf:"bytes,2,opt,name=memory,proto3" json:"memory,omitempty"` +} + +func (x *ResourcesRequestsConfig) Reset() { + *x = ResourcesRequestsConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResourcesRequestsConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResourcesRequestsConfig) ProtoMessage() {} + +func (x *ResourcesRequestsConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResourcesRequestsConfig.ProtoReflect.Descriptor instead. +func (*ResourcesRequestsConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{33} +} + +func (x *ResourcesRequestsConfig) GetCpu() string { + if x != nil { + return x.Cpu + } + return "" +} + +func (x *ResourcesRequestsConfig) GetMemory() string { + if x != nil { + return x.Memory + } + return "" +} + +// Configuration for the SecretDiscoveryService instead of using K8S secrets to mount the certificates. +type SDSConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + Token *structpb.Struct `protobuf:"bytes,5,opt,name=token,proto3" json:"token,omitempty"` +} + +func (x *SDSConfig) Reset() { + *x = SDSConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SDSConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SDSConfig) ProtoMessage() {} + +func (x *SDSConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SDSConfig.ProtoReflect.Descriptor instead. +func (*SDSConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{34} +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *SDSConfig) GetToken() *structpb.Struct { + if x != nil { + return x.Token + } + return nil +} + +// Configuration for secret volume mounts. +// +// See https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets. +type SecretVolume struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MountPath string `protobuf:"bytes,1,opt,name=mountPath,proto3" json:"mountPath,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + SecretName string `protobuf:"bytes,3,opt,name=secretName,proto3" json:"secretName,omitempty"` +} + +func (x *SecretVolume) Reset() { + *x = SecretVolume{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SecretVolume) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SecretVolume) ProtoMessage() {} + +func (x *SecretVolume) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[35] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SecretVolume.ProtoReflect.Descriptor instead. +func (*SecretVolume) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{35} +} + +func (x *SecretVolume) GetMountPath() string { + if x != nil { + return x.MountPath + } + return "" +} + +func (x *SecretVolume) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *SecretVolume) GetSecretName() string { + if x != nil { + return x.SecretName + } + return "" +} + +// SidecarInjectorConfig is described in istio.io documentation. +type SidecarInjectorConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Enables sidecar auto-injection in namespaces by default. + EnableNamespacesByDefault *wrapperspb.BoolValue `protobuf:"bytes,2,opt,name=enableNamespacesByDefault,proto3" json:"enableNamespacesByDefault,omitempty"` + // Setting this to `IfNeeded` will result in the sidecar injector being run again if additional mutations occur. Default: Never + ReinvocationPolicy string `protobuf:"bytes,3,opt,name=reinvocationPolicy,proto3" json:"reinvocationPolicy,omitempty"` + // Instructs Istio to not inject the sidecar on those pods, based on labels that are present in those pods. + // + // Annotations in the pods have higher precedence than the label selectors. + // Order of evaluation: Pod Annotations → NeverInjectSelector → AlwaysInjectSelector → Default Policy. + // See https://istio.io/docs/setup/kubernetes/additional-setup/sidecar-injection/#more-control-adding-exceptions + NeverInjectSelector []*v11.LabelSelector `protobuf:"bytes,11,rep,name=neverInjectSelector,proto3" json:"neverInjectSelector,omitempty"` + // See NeverInjectSelector. + AlwaysInjectSelector []*v11.LabelSelector `protobuf:"bytes,12,rep,name=alwaysInjectSelector,proto3" json:"alwaysInjectSelector,omitempty"` + // If true, webhook or istioctl injector will rewrite PodSpec for liveness health check to redirect request to sidecar. This makes liveness check work even when mTLS is enabled. + RewriteAppHTTPProbe *wrapperspb.BoolValue `protobuf:"bytes,16,opt,name=rewriteAppHTTPProbe,proto3" json:"rewriteAppHTTPProbe,omitempty"` + // injectedAnnotations are additional annotations that will be added to the pod spec after injection + // This is primarily to support PSP annotations. + InjectedAnnotations *structpb.Struct `protobuf:"bytes,19,opt,name=injectedAnnotations,proto3" json:"injectedAnnotations,omitempty"` + // Configure the injection url for sidecar injector webhook + InjectionURL string `protobuf:"bytes,22,opt,name=injectionURL,proto3" json:"injectionURL,omitempty"` + // Templates defines a set of custom injection templates that can be used. For example, defining: + // + // templates: + // + // hello: | + // metadata: + // labels: + // hello: world + // + // Then starting a pod with the `inject.istio.io/templates: hello` annotation, will result in the pod + // being injected with the hello=world labels. + // This is intended for advanced configuration only; most users should use the built in template + Templates *structpb.Struct `protobuf:"bytes,23,opt,name=templates,proto3" json:"templates,omitempty"` + // defaultTemplates: ["sidecar", "hello"] + DefaultTemplates []string `protobuf:"bytes,24,rep,name=defaultTemplates,proto3" json:"defaultTemplates,omitempty"` +} + +func (x *SidecarInjectorConfig) Reset() { + *x = SidecarInjectorConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SidecarInjectorConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SidecarInjectorConfig) ProtoMessage() {} + +func (x *SidecarInjectorConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SidecarInjectorConfig.ProtoReflect.Descriptor instead. +func (*SidecarInjectorConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{36} +} + +func (x *SidecarInjectorConfig) GetEnableNamespacesByDefault() *wrapperspb.BoolValue { + if x != nil { + return x.EnableNamespacesByDefault + } + return nil +} + +func (x *SidecarInjectorConfig) GetReinvocationPolicy() string { + if x != nil { + return x.ReinvocationPolicy + } + return "" +} + +func (x *SidecarInjectorConfig) GetNeverInjectSelector() []*v11.LabelSelector { + if x != nil { + return x.NeverInjectSelector + } + return nil +} + +func (x *SidecarInjectorConfig) GetAlwaysInjectSelector() []*v11.LabelSelector { + if x != nil { + return x.AlwaysInjectSelector + } + return nil +} + +func (x *SidecarInjectorConfig) GetRewriteAppHTTPProbe() *wrapperspb.BoolValue { + if x != nil { + return x.RewriteAppHTTPProbe + } + return nil +} + +func (x *SidecarInjectorConfig) GetInjectedAnnotations() *structpb.Struct { + if x != nil { + return x.InjectedAnnotations + } + return nil +} + +func (x *SidecarInjectorConfig) GetInjectionURL() string { + if x != nil { + return x.InjectionURL + } + return "" +} + +func (x *SidecarInjectorConfig) GetTemplates() *structpb.Struct { + if x != nil { + return x.Templates + } + return nil +} + +func (x *SidecarInjectorConfig) GetDefaultTemplates() []string { + if x != nil { + return x.DefaultTemplates + } + return nil +} + +// Configuration for each of the supported tracers. +type TracerConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Configuration for the datadog tracing service. + Datadog *TracerDatadogConfig `protobuf:"bytes,1,opt,name=datadog,proto3" json:"datadog,omitempty"` + // Configuration for the lightstep tracing service. + Lightstep *TracerLightStepConfig `protobuf:"bytes,2,opt,name=lightstep,proto3" json:"lightstep,omitempty"` + // Configuration for the zipkin tracing service. + Zipkin *TracerZipkinConfig `protobuf:"bytes,3,opt,name=zipkin,proto3" json:"zipkin,omitempty"` + // Configuration for the stackdriver tracing service. + Stackdriver *TracerStackdriverConfig `protobuf:"bytes,4,opt,name=stackdriver,proto3" json:"stackdriver,omitempty"` +} + +func (x *TracerConfig) Reset() { + *x = TracerConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TracerConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TracerConfig) ProtoMessage() {} + +func (x *TracerConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TracerConfig.ProtoReflect.Descriptor instead. +func (*TracerConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{37} +} + +func (x *TracerConfig) GetDatadog() *TracerDatadogConfig { + if x != nil { + return x.Datadog + } + return nil +} + +func (x *TracerConfig) GetLightstep() *TracerLightStepConfig { + if x != nil { + return x.Lightstep + } + return nil +} + +func (x *TracerConfig) GetZipkin() *TracerZipkinConfig { + if x != nil { + return x.Zipkin + } + return nil +} + +func (x *TracerConfig) GetStackdriver() *TracerStackdriverConfig { + if x != nil { + return x.Stackdriver + } + return nil +} + +// Configuration for the datadog tracing service. +type TracerDatadogConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Address in host:port format for reporting trace data to the Datadog agent. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (x *TracerDatadogConfig) Reset() { + *x = TracerDatadogConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TracerDatadogConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TracerDatadogConfig) ProtoMessage() {} + +func (x *TracerDatadogConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TracerDatadogConfig.ProtoReflect.Descriptor instead. +func (*TracerDatadogConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{38} +} + +func (x *TracerDatadogConfig) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +// Configuration for the lightstep tracing service. +type TracerLightStepConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Sets the lightstep satellite pool address in host:port format for reporting trace data. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // Sets the lightstep access token. + AccessToken string `protobuf:"bytes,2,opt,name=accessToken,proto3" json:"accessToken,omitempty"` +} + +func (x *TracerLightStepConfig) Reset() { + *x = TracerLightStepConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TracerLightStepConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TracerLightStepConfig) ProtoMessage() {} + +func (x *TracerLightStepConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[39] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TracerLightStepConfig.ProtoReflect.Descriptor instead. +func (*TracerLightStepConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{39} +} + +func (x *TracerLightStepConfig) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *TracerLightStepConfig) GetAccessToken() string { + if x != nil { + return x.AccessToken + } + return "" +} + +// Configuration for the zipkin tracing service. +type TracerZipkinConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Address of zipkin instance in host:port format for reporting trace data. + // + // Example: .:941 + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (x *TracerZipkinConfig) Reset() { + *x = TracerZipkinConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TracerZipkinConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TracerZipkinConfig) ProtoMessage() {} + +func (x *TracerZipkinConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[40] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TracerZipkinConfig.ProtoReflect.Descriptor instead. +func (*TracerZipkinConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{40} +} + +func (x *TracerZipkinConfig) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +// Configuration for the stackdriver tracing service. +type TracerStackdriverConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // enables trace output to stdout. + Debug *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=debug,proto3" json:"debug,omitempty"` + // The global default max number of attributes per span. + MaxNumberOfAttributes uint32 `protobuf:"varint,2,opt,name=maxNumberOfAttributes,proto3" json:"maxNumberOfAttributes,omitempty"` + // The global default max number of annotation events per span. + MaxNumberOfAnnotations uint32 `protobuf:"varint,3,opt,name=maxNumberOfAnnotations,proto3" json:"maxNumberOfAnnotations,omitempty"` + // The global default max number of message events per span. + MaxNumberOfMessageEvents uint32 `protobuf:"varint,4,opt,name=maxNumberOfMessageEvents,proto3" json:"maxNumberOfMessageEvents,omitempty"` +} + +func (x *TracerStackdriverConfig) Reset() { + *x = TracerStackdriverConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TracerStackdriverConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TracerStackdriverConfig) ProtoMessage() {} + +func (x *TracerStackdriverConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TracerStackdriverConfig.ProtoReflect.Descriptor instead. +func (*TracerStackdriverConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{41} +} + +func (x *TracerStackdriverConfig) GetDebug() *wrapperspb.BoolValue { + if x != nil { + return x.Debug + } + return nil +} + +func (x *TracerStackdriverConfig) GetMaxNumberOfAttributes() uint32 { + if x != nil { + return x.MaxNumberOfAttributes + } + return 0 +} + +func (x *TracerStackdriverConfig) GetMaxNumberOfAnnotations() uint32 { + if x != nil { + return x.MaxNumberOfAnnotations + } + return 0 +} + +func (x *TracerStackdriverConfig) GetMaxNumberOfMessageEvents() uint32 { + if x != nil { + return x.MaxNumberOfMessageEvents + } + return 0 +} + +type BaseConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // For Helm2 use, adds the CRDs to templates. + EnableCRDTemplates *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enableCRDTemplates,proto3" json:"enableCRDTemplates,omitempty"` + // URL to use for validating webhook. + ValidationURL string `protobuf:"bytes,2,opt,name=validationURL,proto3" json:"validationURL,omitempty"` + // For istioctl usage to disable istio config crds in base + EnableIstioConfigCRDs *wrapperspb.BoolValue `protobuf:"bytes,3,opt,name=enableIstioConfigCRDs,proto3" json:"enableIstioConfigCRDs,omitempty"` + ValidateGateway *wrapperspb.BoolValue `protobuf:"bytes,4,opt,name=validateGateway,proto3" json:"validateGateway,omitempty"` + // validation webhook CA bundle + ValidationCABundle string `protobuf:"bytes,5,opt,name=validationCABundle,proto3" json:"validationCABundle,omitempty"` +} + +func (x *BaseConfig) Reset() { + *x = BaseConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BaseConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BaseConfig) ProtoMessage() {} + +func (x *BaseConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[42] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BaseConfig.ProtoReflect.Descriptor instead. +func (*BaseConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{42} +} + +func (x *BaseConfig) GetEnableCRDTemplates() *wrapperspb.BoolValue { + if x != nil { + return x.EnableCRDTemplates + } + return nil +} + +func (x *BaseConfig) GetValidationURL() string { + if x != nil { + return x.ValidationURL + } + return "" +} + +func (x *BaseConfig) GetEnableIstioConfigCRDs() *wrapperspb.BoolValue { + if x != nil { + return x.EnableIstioConfigCRDs + } + return nil +} + +func (x *BaseConfig) GetValidateGateway() *wrapperspb.BoolValue { + if x != nil { + return x.ValidateGateway + } + return nil +} + +func (x *BaseConfig) GetValidationCABundle() string { + if x != nil { + return x.ValidationCABundle + } + return "" +} + +type IstiodRemoteConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // URL to use for sidecar injector webhook. + InjectionURL string `protobuf:"bytes,1,opt,name=injectionURL,proto3" json:"injectionURL,omitempty"` + // Path to use for the sidecar injector webhook service. + InjectionPath string `protobuf:"bytes,2,opt,name=injectionPath,proto3" json:"injectionPath,omitempty"` + // injector ca bundle + InjectionCABundle string `protobuf:"bytes,3,opt,name=injectionCABundle,proto3" json:"injectionCABundle,omitempty"` +} + +func (x *IstiodRemoteConfig) Reset() { + *x = IstiodRemoteConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IstiodRemoteConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IstiodRemoteConfig) ProtoMessage() {} + +func (x *IstiodRemoteConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[43] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IstiodRemoteConfig.ProtoReflect.Descriptor instead. +func (*IstiodRemoteConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{43} +} + +func (x *IstiodRemoteConfig) GetInjectionURL() string { + if x != nil { + return x.InjectionURL + } + return "" +} + +func (x *IstiodRemoteConfig) GetInjectionPath() string { + if x != nil { + return x.InjectionPath + } + return "" +} + +func (x *IstiodRemoteConfig) GetInjectionCABundle() string { + if x != nil { + return x.InjectionCABundle + } + return "" +} + +type Values struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Configuration for the Istio CNI plugin. + Cni *CNIConfig `protobuf:"bytes,2,opt,name=cni,proto3" json:"cni,omitempty"` + // Configuration for ingress and egress gateways. + Gateways *GatewaysConfig `protobuf:"bytes,5,opt,name=gateways,proto3" json:"gateways,omitempty"` + // Global configuration for Istio components. + Global *GlobalConfig `protobuf:"bytes,6,opt,name=global,proto3" json:"global,omitempty"` + // Configuration for the Pilot component. + Pilot *PilotConfig `protobuf:"bytes,10,opt,name=pilot,proto3" json:"pilot,omitempty"` + // Configuration for the ZTunnel component. + Ztunnel *structpb.Value `protobuf:"bytes,41,opt,name=ztunnel,proto3" json:"ztunnel,omitempty"` + // Controls whether telemetry is exported for Pilot. + Telemetry *TelemetryConfig `protobuf:"bytes,23,opt,name=telemetry,proto3" json:"telemetry,omitempty"` + // Configuration for the sidecar injector webhook. + SidecarInjectorWebhook *SidecarInjectorConfig `protobuf:"bytes,13,opt,name=sidecarInjectorWebhook,proto3" json:"sidecarInjectorWebhook,omitempty"` + // Configuration for the Istio CNI plugin. + // + // Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. + IstioCni *CNIUsageConfig `protobuf:"bytes,19,opt,name=istio_cni,json=istioCni,proto3" json:"istio_cni,omitempty"` + // Identifies the revision this installation is associated with. + Revision string `protobuf:"bytes,21,opt,name=revision,proto3" json:"revision,omitempty"` + // Used internally to identify the owner of each resource. + OwnerName string `protobuf:"bytes,22,opt,name=ownerName,proto3" json:"ownerName,omitempty"` + // Defines runtime configuration of components, including Istiod and istio-agent behavior. + // See https://istio.io/docs/reference/config/istio.mesh.v1alpha1/ for all available options. + // TODO can this import the real mesh config API? + MeshConfig *structpb.Value `protobuf:"bytes,36,opt,name=meshConfig,proto3" json:"meshConfig,omitempty"` + // Configuration for the base component. + Base *BaseConfig `protobuf:"bytes,37,opt,name=base,proto3" json:"base,omitempty"` + // Configuration for istiod-remote. + IstiodRemote *IstiodRemoteConfig `protobuf:"bytes,38,opt,name=istiodRemote,proto3" json:"istiodRemote,omitempty"` + // Specifies the aliases for the Istio control plane revision. A MutatingWebhookConfiguration + // is created for each alias. + RevisionTags []string `protobuf:"bytes,39,rep,name=revisionTags,proto3" json:"revisionTags,omitempty"` + // The name of the default revision in the cluster. + DefaultRevision string `protobuf:"bytes,40,opt,name=defaultRevision,proto3" json:"defaultRevision,omitempty"` + // Specifies which installation configuration profile to apply. + Profile string `protobuf:"bytes,42,opt,name=profile,proto3" json:"profile,omitempty"` + // Specifies the compatibility version to use. When this is set, the control plane will + // be configured with the same defaults as the specified version. + CompatibilityVersion string `protobuf:"bytes,43,opt,name=compatibilityVersion,proto3" json:"compatibilityVersion,omitempty"` + // Specifies experimental helm fields that could be removed or changed in the future + Experimental *ExperimentalConfig `protobuf:"bytes,44,opt,name=experimental,proto3" json:"experimental,omitempty"` +} + +func (x *Values) Reset() { + *x = Values{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Values) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Values) ProtoMessage() {} + +func (x *Values) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[44] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Values.ProtoReflect.Descriptor instead. +func (*Values) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{44} +} + +func (x *Values) GetCni() *CNIConfig { + if x != nil { + return x.Cni + } + return nil +} + +func (x *Values) GetGateways() *GatewaysConfig { + if x != nil { + return x.Gateways + } + return nil +} + +func (x *Values) GetGlobal() *GlobalConfig { + if x != nil { + return x.Global + } + return nil +} + +func (x *Values) GetPilot() *PilotConfig { + if x != nil { + return x.Pilot + } + return nil +} + +func (x *Values) GetZtunnel() *structpb.Value { + if x != nil { + return x.Ztunnel + } + return nil +} + +func (x *Values) GetTelemetry() *TelemetryConfig { + if x != nil { + return x.Telemetry + } + return nil +} + +func (x *Values) GetSidecarInjectorWebhook() *SidecarInjectorConfig { + if x != nil { + return x.SidecarInjectorWebhook + } + return nil +} + +// Deprecated: Marked as deprecated in pkg/apis/istio/v1alpha1/values_types.proto. +func (x *Values) GetIstioCni() *CNIUsageConfig { + if x != nil { + return x.IstioCni + } + return nil +} + +func (x *Values) GetRevision() string { + if x != nil { + return x.Revision + } + return "" +} + +func (x *Values) GetOwnerName() string { + if x != nil { + return x.OwnerName + } + return "" +} + +func (x *Values) GetMeshConfig() *structpb.Value { + if x != nil { + return x.MeshConfig + } + return nil +} + +func (x *Values) GetBase() *BaseConfig { + if x != nil { + return x.Base + } + return nil +} + +func (x *Values) GetIstiodRemote() *IstiodRemoteConfig { + if x != nil { + return x.IstiodRemote + } + return nil +} + +func (x *Values) GetRevisionTags() []string { + if x != nil { + return x.RevisionTags + } + return nil +} + +func (x *Values) GetDefaultRevision() string { + if x != nil { + return x.DefaultRevision + } + return "" +} + +func (x *Values) GetProfile() string { + if x != nil { + return x.Profile + } + return "" +} + +func (x *Values) GetCompatibilityVersion() string { + if x != nil { + return x.CompatibilityVersion + } + return "" +} + +func (x *Values) GetExperimental() *ExperimentalConfig { + if x != nil { + return x.Experimental + } + return nil +} + +// ZeroVPNConfig enables cross-cluster access using SNI matching. +type ZeroVPNConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether ZeroVPN is enabled. + Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + Suffix string `protobuf:"bytes,2,opt,name=suffix,proto3" json:"suffix,omitempty"` +} + +func (x *ZeroVPNConfig) Reset() { + *x = ZeroVPNConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ZeroVPNConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ZeroVPNConfig) ProtoMessage() {} + +func (x *ZeroVPNConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[45] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ZeroVPNConfig.ProtoReflect.Descriptor instead. +func (*ZeroVPNConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{45} +} + +func (x *ZeroVPNConfig) GetEnabled() *wrapperspb.BoolValue { + if x != nil { + return x.Enabled + } + return nil +} + +func (x *ZeroVPNConfig) GetSuffix() string { + if x != nil { + return x.Suffix + } + return "" +} + +// ExperimentalConfig is a placeholder for experimental installation features. +type ExperimentalConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Controls whether the experimental feature is enabled. + StableValidationPolicy *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=stableValidationPolicy,proto3" json:"stableValidationPolicy,omitempty"` +} + +func (x *ExperimentalConfig) Reset() { + *x = ExperimentalConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExperimentalConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExperimentalConfig) ProtoMessage() {} + +func (x *ExperimentalConfig) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[46] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExperimentalConfig.ProtoReflect.Descriptor instead. +func (*ExperimentalConfig) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{46} +} + +func (x *ExperimentalConfig) GetStableValidationPolicy() *wrapperspb.BoolValue { + if x != nil { + return x.StableValidationPolicy + } + return nil +} + +// IntOrString is a type that can hold an int32 or a string. When used in +// JSON or YAML marshalling and unmarshalling, it produces or consumes the +// inner type. This allows you to have, for example, a JSON field that can +// accept a name or number. +// TODO: Rename to Int32OrString +// +// +protobuf=true +// +protobuf.options.(gogoproto.goproto_stringer)=false +// +k8s:openapi-gen=true +type IntOrString struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type int64 `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"` + IntVal *wrapperspb.Int32Value `protobuf:"bytes,2,opt,name=intVal,proto3" json:"intVal,omitempty"` + StrVal *wrapperspb.StringValue `protobuf:"bytes,3,opt,name=strVal,proto3" json:"strVal,omitempty"` +} + +func (x *IntOrString) Reset() { + *x = IntOrString{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IntOrString) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IntOrString) ProtoMessage() {} + +func (x *IntOrString) ProtoReflect() protoreflect.Message { + mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[47] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IntOrString.ProtoReflect.Descriptor instead. +func (*IntOrString) Descriptor() ([]byte, []int) { + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{47} +} + +func (x *IntOrString) GetType() int64 { + if x != nil { + return x.Type + } + return 0 +} + +func (x *IntOrString) GetIntVal() *wrapperspb.Int32Value { + if x != nil { + return x.IntVal + } + return nil +} + +func (x *IntOrString) GetStrVal() *wrapperspb.StringValue { + if x != nil { + return x.StrVal + } + return nil +} + +var File_pkg_apis_istio_v1alpha1_values_types_proto protoreflect.FileDescriptor + +var file_pkg_apis_istio_v1alpha1_values_types_proto_rawDesc = []byte{ + 0x0a, 0x2a, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x69, 0x73, 0x74, 0x69, 0x6f, + 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x22, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x72, 0x65, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x34, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x6d, + 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, + 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x68, 0x0a, 0x0a, 0x41, 0x72, 0x63, + 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6d, 0x64, 0x36, 0x34, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x61, 0x6d, 0x64, 0x36, 0x34, 0x12, 0x18, 0x0a, + 0x07, 0x70, 0x70, 0x63, 0x36, 0x34, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x70, 0x70, 0x63, 0x36, 0x34, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x33, 0x39, 0x30, 0x78, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x33, 0x39, 0x30, 0x78, 0x12, 0x14, 0x0a, + 0x05, 0x61, 0x72, 0x6d, 0x36, 0x34, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x61, 0x72, + 0x6d, 0x36, 0x34, 0x22, 0x91, 0x09, 0x0a, 0x09, 0x43, 0x4e, 0x49, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x68, 0x75, 0x62, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x68, 0x75, 0x62, 0x12, 0x28, 0x0a, 0x03, 0x74, 0x61, 0x67, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, + 0x74, 0x61, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x18, 0x1d, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x75, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x75, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6e, 0x69, 0x42, 0x69, 0x6e, 0x44, 0x69, 0x72, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6e, 0x69, 0x42, 0x69, 0x6e, 0x44, 0x69, + 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6e, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x44, 0x69, 0x72, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6e, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x44, 0x69, + 0x72, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6e, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x46, 0x69, 0x6c, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6e, 0x69, 0x43, + 0x6f, 0x6e, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, + 0x6e, 0x69, 0x4e, 0x65, 0x74, 0x6e, 0x73, 0x44, 0x69, 0x72, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x63, 0x6e, 0x69, 0x4e, 0x65, 0x74, 0x6e, 0x73, 0x44, 0x69, 0x72, 0x12, 0x2c, 0x0a, + 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x61, + 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x52, 0x08, 0x61, 0x66, 0x66, + 0x69, 0x6e, 0x69, 0x74, 0x79, 0x12, 0x43, 0x0a, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x70, 0x6f, 0x64, 0x41, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x73, + 0x70, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x73, 0x70, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x1e, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x12, 0x37, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x18, + 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x12, 0x31, 0x0a, + 0x06, 0x72, 0x65, 0x70, 0x61, 0x69, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x4e, 0x49, 0x52, 0x65, 0x70, 0x61, + 0x69, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x72, 0x65, 0x70, 0x61, 0x69, 0x72, + 0x12, 0x34, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x12, 0x31, 0x0a, 0x09, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x0a, + 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x18, 0x01, + 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x64, 0x12, 0x4a, 0x0a, 0x0e, + 0x73, 0x65, 0x63, 0x63, 0x6f, 0x6d, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x13, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x63, 0x6f, 0x6d, + 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x63, 0x6f, 0x6d, + 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x34, 0x0a, 0x07, 0x61, 0x6d, 0x62, 0x69, + 0x65, 0x6e, 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x4e, 0x49, 0x41, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x15, 0x72, 0x6f, + 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x4f, 0x72, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x52, 0x15, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x9c, 0x01, 0x0a, 0x0e, 0x43, 0x4e, 0x49, 0x55, + 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, + 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x12, 0x38, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0xd2, 0x01, 0x0a, 0x10, 0x43, 0x4e, 0x49, 0x41, 0x6d, + 0x62, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, + 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x69, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x69, 0x72, 0x12, + 0x3a, 0x0a, 0x0a, 0x64, 0x6e, 0x73, 0x43, 0x61, 0x70, 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x0a, 0x64, 0x6e, 0x73, 0x43, 0x61, 0x70, 0x74, 0x75, 0x72, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x69, + 0x70, 0x76, 0x36, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x22, 0xad, 0x03, 0x0a, 0x0f, + 0x43, 0x4e, 0x49, 0x52, 0x65, 0x70, 0x61, 0x69, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x68, 0x75, 0x62, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x68, 0x75, 0x62, 0x12, 0x28, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x74, 0x61, + 0x67, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x50, 0x6f, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x61, 0x69, 0x72, 0x50, + 0x6f, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x61, 0x69, + 0x72, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, + 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x0a, + 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x64, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x2c, 0x0a, + 0x11, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x6f, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x4b, + 0x65, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x6e, + 0x50, 0x6f, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x13, 0x62, + 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x6f, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x6e, + 0x50, 0x6f, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a, + 0x11, 0x69, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x69, 0x6e, 0x69, 0x74, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x5a, 0x0a, 0x0e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x12, 0x34, 0x0a, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x22, 0x55, 0x0a, 0x17, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x3a, 0x0a, 0x18, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x76, 0x65, 0x72, + 0x61, 0x67, 0x65, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x18, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x76, 0x65, 0x72, + 0x61, 0x67, 0x65, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xfb, + 0x01, 0x0a, 0x09, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x06, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x3b, 0x0a, 0x0d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4b, 0x0a, 0x0e, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x39, + 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0b, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x58, 0x0a, 0x20, 0x44, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x22, 0x57, 0x0a, 0x16, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3d, 0x0a, + 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x21, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x22, 0xe1, 0x0e, 0x0a, + 0x13, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x46, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x61, 0x75, 0x74, 0x6f, + 0x73, 0x63, 0x61, 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x0a, 0x0c, + 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x61, 0x78, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x61, 0x78, + 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x69, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x4d, 0x69, 0x6e, 0x12, 0x3d, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x6d, 0x65, 0x6d, + 0x6f, 0x72, 0x79, 0x12, 0x37, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x21, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x40, 0x0a, 0x0d, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, + 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, + 0x41, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x29, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x67, 0x72, 0x65, 0x73, + 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x43, 0x0a, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x70, 0x6f, + 0x64, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x7b, 0x0a, 0x1c, + 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x74, 0x69, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0c, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x6d, + 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, + 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, 0x52, 0x1c, 0x70, 0x6f, 0x64, + 0x41, 0x6e, 0x74, 0x69, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x83, 0x01, 0x0a, 0x20, 0x70, 0x6f, + 0x64, 0x41, 0x6e, 0x74, 0x69, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x54, 0x65, 0x72, + 0x6d, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0d, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, + 0x69, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, + 0x70, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, 0x52, 0x20, 0x70, + 0x6f, 0x64, 0x41, 0x6e, 0x74, 0x69, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x54, 0x65, + 0x72, 0x6d, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, + 0x2b, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x35, 0x0a, 0x09, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0d, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x73, 0x12, 0x47, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, + 0x0a, 0x04, 0x7a, 0x76, 0x70, 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x5a, 0x65, 0x72, 0x6f, 0x56, 0x50, 0x4e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x04, 0x7a, 0x76, 0x70, 0x6e, 0x12, 0x44, 0x0a, 0x0b, 0x74, + 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x53, + 0x75, 0x72, 0x67, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x4f, 0x72, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0f, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, + 0x78, 0x53, 0x75, 0x72, 0x67, 0x65, 0x12, 0x4f, 0x0a, 0x15, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, + 0x67, 0x4d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, + 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x49, 0x6e, 0x74, 0x4f, 0x72, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x02, 0x18, 0x01, + 0x52, 0x15, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x3d, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x18, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x14, 0x61, + 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x41, 0x73, 0x52, 0x6f, 0x6f, 0x74, + 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x41, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x2c, 0x0a, + 0x11, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x40, 0x0a, 0x0e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x1c, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x0e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, + 0x0a, 0x69, 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x1d, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0a, 0x69, 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x26, 0x0a, + 0x0e, 0x69, 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, + 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0xed, 0x02, 0x0a, 0x0e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x13, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x5f, 0x65, 0x67, 0x72, + 0x65, 0x73, 0x73, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x67, 0x72, 0x65, + 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x13, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x2d, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x52, 0x0a, 0x14, 0x69, 0x73, + 0x74, 0x69, 0x6f, 0x5f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x67, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x14, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x2d, + 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x40, + 0x0a, 0x0f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x0f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x12, 0x3e, 0x0a, 0x0e, 0x73, 0x65, 0x63, 0x63, 0x6f, 0x6d, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x0e, 0x73, 0x65, 0x63, 0x63, 0x6f, 0x6d, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x22, 0xff, 0x10, 0x0a, 0x0c, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x2c, 0x0a, 0x04, 0x61, 0x72, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, 0x61, 0x72, 0x63, 0x68, 0x12, + 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x44, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, + 0x73, 0x12, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, + 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x13, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x13, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4e, 0x6f, 0x64, 0x65, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x6e, 0x0a, 0x1a, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, + 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x64, 0x67, + 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x1a, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x12, 0x50, 0x0a, 0x10, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x10, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x52, 0x0a, 0x12, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x37, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6c, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x02, 0x18, 0x01, 0x52, 0x12, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x10, + 0x0a, 0x03, 0x68, 0x75, 0x62, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x68, 0x75, 0x62, + 0x12, 0x28, 0x0a, 0x0f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x75, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x50, 0x75, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x50, 0x75, 0x6c, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x25, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x75, 0x6c, 0x6c, 0x53, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x69, 0x73, 0x74, 0x69, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x38, + 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x41, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x18, 0x24, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x6c, + 0x6f, 0x67, 0x41, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x67, + 0x69, 0x6e, 0x67, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4c, 0x6f, 0x67, 0x67, 0x69, + 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, + 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, 0x44, 0x18, 0x35, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, 0x44, 0x12, 0x3b, 0x0a, 0x0c, 0x6d, 0x65, 0x73, + 0x68, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x68, 0x4e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x40, 0x0a, 0x0c, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x75, 0x6c, 0x74, + 0x69, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x18, 0x27, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x12, 0x36, 0x0a, 0x16, 0x70, 0x6f, 0x64, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x2b, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x16, 0x70, 0x6f, 0x64, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x5e, 0x0a, 0x1c, 0x6f, 0x6d, + 0x69, 0x74, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x18, 0x26, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x1c, 0x6f, 0x6d, + 0x69, 0x74, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x12, 0x52, 0x0a, 0x16, 0x6f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x57, 0x65, 0x62, 0x68, + 0x6f, 0x6f, 0x6b, 0x73, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, + 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x16, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x30, + 0x0a, 0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, + 0x61, 0x6d, 0x65, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x11, 0x70, + 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x39, 0x0a, + 0x0a, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x1d, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x49, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x25, 0x0a, 0x03, 0x73, 0x64, 0x73, 0x18, + 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x53, 0x44, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x03, 0x73, 0x64, 0x73, 0x12, + 0x28, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x61, 0x72, + 0x69, 0x61, 0x6e, 0x74, 0x18, 0x43, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x61, 0x72, 0x69, + 0x61, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x18, 0x21, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, + 0x72, 0x61, 0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x72, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x69, 0x6c, + 0x6f, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x30, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x64, 0x18, 0x36, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, + 0x73, 0x74, 0x69, 0x6f, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x69, 0x73, 0x74, + 0x69, 0x6f, 0x64, 0x12, 0x2c, 0x0a, 0x11, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x43, 0x65, 0x72, 0x74, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x38, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, + 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x43, 0x65, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x12, 0x20, 0x0a, 0x09, 0x6a, 0x77, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x39, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6a, 0x77, 0x74, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x12, 0x25, 0x0a, 0x03, 0x73, 0x74, 0x73, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x54, 0x53, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x03, 0x73, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x3b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x0e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, + 0x74, 0x6c, 0x73, 0x43, 0x65, 0x72, 0x74, 0x73, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x4d, 0x74, 0x6c, 0x73, 0x43, 0x65, 0x72, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x61, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x61, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x42, 0x0a, 0x0e, 0x65, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x73, 0x74, 0x69, 0x6f, 0x64, 0x18, 0x3e, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x65, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x73, 0x74, 0x69, 0x6f, 0x64, 0x12, 0x40, 0x0a, 0x0d, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x40, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x16, + 0x0a, 0x06, 0x63, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x41, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x63, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, + 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x76, 0x32, 0x41, 0x50, 0x49, 0x18, 0x42, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x61, 0x75, + 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x76, 0x32, 0x41, 0x50, 0x49, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x45, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x70, + 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x46, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, + 0x69, 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x70, + 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x47, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x69, 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x22, 0x2d, 0x0a, 0x09, 0x53, 0x54, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6f, 0x72, + 0x74, 0x22, 0x52, 0x0a, 0x0c, 0x49, 0x73, 0x74, 0x69, 0x6f, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x42, 0x0a, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6e, 0x61, 0x6c, 0x79, + 0x73, 0x69, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6e, 0x61, + 0x6c, 0x79, 0x73, 0x69, 0x73, 0x22, 0x2b, 0x0a, 0x13, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4c, + 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, + 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, + 0x65, 0x6c, 0x22, 0xf1, 0x10, 0x0a, 0x14, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x46, 0x0a, 0x10, 0x61, + 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x4d, 0x61, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x61, 0x78, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x61, + 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x69, 0x6e, 0x12, 0x3d, 0x0a, 0x06, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x55, 0x74, 0x69, + 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, + 0x18, 0x01, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x37, 0x0a, 0x03, 0x63, 0x70, + 0x75, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, + 0x63, 0x70, 0x75, 0x12, 0x40, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, + 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x03, 0x65, + 0x6e, 0x76, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x42, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, + 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x6c, 0x6f, + 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x50, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, + 0x49, 0x50, 0x12, 0x3a, 0x0a, 0x18, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x72, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x11, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x72, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x12, 0x43, 0x0a, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x7b, 0x0a, 0x1c, 0x70, 0x6f, 0x64, 0x41, + 0x6e, 0x74, 0x69, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, + 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x6d, 0x61, 0x63, 0x68, 0x69, + 0x6e, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x6d, 0x65, + 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, 0x52, 0x1c, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x74, 0x69, + 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x83, 0x01, 0x0a, 0x20, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x74, + 0x69, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x54, 0x65, 0x72, 0x6d, 0x4c, 0x61, 0x62, + 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x33, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x6d, 0x61, 0x63, + 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, + 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, 0x52, 0x20, 0x70, 0x6f, 0x64, 0x41, 0x6e, + 0x74, 0x69, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x54, 0x65, 0x72, 0x6d, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x2b, 0x0a, 0x05, 0x70, + 0x6f, 0x72, 0x74, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0c, 0x72, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, + 0x18, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x39, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x19, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, + 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0d, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x1b, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x12, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x04, 0x7a, 0x76, 0x70, 0x6e, 0x18, 0x1e, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5a, 0x76, + 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x04, 0x7a, 0x76, 0x70, 0x6e, 0x12, 0x43, + 0x0a, 0x0f, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x53, 0x75, 0x72, 0x67, + 0x65, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x4f, 0x72, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x02, + 0x18, 0x01, 0x52, 0x0f, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x53, 0x75, + 0x72, 0x67, 0x65, 0x12, 0x4f, 0x0a, 0x15, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, + 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x20, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, + 0x74, 0x4f, 0x72, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x15, 0x72, + 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x12, 0x34, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x22, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x54, 0x72, 0x61, + 0x66, 0x66, 0x69, 0x63, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x44, 0x0a, 0x0b, 0x74, 0x6f, + 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1e, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x3b, 0x0a, 0x0c, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x73, + 0x18, 0x24, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, + 0x0c, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x4b, 0x0a, + 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x25, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x3d, 0x0a, 0x0d, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x26, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x72, 0x75, 0x6e, + 0x41, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, + 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x41, 0x73, 0x52, + 0x6f, 0x6f, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, + 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x12, 0x40, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, + 0x73, 0x18, 0x30, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, + 0x69, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x31, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x70, 0x46, + 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x68, 0x0a, 0x18, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, + 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5a, 0x76, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x75, 0x66, 0x66, + 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, + 0x22, 0xe8, 0x01, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, + 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x2e, 0x0a, 0x12, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, + 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x12, + 0x4a, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x45, 0x6e, 0x76, 0x6f, 0x79, 0x46, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, + 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x45, 0x6e, 0x76, 0x6f, 0x79, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x87, 0x01, 0x0a, 0x1b, + 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x04, 0x6d, + 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, + 0x66, 0x66, 0x69, 0x63, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x28, 0x0a, 0x04, 0x4d, + 0x6f, 0x64, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x41, 0x4e, 0x59, + 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x52, 0x59, 0x5f, 0x4f, + 0x4e, 0x4c, 0x59, 0x10, 0x01, 0x22, 0xa5, 0x11, 0x0a, 0x0b, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x10, 0x61, + 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x4d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x69, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x61, + 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x61, 0x78, 0x12, 0x45, 0x0a, 0x11, 0x61, + 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, + 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, + 0x11, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, + 0x6f, 0x72, 0x12, 0x26, 0x0a, 0x0c, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x72, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, + 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x61, + 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x02, + 0x18, 0x01, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, + 0x03, 0x63, 0x70, 0x75, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x55, 0x74, 0x69, 0x6c, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x3f, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x63, 0x0a, 0x1f, 0x6b, 0x65, 0x65, 0x70, 0x61, + 0x6c, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x67, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x1f, 0x6b, 0x65, 0x65, + 0x70, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x10, + 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, + 0x10, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x12, 0x35, 0x0a, 0x09, 0x70, 0x6f, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x24, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x09, 0x70, + 0x6f, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x4d, 0x61, 0x70, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, + 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, + 0x61, 0x70, 0x12, 0x29, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x38, 0x0a, + 0x08, 0x61, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x52, 0x08, 0x61, + 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x6f, 0x6c, 0x6c, 0x69, + 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x53, 0x75, 0x72, 0x67, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x4f, + 0x72, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0f, 0x72, 0x6f, 0x6c, + 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x53, 0x75, 0x72, 0x67, 0x65, 0x12, 0x4f, 0x0a, 0x15, + 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, + 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x4f, 0x72, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x15, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, + 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x44, 0x0a, + 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x1a, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x47, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x25, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x55, 0x0a, 0x19, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x38, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x19, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x38, 0x0a, 0x17, 0x6a, 0x77, 0x6b, 0x73, + 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x45, 0x78, 0x74, 0x72, 0x61, 0x52, 0x6f, 0x6f, + 0x74, 0x43, 0x41, 0x18, 0x20, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x6a, 0x77, 0x6b, 0x73, 0x52, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x45, 0x78, 0x74, 0x72, 0x61, 0x52, 0x6f, 0x6f, 0x74, + 0x43, 0x41, 0x12, 0x10, 0x0a, 0x03, 0x68, 0x75, 0x62, 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x68, 0x75, 0x62, 0x12, 0x28, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x23, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x18, 0x27, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x73, 0x65, 0x63, 0x63, + 0x6f, 0x6d, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x26, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x22, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x63, 0x6f, 0x6d, 0x70, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x63, 0x6f, 0x6d, 0x70, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x12, 0x6a, 0x0a, 0x19, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x53, 0x70, 0x72, 0x65, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, + 0x73, 0x18, 0x29, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x70, 0x72, 0x65, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x73, 0x74, + 0x72, 0x61, 0x69, 0x6e, 0x74, 0x52, 0x19, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, + 0x70, 0x72, 0x65, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, + 0x12, 0x47, 0x0a, 0x12, 0x65, 0x78, 0x74, 0x72, 0x61, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x41, 0x72, 0x67, 0x73, 0x18, 0x2a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x65, 0x78, 0x74, 0x72, 0x61, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x41, 0x72, 0x67, 0x73, 0x12, 0x43, 0x0a, 0x0c, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x31, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x0c, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x34, + 0x0a, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x33, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x07, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, + 0x65, 0x73, 0x18, 0x34, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x70, 0x46, 0x61, 0x6d, 0x69, + 0x6c, 0x69, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x70, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x35, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x70, + 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x3d, 0x0a, 0x06, + 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x36, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x55, 0x74, + 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x2a, 0x0a, 0x03, 0x63, + 0x6e, 0x69, 0x18, 0x37, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x43, 0x4e, 0x49, 0x55, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x03, 0x63, 0x6e, 0x69, 0x12, 0x3a, 0x0a, 0x05, 0x74, 0x61, 0x69, 0x6e, 0x74, + 0x18, 0x39, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x74, 0x61, + 0x69, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x17, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5a, 0x74, + 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x3b, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5a, 0x74, 0x75, + 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x54, 0x0a, + 0x1a, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x22, 0xb7, 0x01, 0x0a, 0x12, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x49, 0x6e, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x6e, + 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x55, 0x0a, 0x15, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x1f, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x69, 0x6e, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x4d, 0x6f, + 0x64, 0x65, 0x52, 0x15, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x49, 0x0a, + 0x11, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x74, 0x0a, 0x0f, 0x54, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, + 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x12, 0x2b, 0x0a, 0x02, 0x76, 0x32, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, + 0x72, 0x79, 0x56, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x02, 0x76, 0x32, 0x22, 0xda, + 0x01, 0x0a, 0x11, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x56, 0x32, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x45, 0x0a, 0x0a, 0x70, 0x72, + 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x74, 0x72, 0x79, 0x56, 0x32, 0x50, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, + 0x73, 0x12, 0x48, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x56, 0x32, 0x53, 0x74, 0x61, + 0x63, 0x6b, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, + 0x73, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x22, 0x53, 0x0a, 0x1b, 0x54, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x56, 0x32, 0x50, 0x72, 0x6f, 0x6d, 0x65, 0x74, + 0x68, 0x65, 0x75, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, + 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x22, 0x54, 0x0a, 0x1c, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x56, 0x32, 0x53, + 0x74, 0x61, 0x63, 0x6b, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x8d, 0x01, 0x0a, 0x0b, 0x50, 0x6f, 0x72, 0x74, 0x73, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, + 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x99, 0x09, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x49, 0x6e, + 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x6f, + 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x11, + 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, + 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, + 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x42, 0x0a, 0x0e, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x72, 0x65, 0x44, 0x75, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x72, 0x65, 0x44, 0x75, 0x6d, 0x70, 0x12, 0x30, + 0x0a, 0x13, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x50, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, + 0x12, 0x28, 0x0a, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x50, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x49, 0x50, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x12, 0x28, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x50, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x49, 0x50, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, + 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, + 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x26, 0x0a, 0x0e, 0x6f, 0x75, 0x74, 0x6c, 0x69, 0x65, + 0x72, 0x4c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x6f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x3a, + 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x64, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, + 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x1c, 0x72, 0x65, + 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x44, 0x65, + 0x6c, 0x61, 0x79, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x1c, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x36, + 0x0a, 0x16, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x65, 0x72, 0x69, 0x6f, + 0x64, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, + 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x3c, 0x0a, 0x19, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, + 0x65, 0x73, 0x73, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, + 0x6f, 0x6c, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, 0x72, 0x65, 0x61, 0x64, 0x69, + 0x6e, 0x65, 0x73, 0x73, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x54, 0x68, 0x72, 0x65, 0x73, + 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x3a, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x50, + 0x72, 0x6f, 0x62, 0x65, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6f, + 0x62, 0x65, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x62, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x17, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x50, 0x6f, 0x72, 0x74, + 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x18, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x72, 0x12, 0x32, 0x0a, 0x14, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4f, 0x75, 0x74, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x14, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x50, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x3b, 0x0a, 0x09, 0x6c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, + 0x6c, 0x65, 0x18, 0x24, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, + 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x52, 0x09, 0x6c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, + 0x6c, 0x65, 0x12, 0x68, 0x0a, 0x1f, 0x68, 0x6f, 0x6c, 0x64, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x73, 0x18, 0x25, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, + 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x18, 0x01, 0x52, 0x1f, 0x68, 0x6f, 0x6c, + 0x64, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x74, 0x69, + 0x6c, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x13, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x6f, + 0x72, 0x74, 0x73, 0x18, 0x26, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x32, + 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x27, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x6f, 0x72, + 0x74, 0x73, 0x22, 0x70, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x50, 0x72, 0x6f, + 0x62, 0x65, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2a, 0x0a, 0x10, 0x66, 0x61, 0x69, 0x6c, + 0x75, 0x72, 0x65, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x10, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x54, 0x68, 0x72, 0x65, 0x73, + 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x5e, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x49, 0x6e, 0x69, + 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, + 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x17, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x10, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x70, + 0x75, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x22, 0x3e, 0x0a, 0x09, 0x53, 0x44, 0x53, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x31, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, + 0x18, 0x01, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x60, 0x0a, 0x0c, 0x53, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x91, 0x05, 0x0a, 0x15, + 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x58, 0x0a, 0x19, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x44, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x19, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, + 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x72, 0x65, 0x69, + 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, + 0x65, 0x0a, 0x13, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x6b, + 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, + 0x72, 0x79, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x52, 0x13, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x67, 0x0a, 0x14, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0c, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2e, 0x61, 0x70, + 0x69, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, + 0x70, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x14, 0x61, 0x6c, 0x77, 0x61, 0x79, + 0x73, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, + 0x4c, 0x0a, 0x13, 0x72, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x54, 0x54, + 0x50, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, + 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x72, 0x65, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x41, 0x70, 0x70, 0x48, 0x54, 0x54, 0x50, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x49, 0x0a, + 0x13, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x52, 0x13, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x6a, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x4c, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x4c, 0x12, 0x35, 0x0a, 0x09, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x18, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x22, + 0x81, 0x02, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x37, 0x0a, 0x07, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, + 0x63, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x52, 0x07, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x12, 0x3d, 0x0a, 0x09, 0x6c, 0x69, 0x67, + 0x68, 0x74, 0x73, 0x74, 0x65, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x4c, 0x69, + 0x67, 0x68, 0x74, 0x53, 0x74, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x6c, + 0x69, 0x67, 0x68, 0x74, 0x73, 0x74, 0x65, 0x70, 0x12, 0x34, 0x0a, 0x06, 0x7a, 0x69, 0x70, 0x6b, + 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x5a, 0x69, 0x70, 0x6b, 0x69, 0x6e, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x7a, 0x69, 0x70, 0x6b, 0x69, 0x6e, 0x12, 0x43, + 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, + 0x72, 0x61, 0x63, 0x65, 0x72, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, + 0x76, 0x65, 0x72, 0x22, 0x2f, 0x0a, 0x13, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x44, 0x61, 0x74, + 0x61, 0x64, 0x6f, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x22, 0x53, 0x0a, 0x15, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x4c, 0x69, + 0x67, 0x68, 0x74, 0x53, 0x74, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x2e, 0x0a, 0x12, 0x54, 0x72, 0x61, + 0x63, 0x65, 0x72, 0x5a, 0x69, 0x70, 0x6b, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xf5, 0x01, 0x0a, 0x17, 0x54, 0x72, + 0x61, 0x63, 0x65, 0x72, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x30, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x34, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x4e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x4f, 0x66, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x36, 0x0a, + 0x16, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x41, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x6d, + 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x18, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x4f, 0x66, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x18, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x4f, 0x66, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x22, 0xc6, 0x02, 0x0a, 0x0a, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x4a, 0x0a, 0x12, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x52, 0x44, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, + 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x43, 0x52, 0x44, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0d, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x4c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, + 0x52, 0x4c, 0x12, 0x50, 0x0a, 0x15, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x73, 0x74, 0x69, + 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x52, 0x44, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x15, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x73, 0x74, 0x69, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x43, 0x52, 0x44, 0x73, 0x12, 0x44, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x2e, 0x0a, 0x12, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x41, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x43, 0x41, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x22, 0x8c, 0x01, 0x0a, 0x12, 0x49, + 0x73, 0x74, 0x69, 0x6f, 0x64, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, + 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x55, 0x52, 0x4c, 0x12, 0x24, 0x0a, 0x0d, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x69, 0x6e, + 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x12, 0x2c, 0x0a, 0x11, 0x69, + 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x41, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x43, 0x41, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x22, 0xfd, 0x06, 0x0a, 0x06, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x03, 0x63, 0x6e, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x4e, 0x49, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x03, 0x63, 0x6e, 0x69, 0x12, 0x34, 0x0a, 0x08, 0x67, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x69, 0x6c, 0x6f, + 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x12, 0x30, + 0x0a, 0x07, 0x7a, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x7a, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, + 0x12, 0x37, 0x0a, 0x09, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x18, 0x17, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, + 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x12, 0x57, 0x0a, 0x16, 0x73, 0x69, 0x64, + 0x65, 0x63, 0x61, 0x72, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x57, 0x65, 0x62, 0x68, + 0x6f, 0x6f, 0x6b, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x49, 0x6e, 0x6a, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x16, 0x73, 0x69, 0x64, 0x65, + 0x63, 0x61, 0x72, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x57, 0x65, 0x62, 0x68, 0x6f, + 0x6f, 0x6b, 0x12, 0x39, 0x0a, 0x09, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x5f, 0x63, 0x6e, 0x69, 0x18, + 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x43, 0x4e, 0x49, 0x55, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x08, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x43, 0x6e, 0x69, 0x12, 0x1a, 0x0a, + 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, + 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x68, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x24, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x28, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x25, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x69, 0x73, 0x74, + 0x69, 0x6f, 0x64, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x18, 0x26, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x73, 0x74, 0x69, 0x6f, + 0x64, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x69, + 0x73, 0x74, 0x69, 0x6f, 0x64, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x72, + 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x67, 0x73, 0x18, 0x27, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0c, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x67, 0x73, 0x12, + 0x28, 0x0a, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x12, 0x32, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x2b, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x14, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x0c, 0x65, 0x78, 0x70, 0x65, 0x72, + 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, + 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x65, 0x78, 0x70, + 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x22, 0x5d, 0x0a, 0x0d, 0x5a, 0x65, 0x72, + 0x6f, 0x56, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, + 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x22, 0x68, 0x0a, 0x12, 0x45, 0x78, 0x70, 0x65, + 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x52, + 0x0a, 0x16, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x16, 0x73, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x22, 0x8c, 0x01, 0x0a, 0x0b, 0x49, 0x6e, 0x74, 0x4f, 0x72, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x06, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x12, 0x34, 0x0a, 0x06, 0x73, + 0x74, 0x72, 0x56, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x73, 0x74, 0x72, 0x56, 0x61, + 0x6c, 0x2a, 0x4a, 0x0a, 0x15, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, + 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, + 0x43, 0x54, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x46, 0x46, 0x10, 0x03, 0x2a, 0x60, 0x0a, + 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x7a, 0x69, 0x70, 0x6b, 0x69, + 0x6e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x74, 0x65, 0x70, + 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x10, 0x02, 0x12, + 0x0f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x10, 0x03, + 0x12, 0x13, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x43, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x41, 0x67, + 0x65, 0x6e, 0x74, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x10, 0x05, 0x42, + 0x31, 0x5a, 0x2f, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x2e, 0x69, 0x6f, 0x2f, 0x69, 0x73, 0x74, 0x69, + 0x6f, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, + 0x70, 0x69, 0x73, 0x2f, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescOnce sync.Once + file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescData = file_pkg_apis_istio_v1alpha1_values_types_proto_rawDesc +) + +func file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP() []byte { + file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescOnce.Do(func() { + file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescData) + }) + return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescData +} + +var file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes = make([]protoimpl.MessageInfo, 52) +var file_pkg_apis_istio_v1alpha1_values_types_proto_goTypes = []any{ + (IngressControllerMode)(0), // 0: v1alpha1.ingressControllerMode + (Tracer)(0), // 1: v1alpha1.tracer + (OutboundTrafficPolicyConfig_Mode)(0), // 2: v1alpha1.OutboundTrafficPolicyConfig.Mode + (*ArchConfig)(nil), // 3: v1alpha1.ArchConfig + (*CNIConfig)(nil), // 4: v1alpha1.CNIConfig + (*CNIUsageConfig)(nil), // 5: v1alpha1.CNIUsageConfig + (*CNIAmbientConfig)(nil), // 6: v1alpha1.CNIAmbientConfig + (*CNIRepairConfig)(nil), // 7: v1alpha1.CNIRepairConfig + (*ResourceQuotas)(nil), // 8: v1alpha1.ResourceQuotas + (*TargetUtilizationConfig)(nil), // 9: v1alpha1.TargetUtilizationConfig + (*Resources)(nil), // 10: v1alpha1.Resources + (*ServiceAccount)(nil), // 11: v1alpha1.ServiceAccount + (*DefaultPodDisruptionBudgetConfig)(nil), // 12: v1alpha1.DefaultPodDisruptionBudgetConfig + (*DefaultResourcesConfig)(nil), // 13: v1alpha1.DefaultResourcesConfig + (*EgressGatewayConfig)(nil), // 14: v1alpha1.EgressGatewayConfig + (*GatewaysConfig)(nil), // 15: v1alpha1.GatewaysConfig + (*GlobalConfig)(nil), // 16: v1alpha1.GlobalConfig + (*STSConfig)(nil), // 17: v1alpha1.STSConfig + (*IstiodConfig)(nil), // 18: v1alpha1.IstiodConfig + (*GlobalLoggingConfig)(nil), // 19: v1alpha1.GlobalLoggingConfig + (*IngressGatewayConfig)(nil), // 20: v1alpha1.IngressGatewayConfig + (*IngressGatewayZvpnConfig)(nil), // 21: v1alpha1.IngressGatewayZvpnConfig + (*MultiClusterConfig)(nil), // 22: v1alpha1.MultiClusterConfig + (*OutboundTrafficPolicyConfig)(nil), // 23: v1alpha1.OutboundTrafficPolicyConfig + (*PilotConfig)(nil), // 24: v1alpha1.PilotConfig + (*PilotTaintControllerConfig)(nil), // 25: v1alpha1.PilotTaintControllerConfig + (*PilotIngressConfig)(nil), // 26: v1alpha1.PilotIngressConfig + (*PilotPolicyConfig)(nil), // 27: v1alpha1.PilotPolicyConfig + (*TelemetryConfig)(nil), // 28: v1alpha1.TelemetryConfig + (*TelemetryV2Config)(nil), // 29: v1alpha1.TelemetryV2Config + (*TelemetryV2PrometheusConfig)(nil), // 30: v1alpha1.TelemetryV2PrometheusConfig + (*TelemetryV2StackDriverConfig)(nil), // 31: v1alpha1.TelemetryV2StackDriverConfig + (*PortsConfig)(nil), // 32: v1alpha1.PortsConfig + (*ProxyConfig)(nil), // 33: v1alpha1.ProxyConfig + (*StartupProbe)(nil), // 34: v1alpha1.StartupProbe + (*ProxyInitConfig)(nil), // 35: v1alpha1.ProxyInitConfig + (*ResourcesRequestsConfig)(nil), // 36: v1alpha1.ResourcesRequestsConfig + (*SDSConfig)(nil), // 37: v1alpha1.SDSConfig + (*SecretVolume)(nil), // 38: v1alpha1.SecretVolume + (*SidecarInjectorConfig)(nil), // 39: v1alpha1.SidecarInjectorConfig + (*TracerConfig)(nil), // 40: v1alpha1.TracerConfig + (*TracerDatadogConfig)(nil), // 41: v1alpha1.TracerDatadogConfig + (*TracerLightStepConfig)(nil), // 42: v1alpha1.TracerLightStepConfig + (*TracerZipkinConfig)(nil), // 43: v1alpha1.TracerZipkinConfig + (*TracerStackdriverConfig)(nil), // 44: v1alpha1.TracerStackdriverConfig + (*BaseConfig)(nil), // 45: v1alpha1.BaseConfig + (*IstiodRemoteConfig)(nil), // 46: v1alpha1.IstiodRemoteConfig + (*Values)(nil), // 47: v1alpha1.Values + (*ZeroVPNConfig)(nil), // 48: v1alpha1.ZeroVPNConfig + (*ExperimentalConfig)(nil), // 49: v1alpha1.ExperimentalConfig + (*IntOrString)(nil), // 50: v1alpha1.IntOrString + nil, // 51: v1alpha1.Resources.LimitsEntry + nil, // 52: v1alpha1.Resources.RequestsEntry + nil, // 53: v1alpha1.EgressGatewayConfig.LabelsEntry + nil, // 54: v1alpha1.IngressGatewayConfig.LabelsEntry + (*wrapperspb.BoolValue)(nil), // 55: google.protobuf.BoolValue + (*structpb.Value)(nil), // 56: google.protobuf.Value + (*v1.Affinity)(nil), // 57: k8s.io.api.core.v1.Affinity + (*structpb.Struct)(nil), // 58: google.protobuf.Struct + (*v1.SeccompProfile)(nil), // 59: k8s.io.api.core.v1.SeccompProfile + (*v11.LabelSelector)(nil), // 60: k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector + (*v1.Toleration)(nil), // 61: k8s.io.api.core.v1.Toleration + (*durationpb.Duration)(nil), // 62: google.protobuf.Duration + (*v1.TopologySpreadConstraint)(nil), // 63: k8s.io.api.core.v1.TopologySpreadConstraint + (*v1.VolumeMount)(nil), // 64: k8s.io.api.core.v1.VolumeMount + (*v1.Volume)(nil), // 65: k8s.io.api.core.v1.Volume + (*v1.Lifecycle)(nil), // 66: k8s.io.api.core.v1.Lifecycle + (*wrapperspb.Int32Value)(nil), // 67: google.protobuf.Int32Value + (*wrapperspb.StringValue)(nil), // 68: google.protobuf.StringValue +} +var file_pkg_apis_istio_v1alpha1_values_types_proto_depIdxs = []int32{ + 55, // 0: v1alpha1.CNIConfig.enabled:type_name -> google.protobuf.BoolValue + 56, // 1: v1alpha1.CNIConfig.tag:type_name -> google.protobuf.Value + 57, // 2: v1alpha1.CNIConfig.affinity:type_name -> k8s.io.api.core.v1.Affinity + 58, // 3: v1alpha1.CNIConfig.podAnnotations:type_name -> google.protobuf.Struct + 19, // 4: v1alpha1.CNIConfig.logging:type_name -> v1alpha1.GlobalLoggingConfig + 7, // 5: v1alpha1.CNIConfig.repair:type_name -> v1alpha1.CNIRepairConfig + 55, // 6: v1alpha1.CNIConfig.chained:type_name -> google.protobuf.BoolValue + 8, // 7: v1alpha1.CNIConfig.resource_quotas:type_name -> v1alpha1.ResourceQuotas + 10, // 8: v1alpha1.CNIConfig.resources:type_name -> v1alpha1.Resources + 55, // 9: v1alpha1.CNIConfig.privileged:type_name -> google.protobuf.BoolValue + 59, // 10: v1alpha1.CNIConfig.seccompProfile:type_name -> k8s.io.api.core.v1.SeccompProfile + 6, // 11: v1alpha1.CNIConfig.ambient:type_name -> v1alpha1.CNIAmbientConfig + 50, // 12: v1alpha1.CNIConfig.rollingMaxUnavailable:type_name -> v1alpha1.IntOrString + 55, // 13: v1alpha1.CNIUsageConfig.enabled:type_name -> google.protobuf.BoolValue + 55, // 14: v1alpha1.CNIUsageConfig.chained:type_name -> google.protobuf.BoolValue + 55, // 15: v1alpha1.CNIAmbientConfig.enabled:type_name -> google.protobuf.BoolValue + 55, // 16: v1alpha1.CNIAmbientConfig.dnsCapture:type_name -> google.protobuf.BoolValue + 55, // 17: v1alpha1.CNIAmbientConfig.ipv6:type_name -> google.protobuf.BoolValue + 55, // 18: v1alpha1.CNIRepairConfig.enabled:type_name -> google.protobuf.BoolValue + 56, // 19: v1alpha1.CNIRepairConfig.tag:type_name -> google.protobuf.Value + 55, // 20: v1alpha1.ResourceQuotas.enabled:type_name -> google.protobuf.BoolValue + 51, // 21: v1alpha1.Resources.limits:type_name -> v1alpha1.Resources.LimitsEntry + 52, // 22: v1alpha1.Resources.requests:type_name -> v1alpha1.Resources.RequestsEntry + 58, // 23: v1alpha1.ServiceAccount.annotations:type_name -> google.protobuf.Struct + 55, // 24: v1alpha1.DefaultPodDisruptionBudgetConfig.enabled:type_name -> google.protobuf.BoolValue + 36, // 25: v1alpha1.DefaultResourcesConfig.requests:type_name -> v1alpha1.ResourcesRequestsConfig + 55, // 26: v1alpha1.EgressGatewayConfig.autoscaleEnabled:type_name -> google.protobuf.BoolValue + 9, // 27: v1alpha1.EgressGatewayConfig.memory:type_name -> v1alpha1.TargetUtilizationConfig + 9, // 28: v1alpha1.EgressGatewayConfig.cpu:type_name -> v1alpha1.TargetUtilizationConfig + 55, // 29: v1alpha1.EgressGatewayConfig.customService:type_name -> google.protobuf.BoolValue + 55, // 30: v1alpha1.EgressGatewayConfig.enabled:type_name -> google.protobuf.BoolValue + 58, // 31: v1alpha1.EgressGatewayConfig.env:type_name -> google.protobuf.Struct + 53, // 32: v1alpha1.EgressGatewayConfig.labels:type_name -> v1alpha1.EgressGatewayConfig.LabelsEntry + 58, // 33: v1alpha1.EgressGatewayConfig.nodeSelector:type_name -> google.protobuf.Struct + 58, // 34: v1alpha1.EgressGatewayConfig.podAnnotations:type_name -> google.protobuf.Struct + 60, // 35: v1alpha1.EgressGatewayConfig.podAntiAffinityLabelSelector:type_name -> k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector + 60, // 36: v1alpha1.EgressGatewayConfig.podAntiAffinityTermLabelSelector:type_name -> k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector + 32, // 37: v1alpha1.EgressGatewayConfig.ports:type_name -> v1alpha1.PortsConfig + 10, // 38: v1alpha1.EgressGatewayConfig.resources:type_name -> v1alpha1.Resources + 38, // 39: v1alpha1.EgressGatewayConfig.secretVolumes:type_name -> v1alpha1.SecretVolume + 58, // 40: v1alpha1.EgressGatewayConfig.serviceAnnotations:type_name -> google.protobuf.Struct + 48, // 41: v1alpha1.EgressGatewayConfig.zvpn:type_name -> v1alpha1.ZeroVPNConfig + 61, // 42: v1alpha1.EgressGatewayConfig.tolerations:type_name -> k8s.io.api.core.v1.Toleration + 50, // 43: v1alpha1.EgressGatewayConfig.rollingMaxSurge:type_name -> v1alpha1.IntOrString + 50, // 44: v1alpha1.EgressGatewayConfig.rollingMaxUnavailable:type_name -> v1alpha1.IntOrString + 58, // 45: v1alpha1.EgressGatewayConfig.configVolumes:type_name -> google.protobuf.Struct + 58, // 46: v1alpha1.EgressGatewayConfig.additionalContainers:type_name -> google.protobuf.Struct + 55, // 47: v1alpha1.EgressGatewayConfig.runAsRoot:type_name -> google.protobuf.BoolValue + 11, // 48: v1alpha1.EgressGatewayConfig.serviceAccount:type_name -> v1alpha1.ServiceAccount + 14, // 49: v1alpha1.GatewaysConfig.istio_egressgateway:type_name -> v1alpha1.EgressGatewayConfig + 55, // 50: v1alpha1.GatewaysConfig.enabled:type_name -> google.protobuf.BoolValue + 20, // 51: v1alpha1.GatewaysConfig.istio_ingressgateway:type_name -> v1alpha1.IngressGatewayConfig + 56, // 52: v1alpha1.GatewaysConfig.securityContext:type_name -> google.protobuf.Value + 56, // 53: v1alpha1.GatewaysConfig.seccompProfile:type_name -> google.protobuf.Value + 3, // 54: v1alpha1.GlobalConfig.arch:type_name -> v1alpha1.ArchConfig + 55, // 55: v1alpha1.GlobalConfig.configValidation:type_name -> google.protobuf.BoolValue + 58, // 56: v1alpha1.GlobalConfig.defaultNodeSelector:type_name -> google.protobuf.Struct + 12, // 57: v1alpha1.GlobalConfig.defaultPodDisruptionBudget:type_name -> v1alpha1.DefaultPodDisruptionBudgetConfig + 13, // 58: v1alpha1.GlobalConfig.defaultResources:type_name -> v1alpha1.DefaultResourcesConfig + 61, // 59: v1alpha1.GlobalConfig.defaultTolerations:type_name -> k8s.io.api.core.v1.Toleration + 55, // 60: v1alpha1.GlobalConfig.logAsJson:type_name -> google.protobuf.BoolValue + 19, // 61: v1alpha1.GlobalConfig.logging:type_name -> v1alpha1.GlobalLoggingConfig + 58, // 62: v1alpha1.GlobalConfig.meshNetworks:type_name -> google.protobuf.Struct + 22, // 63: v1alpha1.GlobalConfig.multiCluster:type_name -> v1alpha1.MultiClusterConfig + 55, // 64: v1alpha1.GlobalConfig.omitSidecarInjectorConfigMap:type_name -> google.protobuf.BoolValue + 55, // 65: v1alpha1.GlobalConfig.operatorManageWebhooks:type_name -> google.protobuf.BoolValue + 33, // 66: v1alpha1.GlobalConfig.proxy:type_name -> v1alpha1.ProxyConfig + 35, // 67: v1alpha1.GlobalConfig.proxy_init:type_name -> v1alpha1.ProxyInitConfig + 37, // 68: v1alpha1.GlobalConfig.sds:type_name -> v1alpha1.SDSConfig + 56, // 69: v1alpha1.GlobalConfig.tag:type_name -> google.protobuf.Value + 40, // 70: v1alpha1.GlobalConfig.tracer:type_name -> v1alpha1.TracerConfig + 18, // 71: v1alpha1.GlobalConfig.istiod:type_name -> v1alpha1.IstiodConfig + 17, // 72: v1alpha1.GlobalConfig.sts:type_name -> v1alpha1.STSConfig + 55, // 73: v1alpha1.GlobalConfig.mountMtlsCerts:type_name -> google.protobuf.BoolValue + 55, // 74: v1alpha1.GlobalConfig.externalIstiod:type_name -> google.protobuf.BoolValue + 55, // 75: v1alpha1.GlobalConfig.configCluster:type_name -> google.protobuf.BoolValue + 55, // 76: v1alpha1.GlobalConfig.autoscalingv2API:type_name -> google.protobuf.BoolValue + 55, // 77: v1alpha1.IstiodConfig.enableAnalysis:type_name -> google.protobuf.BoolValue + 55, // 78: v1alpha1.IngressGatewayConfig.autoscaleEnabled:type_name -> google.protobuf.BoolValue + 9, // 79: v1alpha1.IngressGatewayConfig.memory:type_name -> v1alpha1.TargetUtilizationConfig + 9, // 80: v1alpha1.IngressGatewayConfig.cpu:type_name -> v1alpha1.TargetUtilizationConfig + 55, // 81: v1alpha1.IngressGatewayConfig.customService:type_name -> google.protobuf.BoolValue + 55, // 82: v1alpha1.IngressGatewayConfig.enabled:type_name -> google.protobuf.BoolValue + 58, // 83: v1alpha1.IngressGatewayConfig.env:type_name -> google.protobuf.Struct + 54, // 84: v1alpha1.IngressGatewayConfig.labels:type_name -> v1alpha1.IngressGatewayConfig.LabelsEntry + 58, // 85: v1alpha1.IngressGatewayConfig.nodeSelector:type_name -> google.protobuf.Struct + 58, // 86: v1alpha1.IngressGatewayConfig.podAnnotations:type_name -> google.protobuf.Struct + 60, // 87: v1alpha1.IngressGatewayConfig.podAntiAffinityLabelSelector:type_name -> k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector + 60, // 88: v1alpha1.IngressGatewayConfig.podAntiAffinityTermLabelSelector:type_name -> k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector + 32, // 89: v1alpha1.IngressGatewayConfig.ports:type_name -> v1alpha1.PortsConfig + 58, // 90: v1alpha1.IngressGatewayConfig.resources:type_name -> google.protobuf.Struct + 38, // 91: v1alpha1.IngressGatewayConfig.secretVolumes:type_name -> v1alpha1.SecretVolume + 58, // 92: v1alpha1.IngressGatewayConfig.serviceAnnotations:type_name -> google.protobuf.Struct + 21, // 93: v1alpha1.IngressGatewayConfig.zvpn:type_name -> v1alpha1.IngressGatewayZvpnConfig + 50, // 94: v1alpha1.IngressGatewayConfig.rollingMaxSurge:type_name -> v1alpha1.IntOrString + 50, // 95: v1alpha1.IngressGatewayConfig.rollingMaxUnavailable:type_name -> v1alpha1.IntOrString + 61, // 96: v1alpha1.IngressGatewayConfig.tolerations:type_name -> k8s.io.api.core.v1.Toleration + 58, // 97: v1alpha1.IngressGatewayConfig.ingressPorts:type_name -> google.protobuf.Struct + 58, // 98: v1alpha1.IngressGatewayConfig.additionalContainers:type_name -> google.protobuf.Struct + 58, // 99: v1alpha1.IngressGatewayConfig.configVolumes:type_name -> google.protobuf.Struct + 55, // 100: v1alpha1.IngressGatewayConfig.runAsRoot:type_name -> google.protobuf.BoolValue + 11, // 101: v1alpha1.IngressGatewayConfig.serviceAccount:type_name -> v1alpha1.ServiceAccount + 55, // 102: v1alpha1.IngressGatewayZvpnConfig.enabled:type_name -> google.protobuf.BoolValue + 55, // 103: v1alpha1.MultiClusterConfig.enabled:type_name -> google.protobuf.BoolValue + 55, // 104: v1alpha1.MultiClusterConfig.includeEnvoyFilter:type_name -> google.protobuf.BoolValue + 2, // 105: v1alpha1.OutboundTrafficPolicyConfig.mode:type_name -> v1alpha1.OutboundTrafficPolicyConfig.Mode + 55, // 106: v1alpha1.PilotConfig.enabled:type_name -> google.protobuf.BoolValue + 55, // 107: v1alpha1.PilotConfig.autoscaleEnabled:type_name -> google.protobuf.BoolValue + 58, // 108: v1alpha1.PilotConfig.autoscaleBehavior:type_name -> google.protobuf.Struct + 10, // 109: v1alpha1.PilotConfig.resources:type_name -> v1alpha1.Resources + 9, // 110: v1alpha1.PilotConfig.cpu:type_name -> v1alpha1.TargetUtilizationConfig + 58, // 111: v1alpha1.PilotConfig.nodeSelector:type_name -> google.protobuf.Struct + 62, // 112: v1alpha1.PilotConfig.keepaliveMaxServerConnectionAge:type_name -> google.protobuf.Duration + 58, // 113: v1alpha1.PilotConfig.deploymentLabels:type_name -> google.protobuf.Struct + 58, // 114: v1alpha1.PilotConfig.podLabels:type_name -> google.protobuf.Struct + 55, // 115: v1alpha1.PilotConfig.configMap:type_name -> google.protobuf.BoolValue + 58, // 116: v1alpha1.PilotConfig.env:type_name -> google.protobuf.Struct + 57, // 117: v1alpha1.PilotConfig.affinity:type_name -> k8s.io.api.core.v1.Affinity + 50, // 118: v1alpha1.PilotConfig.rollingMaxSurge:type_name -> v1alpha1.IntOrString + 50, // 119: v1alpha1.PilotConfig.rollingMaxUnavailable:type_name -> v1alpha1.IntOrString + 61, // 120: v1alpha1.PilotConfig.tolerations:type_name -> k8s.io.api.core.v1.Toleration + 58, // 121: v1alpha1.PilotConfig.podAnnotations:type_name -> google.protobuf.Struct + 58, // 122: v1alpha1.PilotConfig.serviceAnnotations:type_name -> google.protobuf.Struct + 58, // 123: v1alpha1.PilotConfig.serviceAccountAnnotations:type_name -> google.protobuf.Struct + 56, // 124: v1alpha1.PilotConfig.tag:type_name -> google.protobuf.Value + 59, // 125: v1alpha1.PilotConfig.seccompProfile:type_name -> k8s.io.api.core.v1.SeccompProfile + 63, // 126: v1alpha1.PilotConfig.topologySpreadConstraints:type_name -> k8s.io.api.core.v1.TopologySpreadConstraint + 58, // 127: v1alpha1.PilotConfig.extraContainerArgs:type_name -> google.protobuf.Struct + 64, // 128: v1alpha1.PilotConfig.volumeMounts:type_name -> k8s.io.api.core.v1.VolumeMount + 65, // 129: v1alpha1.PilotConfig.volumes:type_name -> k8s.io.api.core.v1.Volume + 9, // 130: v1alpha1.PilotConfig.memory:type_name -> v1alpha1.TargetUtilizationConfig + 5, // 131: v1alpha1.PilotConfig.cni:type_name -> v1alpha1.CNIUsageConfig + 25, // 132: v1alpha1.PilotConfig.taint:type_name -> v1alpha1.PilotTaintControllerConfig + 0, // 133: v1alpha1.PilotIngressConfig.ingressControllerMode:type_name -> v1alpha1.ingressControllerMode + 55, // 134: v1alpha1.PilotPolicyConfig.enabled:type_name -> google.protobuf.BoolValue + 55, // 135: v1alpha1.TelemetryConfig.enabled:type_name -> google.protobuf.BoolValue + 29, // 136: v1alpha1.TelemetryConfig.v2:type_name -> v1alpha1.TelemetryV2Config + 55, // 137: v1alpha1.TelemetryV2Config.enabled:type_name -> google.protobuf.BoolValue + 30, // 138: v1alpha1.TelemetryV2Config.prometheus:type_name -> v1alpha1.TelemetryV2PrometheusConfig + 31, // 139: v1alpha1.TelemetryV2Config.stackdriver:type_name -> v1alpha1.TelemetryV2StackDriverConfig + 55, // 140: v1alpha1.TelemetryV2PrometheusConfig.enabled:type_name -> google.protobuf.BoolValue + 55, // 141: v1alpha1.TelemetryV2StackDriverConfig.enabled:type_name -> google.protobuf.BoolValue + 55, // 142: v1alpha1.ProxyConfig.enableCoreDump:type_name -> google.protobuf.BoolValue + 55, // 143: v1alpha1.ProxyConfig.privileged:type_name -> google.protobuf.BoolValue + 34, // 144: v1alpha1.ProxyConfig.startupProbe:type_name -> v1alpha1.StartupProbe + 10, // 145: v1alpha1.ProxyConfig.resources:type_name -> v1alpha1.Resources + 1, // 146: v1alpha1.ProxyConfig.tracer:type_name -> v1alpha1.tracer + 66, // 147: v1alpha1.ProxyConfig.lifecycle:type_name -> k8s.io.api.core.v1.Lifecycle + 55, // 148: v1alpha1.ProxyConfig.holdApplicationUntilProxyStarts:type_name -> google.protobuf.BoolValue + 55, // 149: v1alpha1.StartupProbe.enabled:type_name -> google.protobuf.BoolValue + 10, // 150: v1alpha1.ProxyInitConfig.resources:type_name -> v1alpha1.Resources + 58, // 151: v1alpha1.SDSConfig.token:type_name -> google.protobuf.Struct + 55, // 152: v1alpha1.SidecarInjectorConfig.enableNamespacesByDefault:type_name -> google.protobuf.BoolValue + 60, // 153: v1alpha1.SidecarInjectorConfig.neverInjectSelector:type_name -> k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector + 60, // 154: v1alpha1.SidecarInjectorConfig.alwaysInjectSelector:type_name -> k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector + 55, // 155: v1alpha1.SidecarInjectorConfig.rewriteAppHTTPProbe:type_name -> google.protobuf.BoolValue + 58, // 156: v1alpha1.SidecarInjectorConfig.injectedAnnotations:type_name -> google.protobuf.Struct + 58, // 157: v1alpha1.SidecarInjectorConfig.templates:type_name -> google.protobuf.Struct + 41, // 158: v1alpha1.TracerConfig.datadog:type_name -> v1alpha1.TracerDatadogConfig + 42, // 159: v1alpha1.TracerConfig.lightstep:type_name -> v1alpha1.TracerLightStepConfig + 43, // 160: v1alpha1.TracerConfig.zipkin:type_name -> v1alpha1.TracerZipkinConfig + 44, // 161: v1alpha1.TracerConfig.stackdriver:type_name -> v1alpha1.TracerStackdriverConfig + 55, // 162: v1alpha1.TracerStackdriverConfig.debug:type_name -> google.protobuf.BoolValue + 55, // 163: v1alpha1.BaseConfig.enableCRDTemplates:type_name -> google.protobuf.BoolValue + 55, // 164: v1alpha1.BaseConfig.enableIstioConfigCRDs:type_name -> google.protobuf.BoolValue + 55, // 165: v1alpha1.BaseConfig.validateGateway:type_name -> google.protobuf.BoolValue + 4, // 166: v1alpha1.Values.cni:type_name -> v1alpha1.CNIConfig + 15, // 167: v1alpha1.Values.gateways:type_name -> v1alpha1.GatewaysConfig + 16, // 168: v1alpha1.Values.global:type_name -> v1alpha1.GlobalConfig + 24, // 169: v1alpha1.Values.pilot:type_name -> v1alpha1.PilotConfig + 56, // 170: v1alpha1.Values.ztunnel:type_name -> google.protobuf.Value + 28, // 171: v1alpha1.Values.telemetry:type_name -> v1alpha1.TelemetryConfig + 39, // 172: v1alpha1.Values.sidecarInjectorWebhook:type_name -> v1alpha1.SidecarInjectorConfig + 5, // 173: v1alpha1.Values.istio_cni:type_name -> v1alpha1.CNIUsageConfig + 56, // 174: v1alpha1.Values.meshConfig:type_name -> google.protobuf.Value + 45, // 175: v1alpha1.Values.base:type_name -> v1alpha1.BaseConfig + 46, // 176: v1alpha1.Values.istiodRemote:type_name -> v1alpha1.IstiodRemoteConfig + 49, // 177: v1alpha1.Values.experimental:type_name -> v1alpha1.ExperimentalConfig + 55, // 178: v1alpha1.ZeroVPNConfig.enabled:type_name -> google.protobuf.BoolValue + 55, // 179: v1alpha1.ExperimentalConfig.stableValidationPolicy:type_name -> google.protobuf.BoolValue + 67, // 180: v1alpha1.IntOrString.intVal:type_name -> google.protobuf.Int32Value + 68, // 181: v1alpha1.IntOrString.strVal:type_name -> google.protobuf.StringValue + 182, // [182:182] is the sub-list for method output_type + 182, // [182:182] is the sub-list for method input_type + 182, // [182:182] is the sub-list for extension type_name + 182, // [182:182] is the sub-list for extension extendee + 0, // [0:182] is the sub-list for field type_name +} + +func init() { file_pkg_apis_istio_v1alpha1_values_types_proto_init() } +func file_pkg_apis_istio_v1alpha1_values_types_proto_init() { + if File_pkg_apis_istio_v1alpha1_values_types_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*ArchConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*CNIConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*CNIUsageConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*CNIAmbientConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*CNIRepairConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[5].Exporter = func(v any, i int) any { + switch v := v.(*ResourceQuotas); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[6].Exporter = func(v any, i int) any { + switch v := v.(*TargetUtilizationConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[7].Exporter = func(v any, i int) any { + switch v := v.(*Resources); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[8].Exporter = func(v any, i int) any { + switch v := v.(*ServiceAccount); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[9].Exporter = func(v any, i int) any { + switch v := v.(*DefaultPodDisruptionBudgetConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[10].Exporter = func(v any, i int) any { + switch v := v.(*DefaultResourcesConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[11].Exporter = func(v any, i int) any { + switch v := v.(*EgressGatewayConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[12].Exporter = func(v any, i int) any { + switch v := v.(*GatewaysConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[13].Exporter = func(v any, i int) any { + switch v := v.(*GlobalConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[14].Exporter = func(v any, i int) any { + switch v := v.(*STSConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[15].Exporter = func(v any, i int) any { + switch v := v.(*IstiodConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[16].Exporter = func(v any, i int) any { + switch v := v.(*GlobalLoggingConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[17].Exporter = func(v any, i int) any { + switch v := v.(*IngressGatewayConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[18].Exporter = func(v any, i int) any { + switch v := v.(*IngressGatewayZvpnConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[19].Exporter = func(v any, i int) any { + switch v := v.(*MultiClusterConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[20].Exporter = func(v any, i int) any { + switch v := v.(*OutboundTrafficPolicyConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[21].Exporter = func(v any, i int) any { + switch v := v.(*PilotConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[22].Exporter = func(v any, i int) any { + switch v := v.(*PilotTaintControllerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[23].Exporter = func(v any, i int) any { + switch v := v.(*PilotIngressConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[24].Exporter = func(v any, i int) any { + switch v := v.(*PilotPolicyConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[25].Exporter = func(v any, i int) any { + switch v := v.(*TelemetryConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[26].Exporter = func(v any, i int) any { + switch v := v.(*TelemetryV2Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[27].Exporter = func(v any, i int) any { + switch v := v.(*TelemetryV2PrometheusConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[28].Exporter = func(v any, i int) any { + switch v := v.(*TelemetryV2StackDriverConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[29].Exporter = func(v any, i int) any { + switch v := v.(*PortsConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[30].Exporter = func(v any, i int) any { + switch v := v.(*ProxyConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[31].Exporter = func(v any, i int) any { + switch v := v.(*StartupProbe); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[32].Exporter = func(v any, i int) any { + switch v := v.(*ProxyInitConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[33].Exporter = func(v any, i int) any { + switch v := v.(*ResourcesRequestsConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[34].Exporter = func(v any, i int) any { + switch v := v.(*SDSConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[35].Exporter = func(v any, i int) any { + switch v := v.(*SecretVolume); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[36].Exporter = func(v any, i int) any { + switch v := v.(*SidecarInjectorConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[37].Exporter = func(v any, i int) any { + switch v := v.(*TracerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[38].Exporter = func(v any, i int) any { + switch v := v.(*TracerDatadogConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[39].Exporter = func(v any, i int) any { + switch v := v.(*TracerLightStepConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[40].Exporter = func(v any, i int) any { + switch v := v.(*TracerZipkinConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[41].Exporter = func(v any, i int) any { + switch v := v.(*TracerStackdriverConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[42].Exporter = func(v any, i int) any { + switch v := v.(*BaseConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[43].Exporter = func(v any, i int) any { + switch v := v.(*IstiodRemoteConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[44].Exporter = func(v any, i int) any { + switch v := v.(*Values); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[45].Exporter = func(v any, i int) any { + switch v := v.(*ZeroVPNConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[46].Exporter = func(v any, i int) any { + switch v := v.(*ExperimentalConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[47].Exporter = func(v any, i int) any { + switch v := v.(*IntOrString); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_pkg_apis_istio_v1alpha1_values_types_proto_rawDesc, + NumEnums: 3, + NumMessages: 52, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_pkg_apis_istio_v1alpha1_values_types_proto_goTypes, + DependencyIndexes: file_pkg_apis_istio_v1alpha1_values_types_proto_depIdxs, + EnumInfos: file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes, + MessageInfos: file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes, + }.Build() + File_pkg_apis_istio_v1alpha1_values_types_proto = out.File + file_pkg_apis_istio_v1alpha1_values_types_proto_rawDesc = nil + file_pkg_apis_istio_v1alpha1_values_types_proto_goTypes = nil + file_pkg_apis_istio_v1alpha1_values_types_proto_depIdxs = nil +} diff --git a/operator/pkg/apis/istio/v1alpha1/values_types.proto b/operator/pkg/apis/istio/v1alpha1/values_types.proto new file mode 100644 index 0000000..f8eb9ea --- /dev/null +++ b/operator/pkg/apis/istio/v1alpha1/values_types.proto @@ -0,0 +1,1439 @@ +// Copyright Istio 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. + +syntax = 'proto3'; + +package v1alpha1; + +import "google/protobuf/any.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/wrappers.proto"; +import "k8s.io/api/core/v1/generated.proto"; +import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto"; + +// Package-wide variables from generator "generated". +option go_package = "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1"; + +// ArchConfig specifies the pod scheduling target architecture(amd64, ppc64le, s390x, arm64) +// for all the Istio control plane components. +message ArchConfig { + // Sets pod scheduling weight for amd64 arch + uint32 amd64 = 1; + + // Sets pod scheduling weight for ppc64le arch. + uint32 ppc64le = 2; + + // Sets pod scheduling weight for s390x arch. + uint32 s390x = 3; + + // Sets pod scheduling weight for arm64 arch. + uint32 arm64 = 4; +} + +// Configuration for CNI. +message CNIConfig { + // Controls whether CNI is installed. + google.protobuf.BoolValue enabled = 1; + + // Hub to pull the container image from. Image will be `Hub/Image:Tag-Variant`. + string hub = 2; + + // The container image tag to pull. Image will be `Hub/Image:Tag-Variant`. + google.protobuf.Value tag = 3; + + // The container image variant to pull. Options are "debug" or "distroless". Unset will use the default for the given version. + string variant = 29; + + // Image name to pull from. Image will be `Hub/Image:Tag-Variant`. + // If Image contains a "/", it will replace the entire `image` in the pod. + string image = 4; + + // Specifies the image pull policy. one of Always, Never, IfNotPresent. + // Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. + // + // More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + string pullPolicy = 5; + + // The directory path within the cluster node's filesystem where the CNI binaries are to be installed. Typically /var/lib/cni/bin. + string cniBinDir = 6; + + // The directory path within the cluster node's filesystem where the CNI configuration files are to be installed. Typically /etc/cni/net.d. + string cniConfDir = 7; + + // The name of the CNI plugin configuration file. Defaults to istio-cni.conf. + string cniConfFileName = 8; + + // The directory path within the cluster node's filesystem where network namespaces are located. + // Defaults to '/var/run/netns', in minikube/docker/others can be '/var/run/docker/netns'. + string cniNetnsDir = 31; + + // List of namespaces that should be ignored by the CNI plugin. + repeated string excludeNamespaces = 9; + + // K8s affinity to set on the istio-cni Pods. Can be used to exclude istio-cni from being scheduled on specified nodes. + k8s.io.api.core.v1.Affinity affinity = 20; + + // Additional annotations to apply to the istio-cni Pods. + google.protobuf.Struct podAnnotations = 10 [deprecated = true]; + + // PodSecurityPolicy cluster role. No longer used anywhere. + string psp_cluster_role = 11; + + // DEPRECATED. Configuration log level of istio-cni binary. By default, istio-cni sends all logs to the UDS server. + // To see the logs, change global.logging.level to cni:debug. + string logLevel = 12 [deprecated = true]; + + // Same as `global.logging.level`, but will override it if set + GlobalLoggingConfig logging = 25; + + // Configuration for the CNI Repair controller. + CNIRepairConfig repair = 13; + + // Configure the plugin as a chained CNI plugin. When true, the configuration is added to the CNI chain; when false, + // the configuration is added as a standalone file in the CNI configuration directory. + google.protobuf.BoolValue chained = 14; + + // The resource quotas configration for the CNI DaemonSet. + ResourceQuotas resource_quotas = 16; + + // The k8s resource requests and limits for the istio-cni Pods. + Resources resources = 17; + + // No longer used for CNI. See: https://github.com/istio/istio/issues/49004 + google.protobuf.BoolValue privileged = 18 [deprecated = true]; + + // The Container seccompProfile + // + // See: https://kubernetes.io/docs/tutorials/security/seccomp/ + k8s.io.api.core.v1.SeccompProfile seccompProfile = 19; + + // Configuration for Istio Ambient. + CNIAmbientConfig ambient = 21; + + // Specifies the CNI provider. Can be either "default" or "multus". When set to "multus", an additional + // NetworkAttachmentDefinition resource is deployed to the cluster to allow the istio-cni plugin to be + // invoked in a cluster using the Multus CNI plugin. + string provider = 22; + + // The number of pods that can be unavailable during a rolling update of the CNI DaemonSet (see + // `updateStrategy.rollingUpdate.maxUnavailable` here: + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/daemon-set-v1/#DaemonSetSpec). + // May be specified as a number of pods or as a percent of the total number + // of pods at the start of the update. + IntOrString rollingMaxUnavailable = 23; +} + +message CNIUsageConfig { + // Controls whether CNI should be used. + google.protobuf.BoolValue enabled = 1; + + google.protobuf.BoolValue chained = 2 [deprecated = true]; + + // Specifies the CNI provider. Can be either "default" or "multus". When set to "multus", an annotation + // `k8s.v1.cni.cncf.io/networks` is set on injected pods to point to a NetworkAttachmentDefinition + string provider = 3; +} + +message CNIAmbientConfig { + // Controls whether ambient redirection is enabled + google.protobuf.BoolValue enabled = 1; + + // The directory path containing the configuration files for Ambient. Defaults to /etc/ambient-config. + string configDir = 3; + + // If enabled, and ambient is enabled, DNS redirection will be enabled. + google.protobuf.BoolValue dnsCapture = 5; + + // UNSTABLE: If enabled, and ambient is enabled, enables ipv6 support + google.protobuf.BoolValue ipv6 = 7; +} + +message CNIRepairConfig { + // Controls whether repair behavior is enabled. + google.protobuf.BoolValue enabled = 1; + + // Hub to pull the container image from. Image will be `Hub/Image:Tag-Variant`. + string hub = 2; + + // The container image tag to pull. Image will be `Hub/Image:Tag-Variant`. + google.protobuf.Value tag = 3; + + // Image name to pull from. Image will be `Hub/Image:Tag-Variant`. + // If Image contains a "/", it will replace the entire `image` in the pod. + string image = 4; + + // The Repair controller has 3 modes (labelPods, deletePods, and repairPods). Pick which one meets your use cases. Note only one may be used. + // The mode defines the action the controller will take when a pod is detected as broken. + // If labelPods is true, the controller will label all broken pods with =. + // This is only capable of identifying broken pods; the user is responsible for fixing them (generally, by deleting them). + // Note this gives the DaemonSet a relatively high privilege, as modifying pod metadata/status can have wider impacts. + bool labelPods = 5; + + // The Repair controller has 3 modes (labelPods, deletePods, and repairPods). Pick which one meets your use cases. Note only one may be used. + // The mode defines the action the controller will take when a pod is detected as broken. + // If repairPods is true, the controller will dynamically repair any broken pod by setting up the pod networking configuration even after it has started. + // Note the pod will be crashlooping, so this may take a few minutes to become fully functional based on when the retry occurs. + // This requires no RBAC privilege, but will require the CNI agent to run as a privileged pod. + bool repairPods = 11; + + // No longer used. + string createEvents = 6 [deprecated = true]; + + // The Repair controller has 3 modes (labelPods, deletePods, and repairPods). Pick which one meets your use cases. Note only one may be used. + // The mode defines the action the controller will take when a pod is detected as broken. + // If deletePods is true, the controller will delete the broken pod. The pod will then be rescheduled, hopefully onto a node that is fully ready. + // Note this gives the DaemonSet a relatively high privilege, as it can delete any Pod. + bool deletePods = 7; + + // The label key to apply to a broken pod when the controller is in labelPods mode. + string brokenPodLabelKey = 8; + + // The label value to apply to a broken pod when the controller is in labelPods mode. + string brokenPodLabelValue = 9; + + // The name of the init container to use for the repairPods mode. + string initContainerName = 10; +} + +// Configuration for the resource quotas for the CNI DaemonSet. +message ResourceQuotas { + // Controls whether to create resource quotas or not for the CNI DaemonSet. + google.protobuf.BoolValue enabled = 1; + + // The hard limit on the number of pods in the namespace where the CNI DaemonSet is deployed. + int64 pods = 2; +} + +// Configuration for CPU or memory target utilization for HorizontalPodAutoscaler target. +message TargetUtilizationConfig { + // K8s utilization setting for HorizontalPodAutoscaler target. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + int32 targetAverageUtilization = 1; +} + +// Compute resources required by a container. +message Resources { + // The maximum amount of compute resources allowed. + // More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + map limits = 1; + + // The minimum amount of compute resources required. If Requests is omitted for a container, + // it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. + // More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + map requests = 2; +} + +// Mirrors ServiceAccount for unmarshaling. +message ServiceAccount { + google.protobuf.Struct annotations = 1; +} + +// DefaultPodDisruptionBudgetConfig specifies the default pod disruption budget configuration. +// +// See https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ +message DefaultPodDisruptionBudgetConfig { + // Controls whether a PodDisruptionBudget with a default minAvailable value of 1 is created for each deployment. + google.protobuf.BoolValue enabled = 1; +} + +// DefaultResourcesConfig specifies the default k8s resources settings for all Istio control plane components. +message DefaultResourcesConfig { + // k8s resources settings. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + ResourcesRequestsConfig requests = 1; +} + +// Configuration for an egress gateway. +message EgressGatewayConfig { + // Controls whether auto scaling with a HorizontalPodAutoscaler is enabled. + google.protobuf.BoolValue autoscaleEnabled = 1; + + // maxReplicas setting for HorizontalPodAutoscaler. + uint32 autoscaleMax = 2; + + // minReplicas setting for HorizontalPodAutoscaler. + uint32 autoscaleMin = 3; + + // K8s memory utilization setting for HorizontalPodAutoscaler target. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + TargetUtilizationConfig memory = 4 [deprecated = true]; + + // K8s utilization setting for HorizontalPodAutoscaler target. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + TargetUtilizationConfig cpu = 5 [deprecated = true]; + + google.protobuf.BoolValue customService = 6; + + // Controls whether an egress gateway is enabled. + google.protobuf.BoolValue enabled = 7; + + // Environment variables passed to the proxy container. + google.protobuf.Struct env = 8; + + map labels = 9; + + string name = 25; + + // K8s node selector. + // + // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + google.protobuf.Struct nodeSelector = 10 [deprecated = true]; + + // K8s annotations for pods. + // + // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + google.protobuf.Struct podAnnotations = 11 [deprecated = true]; + + // Pod anti-affinity label selector. + // + // Specify the pod anti-affinity that allows you to constrain which nodes + // your pod is eligible to be scheduled based on labels on pods that are + // already running on the node rather than based on labels on nodes. + // There are currently two types of anti-affinity: + // "requiredDuringSchedulingIgnoredDuringExecution" + // "preferredDuringSchedulingIgnoredDuringExecution" + // which denote “hard” vs. “soft” requirements, you can define your values + // in "podAntiAffinityLabelSelector" and "podAntiAffinityTermLabelSelector" + // correspondingly. + // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + // + // Examples: + // podAntiAffinityLabelSelector: + // - key: security + // operator: In + // values: S1,S2 + // topologyKey: "kubernetes.io/hostname" + // This pod anti-affinity rule says that the pod requires not to be scheduled + // onto a node if that node is already running a pod with label having key + // “security” and value “S1”. + repeated k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector podAntiAffinityLabelSelector = 12 [deprecated = true]; + + // See PodAntiAffinityLabelSelector. + repeated k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector podAntiAffinityTermLabelSelector = 13 [deprecated = true]; + + // Ports Configuration for the egress gateway service. + repeated PortsConfig ports = 14; + + // K8s resources settings. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + Resources resources = 15 [deprecated = true]; + + // Config for secret volume mounts. + repeated SecretVolume secretVolumes = 16; + + // Annotations to add to the egress gateway service. + google.protobuf.Struct serviceAnnotations = 17; + + // Service type. + // + // See https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + string type = 18; + + // Enables cross-cluster access using SNI matching. + ZeroVPNConfig zvpn = 19; + + repeated k8s.io.api.core.v1.Toleration tolerations = 20 [deprecated = true]; + + // K8s rolling update strategy + IntOrString rollingMaxSurge = 21 [deprecated = true]; + + // The number of pods that can be unavailable during a rolling update (see + // `strategy.rollingUpdate.maxUnavailable` here: + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/#DeploymentSpec). + // May be specified as a number of pods or as a percent of the total number + // of pods at the start of the update. + IntOrString rollingMaxUnavailable = 22 [deprecated = true]; + + repeated google.protobuf.Struct configVolumes = 23; + + repeated google.protobuf.Struct additionalContainers = 24; + + google.protobuf.BoolValue runAsRoot = 26; + + // The injection template to use for the gateway. If not set, no injection will be performed. + string injectionTemplate = 27; + + ServiceAccount serviceAccount = 28; + + // Defines which IP family to use for single stack or the order of IP families for dual-stack. + // Valid list items are "IPv4", "IPv6". + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + repeated string ipFamilies = 29; + + // Controls whether Services are configured to use IPv4, IPv6, or both. Valid options + // are PreferDualStack, RequireDualStack, and SingleStack. + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + string ipFamilyPolicy = 30; + // Next available 31. +} + +// Configuration for gateways. +message GatewaysConfig { + // Configuration for an egress gateway. + EgressGatewayConfig istio_egressgateway = 1 [json_name = "istio-egressgateway"]; + + // Controls whether any gateways are enabled. + google.protobuf.BoolValue enabled = 2; + + // Configuration for an ingress gateway. + IngressGatewayConfig istio_ingressgateway = 4 [json_name = "istio-ingressgateway"]; + + google.protobuf.Value securityContext = 10; + + google.protobuf.Value seccompProfile = 12; +} + +// Global Configuration for Istio components. +message GlobalConfig { + // Specifies pod scheduling arch(amd64, ppc64le, s390x, arm64) and weight as follows: + // 0 - Never scheduled + // 1 - Least preferred + // 2 - No preference + // 3 - Most preferred + // + // Deprecated: replaced by the affinity k8s settings which allows architecture nodeAffinity configuration of this behavior. + ArchConfig arch = 1 [deprecated = true]; + + // List of certSigners to allow "approve" action in the ClusterRole + repeated string certSigners = 68; + + // Controls whether the server-side validation is enabled. + google.protobuf.BoolValue configValidation = 3; + + // Default k8s node selector for all the Istio control plane components + // + // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + google.protobuf.Struct defaultNodeSelector = 6 [deprecated = true]; + + // Specifies the default pod disruption budget configuration. + DefaultPodDisruptionBudgetConfig defaultPodDisruptionBudget = 7 [deprecated = true]; + + // Default k8s resources settings for all Istio control plane components. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + DefaultResourcesConfig defaultResources = 9 [deprecated = true]; + + // Default node tolerations to be applied to all deployments so that all pods can be + // scheduled to nodes with matching taints. Each component can overwrite + // these default values by adding its tolerations block in the relevant section below + // and setting the desired values. + // Configure this field in case that all pods of Istio control plane are expected to + // be scheduled to particular nodes with specified taints. + repeated k8s.io.api.core.v1.Toleration defaultTolerations = 55 [deprecated = true]; + + // Specifies the docker hub for Istio images. + string hub = 12; + + // Specifies the image pull policy for the Istio images. one of Always, Never, IfNotPresent. + // Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. + // + // More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + string imagePullPolicy = 13; + + // ImagePullSecrets for the control plane ServiceAccount, list of secrets in the same namespace + // to use for pulling any images in pods that reference this ServiceAccount. + // Must be set for any cluster configured with private docker registry. + repeated string imagePullSecrets = 37; + + // Specifies the default namespace for the Istio control plane components. + string istioNamespace = 14; + + // Specifies whether istio components should output logs in json format by adding --log_as_json argument to each container. + google.protobuf.BoolValue logAsJson = 36; + + // Specifies the global logging level settings for the Istio control plane components. + GlobalLoggingConfig logging = 17; + + // The Mesh Identifier. It should be unique within the scope where + // meshes will interact with each other, but it is not required to be + // globally/universally unique. For example, if any of the following are true, + // then two meshes must have different Mesh IDs: + // - Meshes will have their telemetry aggregated in one place + // - Meshes will be federated together + // - Policy will be written referencing one mesh from the other + // + // If an administrator expects that any of these conditions may become true in + // the future, they should ensure their meshes have different Mesh IDs + // assigned. + // + // Within a multicluster mesh, each cluster must be (manually or auto) + // configured to have the same Mesh ID value. If an existing cluster 'joins' a + // multicluster mesh, it will need to be migrated to the new mesh ID. Details + // of migration TBD, and it may be a disruptive operation to change the Mesh + // ID post-install. + // + // If the mesh admin does not specify a value, Istio will use the value of the + // mesh's Trust Domain. The best practice is to select a proper Trust Domain + // value. + string meshID = 53; + + // Configure the mesh networks to be used by the Split Horizon EDS. + // + // The following example defines two networks with different endpoints association methods. + // For `network1` all endpoints that their IP belongs to the provided CIDR range will be + // mapped to network1. The gateway for this network example is specified by its public IP + // address and port. + // The second network, `network2`, in this example is defined differently with all endpoints + // retrieved through the specified Multi-Cluster registry being mapped to network2. The + // gateway is also defined differently with the name of the gateway service on the remote + // cluster. The public IP for the gateway will be determined from that remote service (only + // LoadBalancer gateway service type is currently supported, for a NodePort type gateway service, + // it still need to be configured manually). + // + // meshNetworks: + // network1: + // endpoints: + // - fromCidr: "192.168.0.1/24" + // gateways: + // - address: 1.1.1.1 + // port: 80 + // network2: + // endpoints: + // - fromRegistry: reg1 + // gateways: + // - registryServiceName: istio-ingressgateway.istio-system.svc.cluster.local + // port: 443 + // + google.protobuf.Struct meshNetworks = 19; + + // Specifies the Configuration for Istio mesh across multiple clusters through Istio gateways. + MultiClusterConfig multiCluster = 22; + + // Network defines the network this cluster belong to. This name + // corresponds to the networks in the map of mesh networks. + string network = 39; + + // Custom DNS config for the pod to resolve names of services in other + // clusters. Use this to add additional search domains, and other settings. + // see https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#dns-config + // This does not apply to gateway pods as they typically need a different + // set of DNS settings than the normal application pods (e.g. in multicluster scenarios). + repeated string podDNSSearchNamespaces = 43; + + // Controls whether the creation of the sidecar injector ConfigMap should be skipped. + // Defaults to false. When set to true, the sidecar injector ConfigMap will not be created. + google.protobuf.BoolValue omitSidecarInjectorConfigMap = 38; + + // Controls whether the WebhookConfiguration resource(s) should be created. The current behavior + // of Istiod is to manage its own webhook configurations. + // When this option is set to true, Istio Operator, instead of webhooks, manages the + // webhook configurations. When this option is set as false, webhooks manage their + // own webhook configurations. + google.protobuf.BoolValue operatorManageWebhooks = 41; + + // Specifies the k8s priorityClassName for the istio control plane components. + // + // See https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass + string priorityClassName = 27 [deprecated = true]; + + // Specifies how proxies are configured within Istio. + ProxyConfig proxy = 28; + + // Specifies the Configuration for proxy_init container which sets the pods' networking to intercept the inbound/outbound traffic. + ProxyInitConfig proxy_init = 29 [json_name = "proxy_init"]; + + // Specifies the Configuration for the SecretDiscoveryService instead of using K8S secrets to mount the certificates. + SDSConfig sds = 30; + + // Specifies the tag for the Istio docker images. + google.protobuf.Value tag = 31; + + // The variant of the Istio container images to use. Options are "debug" or "distroless". Unset will use the default for the given version. + string variant = 67; + + // Specifies the Configuration for each of the supported tracers. + TracerConfig tracer = 33; + + // Specifies the Istio control plane’s pilot Pod IP address or remote cluster DNS resolvable hostname. + string remotePilotAddress = 48; + + // Specifies the configution of istiod + IstiodConfig istiod = 54; + + // Configure the Pilot certificate provider. + // Currently, four providers are supported: "kubernetes", "istiod", "custom" and "none". + string pilotCertProvider = 56; + + // Configure the policy for validating JWT. + // This is deprecated and has no effect. + string jwtPolicy = 57 [deprecated = true]; + + // Specifies the configuration for Security Token Service. + STSConfig sts = 58; + + // Configures the revision this control plane is a part of + string revision = 59; + + // Controls whether the in-cluster MTLS key and certs are loaded from the secret volume mounts. + google.protobuf.BoolValue mountMtlsCerts = 60; + + // The address of the CA for CSR. + string caAddress = 61; + + // Controls whether one external istiod is enabled. + google.protobuf.BoolValue externalIstiod = 62; + + // Controls whether a remote cluster is the config cluster for an external istiod + google.protobuf.BoolValue configCluster = 64; + + // The name of the CA for workloads. + // For example, when caName=GkeWorkloadCertificate, GKE workload certificates + // will be used as the certificates for workloads. + // The default value is "" and when caName="", the CA will be configured by other + // mechanisms (e.g., environmental variable CA_PROVIDER). + string caName = 65; + + // TODO: remove this? + // No longer used. + google.protobuf.BoolValue autoscalingv2API = 66; + + // Platform in which Istio is deployed. Possible values are: "openshift" and "gcp" + // An empty value means it is a vanilla Kubernetes distribution, therefore no special + // treatment will be considered. + string platform = 69; + + // Defines which IP family to use for single stack or the order of IP families for dual-stack. + // Valid list items are "IPv4", "IPv6". + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + repeated string ipFamilies = 70; + + // Controls whether Services are configured to use IPv4, IPv6, or both. Valid options + // are PreferDualStack, RequireDualStack, and SingleStack. + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + string ipFamilyPolicy = 71; + // The next available key is 72 +} + +// Configuration for Security Token Service (STS) server. +// +// See https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-16 +message STSConfig { + uint32 servicePort = 1; +} + +message IstiodConfig { + // If enabled, istiod will perform config analysis + google.protobuf.BoolValue enableAnalysis = 2; +} + +// GlobalLoggingConfig specifies the global logging level settings for the Istio control plane components. +message GlobalLoggingConfig { + // Comma-separated minimum per-scope logging level of messages to output, in the form of :,: + // The control plane has different scopes depending on component, but can configure default log level across all components + // If empty, default scope and level will be used as configured in code + string level = 1; +} + +// Configuration for an ingress gateway. +message IngressGatewayConfig { + // Controls whether auto scaling with a HorizontalPodAutoscaler is enabled. + google.protobuf.BoolValue autoscaleEnabled = 1; + + // maxReplicas setting for HorizontalPodAutoscaler. + uint32 autoscaleMax = 2; + + // minReplicas setting for HorizontalPodAutoscaler. + uint32 autoscaleMin = 3; + + // K8s memory utilization setting for HorizontalPodAutoscaler target. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + TargetUtilizationConfig memory = 4 [deprecated = true]; + + // K8s cpu utilization setting for HorizontalPodAutoscaler target. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + TargetUtilizationConfig cpu = 5 [deprecated = true]; + + google.protobuf.BoolValue customService = 6; + + // Controls whether an ingress gateway is enabled. + google.protobuf.BoolValue enabled = 10; + + // Environment variables passed to the proxy container. + google.protobuf.Struct env = 11; + + map labels = 15; + + string loadBalancerIP = 16; + + repeated string loadBalancerSourceRanges = 17; + + string name = 44; + + // K8s node selector. + // + // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + google.protobuf.Struct nodeSelector = 19 [deprecated = true]; + + // K8s annotations for pods. + // + // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + google.protobuf.Struct podAnnotations = 20 [deprecated = true]; + + // See EgressGatewayConfig. + repeated k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector podAntiAffinityLabelSelector = 21 [deprecated = true]; + + // See EgressGatewayConfig. + repeated k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector podAntiAffinityTermLabelSelector = 22 [deprecated = true]; + + // Port Configuration for the ingress gateway. + repeated PortsConfig ports = 23; + + // Number of replicas for the ingress gateway Deployment. + uint32 replicaCount = 24 [deprecated = true]; + + // K8s resources settings. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + google.protobuf.Struct resources = 25 [deprecated = true]; + + // Config for secret volume mounts. + repeated SecretVolume secretVolumes = 27; + + // Annotations to add to the egress gateway service. + google.protobuf.Struct serviceAnnotations = 28; + + // Service type. + // + // See https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + string type = 29; + + // Enables cross-cluster access using SNI matching. + IngressGatewayZvpnConfig zvpn = 30; + + // K8s rolling update strategy + IntOrString rollingMaxSurge = 31 [deprecated = true]; + + // The number of pods that can be unavailable during a rolling update (see + // `strategy.rollingUpdate.maxUnavailable` here: + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/#DeploymentSpec). + // May be specified as a number of pods or as a percent of the total number + // of pods at the start of the update. + IntOrString rollingMaxUnavailable = 32 [deprecated = true]; + + string externalTrafficPolicy = 34; + + repeated k8s.io.api.core.v1.Toleration tolerations = 35 [deprecated = true]; + + repeated google.protobuf.Struct ingressPorts = 36; + + repeated google.protobuf.Struct additionalContainers = 37; + + repeated google.protobuf.Struct configVolumes = 38; + + google.protobuf.BoolValue runAsRoot = 45; + + // The injection template to use for the gateway. If not set, no injection will be performed. + string injectionTemplate = 46; + + ServiceAccount serviceAccount = 47; + + // Defines which IP family to use for single stack or the order of IP families for dual-stack. + // Valid list items are "IPv4", "IPv6". + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + repeated string ipFamilies = 48; + + // Controls whether Services are configured to use IPv4, IPv6, or both. Valid options + // are PreferDualStack, RequireDualStack, and SingleStack. + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + string ipFamilyPolicy = 49; + // Next available 50. +} + +// IngressGatewayZvpnConfig enables cross-cluster access using SNI matching. +message IngressGatewayZvpnConfig { + // Controls whether ZeroVPN is enabled. + google.protobuf.BoolValue enabled = 1; + + string suffix = 2; +} + +// MultiClusterConfig specifies the Configuration for Istio mesh across multiple clusters through the istio gateways. +message MultiClusterConfig { + // Enables the connection between two kubernetes clusters via their respective ingressgateway services. + // Use if the pods in each cluster cannot directly talk to one another. + google.protobuf.BoolValue enabled = 1; + + // The name of the cluster this installation will run in. This is required for sidecar injection + // to properly label proxies + string clusterName = 2; + + // The suffix for global service names. + string globalDomainSuffix = 3; + + // Enable envoy filter to translate `globalDomainSuffix` to cluster local suffix for cross cluster communication. + google.protobuf.BoolValue includeEnvoyFilter = 4; +} + +// OutboundTrafficPolicyConfig controls the default behavior of the sidecar for handling outbound traffic from the application. +message OutboundTrafficPolicyConfig { + // Specifies the sidecar's default behavior when handling outbound traffic from the application. + enum Mode { + // Outbound traffic to unknown destinations will be allowed, in case there are no services or ServiceEntries for the destination port + ALLOW_ANY = 0; + // Restrict outbound traffic to services defined in the service registry as well as those defined through ServiceEntries + REGISTRY_ONLY = 1; + } + Mode mode = 2; +} + +// Configuration for Pilot. +message PilotConfig { + // Controls whether Pilot is enabled. + google.protobuf.BoolValue enabled = 1; + + // Controls whether a HorizontalPodAutoscaler is installed for Pilot. + google.protobuf.BoolValue autoscaleEnabled = 2; + + // Minimum number of replicas in the HorizontalPodAutoscaler for Pilot. + uint32 autoscaleMin = 3; + + // Maximum number of replicas in the HorizontalPodAutoscaler for Pilot. + uint32 autoscaleMax = 4; + + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior + google.protobuf.Struct autoscaleBehavior = 40; + + // Number of replicas in the Pilot Deployment. + uint32 replicaCount = 5 [deprecated = true]; + + // Image name used for Pilot. + // + // This can be set either to image name if hub is also set, or can be set to the full hub:name string. + // + // Examples: custom-pilot, docker.io/someuser:custom-pilot + string image = 6; + + // Trace sampling fraction. + // + // Used to set the fraction of time that traces are sampled. Higher values are more accurate but add CPU overhead. + // + // Allowed values: 0.0 to 1.0 + double traceSampling = 8; + + // K8s resources settings. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + Resources resources = 9 [deprecated = true]; + + // Target CPU utilization used in HorizontalPodAutoscaler. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + TargetUtilizationConfig cpu = 11 [deprecated = true]; + + // K8s node selector. + // + // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + google.protobuf.Struct nodeSelector = 12 [deprecated = true]; + + // Maximum duration that a sidecar can be connected to a pilot. + // + // This setting balances out load across pilot instances, but adds some resource overhead. + // + // Examples: 300s, 30m, 1h + google.protobuf.Duration keepaliveMaxServerConnectionAge = 13; + + // Labels that are added to Pilot deployment. + // + // See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + google.protobuf.Struct deploymentLabels = 14; + + // Labels that are added to Pilot pods. + // + // See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + google.protobuf.Struct podLabels = 36; + + // Configuration settings passed to Pilot as a ConfigMap. + // + // This controls whether the mesh config map, generated from values.yaml is generated. + // If false, pilot wil use default values or user-supplied values, in that order of preference. + google.protobuf.BoolValue configMap = 18; + + // Environment variables passed to the Pilot container. + // + // Examples: + // env: + // ENV_VAR_1: value1 + // ENV_VAR_2: value2 + google.protobuf.Struct env = 21; + + // K8s affinity to set on the Pilot Pods. + k8s.io.api.core.v1.Affinity affinity = 22; + + // K8s rolling update strategy + IntOrString rollingMaxSurge = 24 [deprecated = true]; + + // The number of pods that can be unavailable during a rolling update (see + // `strategy.rollingUpdate.maxUnavailable` here: + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/#DeploymentSpec). + // May be specified as a number of pods or as a percent of the total number + // of pods at the start of the update. + IntOrString rollingMaxUnavailable = 25 [deprecated = true]; + + // The node tolerations to be applied to the Pilot deployment so that it can be + // scheduled to particular nodes with matching taints. + // More info: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling + repeated k8s.io.api.core.v1.Toleration tolerations = 26 [deprecated = true]; + + // K8s annotations for pods. + // + // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + google.protobuf.Struct podAnnotations = 30 [deprecated = true]; + + // K8s annotations for the Service. + // + // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + google.protobuf.Struct serviceAnnotations = 37; + + // K8s annotations for the service account + google.protobuf.Struct serviceAccountAnnotations = 56; + + // Specifies an extra root certificate in PEM format. This certificate will be trusted + // by pilot when resolving JWKS URIs. + string jwksResolverExtraRootCA = 32; + + // Hub to pull the container image from. Image will be `Hub/Image:Tag-Variant`. + string hub = 34; + + // The container image tag to pull. Image will be `Hub/Image:Tag-Variant`. + google.protobuf.Value tag = 35; + + // The container image variant to pull. Options are "debug" or "distroless". Unset will use the default for the given version. + string variant = 39; + + // The seccompProfile for the Pilot container. + // + // See: https://kubernetes.io/docs/tutorials/security/seccomp/ + k8s.io.api.core.v1.SeccompProfile seccompProfile = 38; + + // The k8s topologySpreadConstraints for the Pilot pods. + repeated k8s.io.api.core.v1.TopologySpreadConstraint topologySpreadConstraints = 41; + + // Additional container arguments for the Pilot container. + repeated google.protobuf.Struct extraContainerArgs = 42; + + // Additional volumeMounts to add to the Pilot container. + repeated k8s.io.api.core.v1.VolumeMount volumeMounts = 49; + + // Additional volumes to add to the Pilot Pod. + repeated k8s.io.api.core.v1.Volume volumes = 51; + + // Defines which IP family to use for single stack or the order of IP families for dual-stack. + // Valid list items are "IPv4", "IPv6". + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + repeated string ipFamilies = 52; + + // Controls whether Services are configured to use IPv4, IPv6, or both. Valid options + // are PreferDualStack, RequireDualStack, and SingleStack. + // More info: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + string ipFamilyPolicy = 53; + + // Target memory utilization used in HorizontalPodAutoscaler. + // + // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + TargetUtilizationConfig memory = 54 [deprecated = true]; + + // Configures whether to use an existing CNI installation for workloads + CNIUsageConfig cni = 55; + + PilotTaintControllerConfig taint = 57; + + // If set, `istiod` will allow connections from trusted node proxy ztunnels + // in the provided namespace. + string trustedZtunnelNamespace = 59; +} + +message PilotTaintControllerConfig { + // Enable the untaint controller for new nodes. This aims to solve a race for CNI installation on + // new nodes. For this to work, the newly added nodes need to have the istio CNI taint as they are + // added to the cluster. This is usually done by configuring the cluster infra provider. + bool enabled = 1; + + // The namespace of the CNI daemonset, incase it's not the same as istiod. + string namespace = 2; +} + +// Controls legacy k8s ingress. Only one pilot profile should enable ingress support. +message PilotIngressConfig { + // Sets the type ingress service for Pilot. + // + // If empty, node-port is assumed. + // + // Allowed values: node-port, istio-ingressgateway, ingress + string ingressService = 1; + + ingressControllerMode ingressControllerMode = 2; + // If mode is STRICT, this value must be set on "kubernetes.io/ingress.class" annotation to activate. + string ingressClass = 3; +} + +// Mode for the ingress controller. +enum ingressControllerMode { + // Unspecified Istio ingress controller. + UNSPECIFIED = 0; + // Selects all Ingress resources, with or without Istio annotation. + DEFAULT = 1; + // Selects only resources with istio annotation. + STRICT = 2; + // No ingress or sync. + OFF = 3; +} + +// Controls whether Istio policy is applied to Pilot. +message PilotPolicyConfig { + // Controls whether Istio policy is applied to Pilot. + google.protobuf.BoolValue enabled = 1; +} + +// Controls telemetry configuration +message TelemetryConfig { + // Controls whether telemetry is exported for Pilot. + google.protobuf.BoolValue enabled = 1; + + // Configuration for Telemetry v2. + TelemetryV2Config v2 = 3; +} + +// Controls whether pilot will configure telemetry v2. +message TelemetryV2Config { + // Controls whether pilot will configure telemetry v2. + google.protobuf.BoolValue enabled = 1; + + // Telemetry v2 settings for prometheus. + TelemetryV2PrometheusConfig prometheus = 2; + + // Telemetry v2 settings for stackdriver. + TelemetryV2StackDriverConfig stackdriver = 3; +} + +// Controls telemetry v2 prometheus settings. +message TelemetryV2PrometheusConfig { + // Controls whether stats envoyfilter would be enabled or not. + google.protobuf.BoolValue enabled = 1; +} + +// TelemetryV2StackDriverConfig controls telemetry v2 stackdriver settings. +message TelemetryV2StackDriverConfig { + google.protobuf.BoolValue enabled = 1; +} + +// Configuration for a port. +message PortsConfig { + // Port name. + string name = 1; + + // Port number. + int32 port = 2; + + // NodePort number. + int32 nodePort = 3; + + // Target port number. + int32 targetPort = 4; + + // Protocol name. + string protocol = 5; +} + +// Configuration for Proxy. +message ProxyConfig { + // Controls the 'policy' in the sidecar injector. + string autoInject = 4; + + // Domain for the cluster, default: "cluster.local". + // + // K8s allows this to be customized, see https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/ + string clusterDomain = 5; + + // Per Component log level for proxy, applies to gateways and sidecars. + // + // If a component level is not set, then the global "logLevel" will be used. If left empty, "misc:error" is used. + string componentLogLevel = 6; + + // Enables core dumps for newly injected sidecars. + // + // If set, newly injected sidecars will have core dumps enabled. + google.protobuf.BoolValue enableCoreDump = 9; + + // Specifies the Istio ingress ports not to capture. + string excludeInboundPorts = 12; + + // Lists the excluded IP ranges of Istio egress traffic that the sidecar captures. + string excludeIPRanges = 13; + + // Image name or path for the proxy, default: "proxyv2". + // + // If registry or tag are not specified, global.hub and global.tag are used. + // + // Examples: my-proxy (uses global.hub/tag), docker.io/myrepo/my-proxy:v1.0.0 + string image = 14; + + // Lists the IP ranges of Istio egress traffic that the sidecar captures. + // + // Example: "172.30.0.0/16,172.20.0.0/16" + // This would only capture egress traffic on those two IP Ranges, all other outbound traffic would # be allowed by the sidecar." + string includeIPRanges = 16; + + // Log level for proxy, applies to gateways and sidecars. If left empty, "warning" is used. Expected values are: trace\|debug\|info\|warning\|error\|critical\|off + string logLevel = 18; + + // Path to the file to which the proxy will write outlier detection logs. + // + // Example: "/dev/stdout" + // This would write the logs to standard output. + string outlierLogPath = 42; + + // Enables privileged securityContext for the istio-proxy container. + // + // See https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + google.protobuf.BoolValue privileged = 19; + + // Sets the initial delay for readiness probes in seconds. + uint32 readinessInitialDelaySeconds = 20; + + // Sets the interval between readiness probes in seconds. + uint32 readinessPeriodSeconds = 21; + + // Sets the number of successive failed probes before indicating readiness failure. + uint32 readinessFailureThreshold = 22; + + // Configures the startup probe for the istio-proxy container. + StartupProbe startupProbe = 41; + + // Default port used for the Pilot agent's health checks. + uint32 statusPort = 23; + + // K8s resources settings. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + Resources resources = 24 [deprecated = true]; + + // Specify which tracer to use. One of: zipkin, lightstep, datadog, stackdriver. + // If using stackdriver tracer outside GCP, set env GOOGLE_APPLICATION_CREDENTIALS to the GCP credential file. + tracer tracer = 25; + + // A comma separated list of outbound ports to be excluded from redirection to Envoy. + string excludeOutboundPorts = 28; + + // The k8s lifecycle hooks definition (pod.spec.containers.lifecycle) for the proxy container. + // More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + k8s.io.api.core.v1.Lifecycle lifecycle = 36; + + // Controls if sidecar is injected at the front of the container list and blocks the start of the other containers until the proxy is ready + // + // Deprecated: replaced by ProxyConfig setting which allows per-pod configuration of this behavior. + google.protobuf.BoolValue holdApplicationUntilProxyStarts = 37 [deprecated = true]; + + // A comma separated list of inbound ports for which traffic is to be redirected to Envoy. + // The wildcard character '*' can be used to configure redirection for all ports. + string includeInboundPorts = 38; + + // A comma separated list of outbound ports for which traffic is to be redirected to Envoy, regardless of the destination IP. + string includeOutboundPorts = 39; +} + +message StartupProbe { + // Enables or disables a startup probe. + // For optimal startup times, changing this should be tied to the readiness probe values. + // + // If the probe is enabled, it is recommended to have delay=0s,period=15s,failureThreshold=4. + // This ensures the pod is marked ready immediately after the startup probe passes (which has a 1s poll interval), + // and doesn't spam the readiness endpoint too much + // + // If the probe is disabled, it is recommended to have delay=1s,period=2s,failureThreshold=30. + // This ensures the startup is reasonable fast (polling every 2s). 1s delay is used since the startup is not often ready instantly. + google.protobuf.BoolValue enabled = 1; + + // Minimum consecutive failures for the probe to be considered failed after having succeeded. + uint32 failureThreshold = 2; +} + +// Specifies which tracer to use. +enum tracer { + zipkin = 0; + lightstep = 1; + datadog = 2; + stackdriver = 3; + openCensusAgent = 4; + none = 5; +} + +// Configuration for proxy_init container which sets the pods' networking to intercept the inbound/outbound traffic. +message ProxyInitConfig { + // Specifies the image for the proxy_init container. + string image = 1; + // K8s resources settings. + // + // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container + Resources resources = 5 [deprecated = true]; +} + +// Configuration for K8s resource requests. +message ResourcesRequestsConfig { + // CPU requests. + string cpu = 1; + + // Memory requests. + string memory = 2; +} + +// Configuration for the SecretDiscoveryService instead of using K8S secrets to mount the certificates. +message SDSConfig { + google.protobuf.Struct token = 5 [deprecated = true]; +} + +// Configuration for secret volume mounts. +// +// See https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets. +message SecretVolume { + string mountPath = 1; + + string name = 2; + + string secretName = 3; +} + +// SidecarInjectorConfig is described in istio.io documentation. +message SidecarInjectorConfig { + // Enables sidecar auto-injection in namespaces by default. + google.protobuf.BoolValue enableNamespacesByDefault = 2; + + // Setting this to `IfNeeded` will result in the sidecar injector being run again if additional mutations occur. Default: Never + string reinvocationPolicy = 3; + + // Instructs Istio to not inject the sidecar on those pods, based on labels that are present in those pods. + // + // Annotations in the pods have higher precedence than the label selectors. + // Order of evaluation: Pod Annotations → NeverInjectSelector → AlwaysInjectSelector → Default Policy. + // See https://istio.io/docs/setup/kubernetes/additional-setup/sidecar-injection/#more-control-adding-exceptions + repeated k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector neverInjectSelector = 11; + + // See NeverInjectSelector. + repeated k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector alwaysInjectSelector = 12; + + // If true, webhook or istioctl injector will rewrite PodSpec for liveness health check to redirect request to sidecar. This makes liveness check work even when mTLS is enabled. + google.protobuf.BoolValue rewriteAppHTTPProbe = 16; + + // injectedAnnotations are additional annotations that will be added to the pod spec after injection + // This is primarily to support PSP annotations. + google.protobuf.Struct injectedAnnotations = 19; + + // Configure the injection url for sidecar injector webhook + string injectionURL = 22; + + // Templates defines a set of custom injection templates that can be used. For example, defining: + // + // templates: + // hello: | + // metadata: + // labels: + // hello: world + // + // Then starting a pod with the `inject.istio.io/templates: hello` annotation, will result in the pod + // being injected with the hello=world labels. + // This is intended for advanced configuration only; most users should use the built in template + google.protobuf.Struct templates = 23; + + // Default templates specifies a set of default templates that are used in sidecar injection. + // By default, a template `sidecar` is always provided, which contains the template of default sidecar. + // To inject other additional templates, define it using the `templates` option, and add it to + // the default templates list. + // For example: + + // templates: + // hello: | + // metadata: + // labels: + // hello: world + + // defaultTemplates: ["sidecar", "hello"] + repeated string defaultTemplates = 24; +} + +// Configuration for each of the supported tracers. +message TracerConfig { + // Configuration for the datadog tracing service. + TracerDatadogConfig datadog = 1; + + // Configuration for the lightstep tracing service. + TracerLightStepConfig lightstep = 2; + + // Configuration for the zipkin tracing service. + TracerZipkinConfig zipkin = 3; + + // Configuration for the stackdriver tracing service. + TracerStackdriverConfig stackdriver = 4; +} + +// Configuration for the datadog tracing service. +message TracerDatadogConfig { + // Address in host:port format for reporting trace data to the Datadog agent. + string address = 1; +} + +// Configuration for the lightstep tracing service. +message TracerLightStepConfig { + // Sets the lightstep satellite pool address in host:port format for reporting trace data. + string address = 1; + + // Sets the lightstep access token. + string accessToken = 2; +} + +// Configuration for the zipkin tracing service. +message TracerZipkinConfig { + // Address of zipkin instance in host:port format for reporting trace data. + // + // Example: .:941 + string address = 1; +} + +// Configuration for the stackdriver tracing service. +message TracerStackdriverConfig { + // enables trace output to stdout. + google.protobuf.BoolValue debug = 1; + + // The global default max number of attributes per span. + uint32 maxNumberOfAttributes = 2; + + // The global default max number of annotation events per span. + uint32 maxNumberOfAnnotations = 3; + + // The global default max number of message events per span. + uint32 maxNumberOfMessageEvents = 4; +} + +message BaseConfig { + // For Helm2 use, adds the CRDs to templates. + google.protobuf.BoolValue enableCRDTemplates = 1; + + // URL to use for validating webhook. + string validationURL = 2; + + // For istioctl usage to disable istio config crds in base + google.protobuf.BoolValue enableIstioConfigCRDs = 3; + + google.protobuf.BoolValue validateGateway = 4; + + // validation webhook CA bundle + string validationCABundle = 5; +} + +message IstiodRemoteConfig { + // URL to use for sidecar injector webhook. + string injectionURL = 1; + // Path to use for the sidecar injector webhook service. + string injectionPath = 2; + // injector ca bundle + string injectionCABundle = 3; + +} + +message Values { + // Configuration for the Istio CNI plugin. + CNIConfig cni = 2; + + // Configuration for ingress and egress gateways. + GatewaysConfig gateways = 5; + + // Global configuration for Istio components. + GlobalConfig global = 6; + + // Configuration for the Pilot component. + PilotConfig pilot = 10; + + // Configuration for the ZTunnel component. + google.protobuf.Value ztunnel = 41; + + // Controls whether telemetry is exported for Pilot. + TelemetryConfig telemetry = 23; + + // Configuration for the sidecar injector webhook. + SidecarInjectorConfig sidecarInjectorWebhook = 13; + + // Configuration for the Istio CNI plugin. + CNIUsageConfig istio_cni = 19 [deprecated = true]; + + // Identifies the revision this installation is associated with. + string revision = 21; + + // Used internally to identify the owner of each resource. + string ownerName = 22; + + // Defines runtime configuration of components, including Istiod and istio-agent behavior. + // See https://istio.io/docs/reference/config/istio.mesh.v1alpha1/ for all available options. + // TODO can this import the real mesh config API? + google.protobuf.Value meshConfig = 36; + + // Configuration for the base component. + BaseConfig base = 37; + + // Configuration for istiod-remote. + IstiodRemoteConfig istiodRemote = 38; + + // Specifies the aliases for the Istio control plane revision. A MutatingWebhookConfiguration + // is created for each alias. + repeated string revisionTags = 39; + + // The name of the default revision in the cluster. + string defaultRevision = 40; + + // Specifies which installation configuration profile to apply. + string profile = 42; + + // Specifies the compatibility version to use. When this is set, the control plane will + // be configured with the same defaults as the specified version. + string compatibilityVersion = 43; + + // Specifies experimental helm fields that could be removed or changed in the future + ExperimentalConfig experimental = 44; +} + +// ZeroVPNConfig enables cross-cluster access using SNI matching. +message ZeroVPNConfig { + // Controls whether ZeroVPN is enabled. + google.protobuf.BoolValue enabled = 1; + + string suffix = 2; +} + +// ExperimentalConfig is a placeholder for experimental installation features. +message ExperimentalConfig { + // Controls whether the experimental feature is enabled. + google.protobuf.BoolValue stableValidationPolicy = 1; +} + +// IntOrString is a type that can hold an int32 or a string. When used in +// JSON or YAML marshalling and unmarshalling, it produces or consumes the +// inner type. This allows you to have, for example, a JSON field that can +// accept a name or number. +// TODO: Rename to Int32OrString +// +// +protobuf=true +// +protobuf.options.(gogoproto.goproto_stringer)=false +// +k8s:openapi-gen=true +message IntOrString { + int64 type = 1; + + google.protobuf.Int32Value intVal = 2; + + google.protobuf.StringValue strVal = 3; +} diff --git a/operator/pkg/apis/istio/v1alpha1/zz_generated.deepcopy.go b/operator/pkg/apis/istio/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000..477b9aa --- /dev/null +++ b/operator/pkg/apis/istio/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,89 @@ +// Copyright Istio 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. + +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IstioOperator) DeepCopyInto(out *IstioOperator) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Spec != nil { + in.Spec.DeepCopyInto(out.Spec) + } + if in.Status != nil { + in.Status.DeepCopyInto(out.Status) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioOperator. +func (in *IstioOperator) DeepCopy() *IstioOperator { + if in == nil { + return nil + } + out := new(IstioOperator) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IstioOperator) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IstioOperatorList) DeepCopyInto(out *IstioOperatorList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]IstioOperator, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioOperatorList. +func (in *IstioOperatorList) DeepCopy() *IstioOperatorList { + if in == nil { + return nil + } + out := new(IstioOperatorList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IstioOperatorList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/operator/pkg/component/component.go b/operator/pkg/component/component.go new file mode 100644 index 0000000..804e397 --- /dev/null +++ b/operator/pkg/component/component.go @@ -0,0 +1,383 @@ +// Copyright Istio 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 component defines an in-memory representation of IstioOperator... It provides functions +for manipulating the component and rendering a manifest from it. +See ../README.md for an architecture overview. +*/ +package component + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/version" + "sigs.k8s.io/yaml" + + "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/helm" + "github.com/jehawley/istio/operator/pkg/metrics" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/patch" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/translate" + "istio.io/istio/pkg/log" + "istio.io/istio/pkg/util/sets" +) + +const ( + // String to emit for any component which is disabled. + componentDisabledStr = "component is disabled." + yamlCommentStr = "#" +) + +var scope = log.RegisterScope("installer", "installer") + +// Options defines options for a component. +type Options struct { + // installSpec is the global IstioOperatorSpec. + InstallSpec *v1alpha1.IstioOperatorSpec + // translator is the translator for this component. + Translator *translate.Translator + // Namespace is the namespace for this component. + Namespace string + // Filter is the filenames to render + Filter sets.String + // Version is the Kubernetes version information. + Version *version.Info +} + +// IstioComponent defines the interface for a component. +type IstioComponent interface { + // ComponentName returns the name of the component. + ComponentName() name.ComponentName + // ResourceName returns the name of the resources of the component. + ResourceName() string + // Namespace returns the namespace for the component. + Namespace() string + // Enabled reports whether the component is enabled. + Enabled() bool + // Run starts the component. Must be called before the component is used. + Run() error + // RenderManifest returns a string with the rendered manifest for the component. + RenderManifest() (string, error) +} + +// CommonComponentFields is a struct common to all components. +type CommonComponentFields struct { + *Options + ComponentName name.ComponentName + // resourceName is the name of all resources for this component. + ResourceName string + // index is the index of the component (only used for components with multiple instances like gateways). + index int + // componentSpec for the actual component e.g. GatewaySpec, ComponentSpec. + componentSpec any + // started reports whether the component is in initialized and running. + started bool + renderer helm.TemplateRenderer +} + +type IstioComponentBase struct { + *CommonComponentFields +} + +func (c *IstioComponentBase) ComponentName() name.ComponentName { + return c.CommonComponentFields.ComponentName +} + +func (c *IstioComponentBase) ResourceName() string { + return c.CommonComponentFields.ResourceName +} + +func (c *IstioComponentBase) Namespace() string { + return c.CommonComponentFields.Namespace +} + +func (c *IstioComponentBase) Enabled() bool { + if c.CommonComponentFields.ComponentName.IsGateway() { + // type assert is guaranteed to work in this context. + return c.componentSpec.(*v1alpha1.GatewaySpec).Enabled.GetValue() + } + return isCoreComponentEnabled(c.CommonComponentFields) +} + +func (c *IstioComponentBase) Run() error { + return runComponent(c.CommonComponentFields) +} + +func (c *IstioComponentBase) RenderManifest() (string, error) { + return renderManifest(c) +} + +// NewCoreComponent creates a new IstioComponent with the given componentName and options. +func NewCoreComponent(cn name.ComponentName, opts *Options) IstioComponent { + var component IstioComponent + switch cn { + case name.IstioBaseComponentName: + component = NewCRDComponent(opts) + case name.PilotComponentName: + component = NewPilotComponent(opts) + case name.CNIComponentName: + component = NewCNIComponent(opts) + case name.IstiodRemoteComponentName: + component = NewIstiodRemoteComponent(opts) + case name.ZtunnelComponentName: + component = NewZtunnelComponent(opts) + default: + scope.Errorf("Unknown component componentName: " + string(cn)) + } + return component +} + +// BaseComponent is the base component. +type BaseComponent struct { + *IstioComponentBase +} + +// NewCRDComponent creates a new BaseComponent and returns a pointer to it. +func NewCRDComponent(opts *Options) *BaseComponent { + return &BaseComponent{ + &IstioComponentBase{ + &CommonComponentFields{ + Options: opts, + ComponentName: name.IstioBaseComponentName, + }, + }, + } +} + +// PilotComponent is the pilot component. +type PilotComponent struct { + *IstioComponentBase +} + +// NewPilotComponent creates a new PilotComponent and returns a pointer to it. +func NewPilotComponent(opts *Options) *PilotComponent { + cn := name.PilotComponentName + return &PilotComponent{ + &IstioComponentBase{ + &CommonComponentFields{ + Options: opts, + ComponentName: cn, + ResourceName: opts.Translator.ComponentMaps[cn].ResourceName, + }, + }, + } +} + +type CNIComponent struct { + *IstioComponentBase +} + +// NewCNIComponent creates a new NewCNIComponent and returns a pointer to it. +func NewCNIComponent(opts *Options) *CNIComponent { + cn := name.CNIComponentName + return &CNIComponent{ + &IstioComponentBase{ + &CommonComponentFields{ + Options: opts, + ComponentName: cn, + }, + }, + } +} + +// IstiodRemoteComponent is the istiod remote component. +type IstiodRemoteComponent struct { + *IstioComponentBase +} + +// NewIstiodRemoteComponent creates a new NewIstiodRemoteComponent and returns a pointer to it. +func NewIstiodRemoteComponent(opts *Options) *IstiodRemoteComponent { + cn := name.IstiodRemoteComponentName + return &IstiodRemoteComponent{ + &IstioComponentBase{ + &CommonComponentFields{ + Options: opts, + ComponentName: cn, + }, + }, + } +} + +// IngressComponent is the ingress gateway component. +type IngressComponent struct { + *IstioComponentBase +} + +// NewIngressComponent creates a new IngressComponent and returns a pointer to it. +func NewIngressComponent(resourceName string, index int, spec *v1alpha1.GatewaySpec, opts *Options) *IngressComponent { + cn := name.IngressComponentName + return &IngressComponent{ + &IstioComponentBase{ + CommonComponentFields: &CommonComponentFields{ + Options: opts, + ComponentName: cn, + ResourceName: resourceName, + index: index, + componentSpec: spec, + }, + }, + } +} + +// EgressComponent is the egress gateway component. +type EgressComponent struct { + *IstioComponentBase +} + +// NewEgressComponent creates a new IngressComponent and returns a pointer to it. +func NewEgressComponent(resourceName string, index int, spec *v1alpha1.GatewaySpec, opts *Options) *EgressComponent { + cn := name.EgressComponentName + return &EgressComponent{ + &IstioComponentBase{ + CommonComponentFields: &CommonComponentFields{ + Options: opts, + ComponentName: cn, + index: index, + componentSpec: spec, + ResourceName: resourceName, + }, + }, + } +} + +// ZtunnelComponent is the istio ztunnel component. +type ZtunnelComponent struct { + *IstioComponentBase +} + +// NewZtunnelComponent creates a new ZtunnelComponent and returns a pointer to it. +func NewZtunnelComponent(opts *Options) *ZtunnelComponent { + return &ZtunnelComponent{ + &IstioComponentBase{ + &CommonComponentFields{ + Options: opts, + ComponentName: name.ZtunnelComponentName, + }, + }, + } +} + +// runComponent performs startup tasks for the component defined by the given CommonComponentFields. +func runComponent(c *CommonComponentFields) error { + r := createHelmRenderer(c) + if err := r.Run(); err != nil { + return err + } + c.renderer = r + c.started = true + return nil +} + +// renderManifest renders the manifest for the component defined by c and returns the resulting string. +func renderManifest(cf *IstioComponentBase) (string, error) { + if !cf.started { + metrics.CountManifestRenderError(cf.ComponentName(), metrics.RenderNotStartedError) + return "", fmt.Errorf("component %s not started in RenderManifest", cf.CommonComponentFields.ComponentName) + } + + if !cf.Enabled() { + return disabledYAMLStr(cf.ComponentName(), cf.CommonComponentFields.ResourceName), nil + } + + mergedYAML, err := cf.Translator.TranslateHelmValues(cf.InstallSpec, cf.componentSpec, cf.ComponentName()) + if err != nil { + metrics.CountManifestRenderError(cf.ComponentName(), metrics.HelmTranslateIOPToValuesError) + return "", err + } + + scope.Debugf("Merged values:\n%s\n", mergedYAML) + + my, err := cf.renderer.RenderManifestFiltered(mergedYAML, func(s string) bool { + return cf.Filter.IsEmpty() || cf.Filter.Contains(s) + }) + if err != nil { + log.Errorf("Error rendering the manifest: %s", err) + metrics.CountManifestRenderError(cf.ComponentName(), metrics.HelmChartRenderError) + return "", err + } + my += helm.YAMLSeparator + "\n" + scope.Debugf("Initial manifest with merged values:\n%s\n", my) + + // Add the k8s resources from IstioOperatorSpec. + my, err = cf.Translator.OverlayK8sSettings(my, cf.InstallSpec, cf.CommonComponentFields.ComponentName, + cf.CommonComponentFields.ResourceName, cf.index) + if err != nil { + metrics.CountManifestRenderError(cf.ComponentName(), metrics.K8SSettingsOverlayError) + return "", err + } + cnOutput := string(cf.CommonComponentFields.ComponentName) + my = "# Resources for " + cnOutput + " component\n\n" + my + scope.Debugf("Manifest after k8s API settings:\n%s\n", my) + + // Add the k8s resource overlays from IstioOperatorSpec. + pathToK8sOverlay := fmt.Sprintf("Components.%s.", cf.CommonComponentFields.ComponentName) + if cf.CommonComponentFields.ComponentName.IsGateway() { + pathToK8sOverlay += fmt.Sprintf("%d.", cf.index) + } + + pathToK8sOverlay += "K8S.Overlays" + var overlays []*v1alpha1.K8SObjectOverlay + found, err := tpath.SetFromPath(cf.InstallSpec, pathToK8sOverlay, &overlays) + if err != nil { + return "", err + } + if !found { + scope.Debugf("Manifest after resources: \n%s\n", my) + metrics.CountManifestRender(cf.ComponentName()) + return my, nil + } + kyo, err := yaml.Marshal(overlays) + if err != nil { + return "", err + } + scope.Infof("Applying Kubernetes overlay: \n%s\n", kyo) + ret, err := patch.YAMLManifestPatch(my, cf.Namespace(), overlays) + if err != nil { + metrics.CountManifestRenderError(cf.ComponentName(), metrics.K8SManifestPatchError) + return "", err + } + + scope.Debugf("Manifest after resources and overlay: \n%s\n", ret) + metrics.CountManifestRender(cf.ComponentName()) + return ret, nil +} + +// createHelmRenderer creates a helm renderer for the component defined by c and returns a ptr to it. +// If a helm subdir is not found in ComponentMap translations, it is assumed to be "addon/". +func createHelmRenderer(c *CommonComponentFields) helm.TemplateRenderer { + iop := c.InstallSpec + cns := string(c.ComponentName) + helmSubdir := c.Translator.ComponentMap(cns).HelmSubdir + return helm.NewHelmRenderer(iop.InstallPackagePath, helmSubdir, cns, c.Namespace, c.Version) +} + +func isCoreComponentEnabled(c *CommonComponentFields) bool { + enabled, err := c.Translator.IsComponentEnabled(c.ComponentName, c.InstallSpec) + if err != nil { + return false + } + return enabled +} + +// disabledYAMLStr returns the YAML comment string that the given component is disabled. +func disabledYAMLStr(componentName name.ComponentName, resourceName string) string { + fullName := string(componentName) + if resourceName != "" { + fullName += " " + resourceName + } + return fmt.Sprintf("%s %s %s\n", yamlCommentStr, fullName, componentDisabledStr) +} diff --git a/operator/pkg/controlplane/control_plane.go b/operator/pkg/controlplane/control_plane.go new file mode 100644 index 0000000..378a725 --- /dev/null +++ b/operator/pkg/controlplane/control_plane.go @@ -0,0 +1,146 @@ +// Copyright Istio 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 controlplane + +import ( + "fmt" + "sort" + + "k8s.io/apimachinery/pkg/version" + + "istio.io/api/123/operator/v1alpha1" + iop "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/component" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/translate" + "github.com/jehawley/istio/operator/pkg/util" + "istio.io/istio/pkg/util/sets" +) + +// IstioControlPlane is an installation of an Istio control plane. +type IstioControlPlane struct { + // components is a slice of components that are part of the feature. + components []component.IstioComponent + started bool +} + +// NewIstioControlPlane creates a new IstioControlPlane and returns a pointer to it. +func NewIstioControlPlane( + installSpec *v1alpha1.IstioOperatorSpec, + translator *translate.Translator, + filter []string, + ver *version.Info, +) (*IstioControlPlane, error) { + out := &IstioControlPlane{} + opts := &component.Options{ + InstallSpec: installSpec, + Translator: translator, + Filter: sets.New(filter...), + Version: ver, + } + for _, c := range name.AllCoreComponentNames { + o := *opts + ns, err := name.Namespace(c, installSpec) + if err != nil { + return nil, err + } + o.Namespace = ns + out.components = append(out.components, component.NewCoreComponent(c, &o)) + } + + if installSpec.Components != nil { + for idx, c := range installSpec.Components.IngressGateways { + o := *opts + o.Namespace = defaultIfEmpty(c.Namespace, iop.Namespace(installSpec)) + out.components = append(out.components, component.NewIngressComponent(c.Name, idx, c, &o)) + } + for idx, c := range installSpec.Components.EgressGateways { + o := *opts + o.Namespace = defaultIfEmpty(c.Namespace, iop.Namespace(installSpec)) + out.components = append(out.components, component.NewEgressComponent(c.Name, idx, c, &o)) + } + } + return out, nil +} + +func orderedKeys(m map[string]*v1alpha1.ExternalComponentSpec) []string { + var keys []string + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} + +func defaultIfEmpty(val, dflt string) string { + if val == "" { + return dflt + } + return val +} + +// Run starts the Istio control plane. +func (i *IstioControlPlane) Run() error { + for _, c := range i.components { + if err := c.Run(); err != nil { + return err + } + } + i.started = true + return nil +} + +// RenderManifest returns a manifest rendered against +func (i *IstioControlPlane) RenderManifest() (manifests name.ManifestMap, errsOut util.Errors) { + if !i.started { + return nil, util.NewErrs(fmt.Errorf("istioControlPlane must be Run before calling RenderManifest")) + } + + manifests = make(name.ManifestMap) + for _, c := range i.components { + ms, err := c.RenderManifest() + errsOut = util.AppendErr(errsOut, err) + manifests[c.ComponentName()] = append(manifests[c.ComponentName()], ms) + } + if len(errsOut) > 0 { + return nil, errsOut + } + return +} + +// componentsEqual reports whether the given components are equal to those in i. +func (i *IstioControlPlane) componentsEqual(components []component.IstioComponent) bool { + if i.components == nil && components == nil { + return true + } + if len(i.components) != len(components) { + return false + } + for c := 0; c < len(i.components); c++ { + if i.components[c].ComponentName() != components[c].ComponentName() { + return false + } + if i.components[c].Namespace() != components[c].Namespace() { + return false + } + if i.components[c].Enabled() != components[c].Enabled() { + return false + } + if i.components[c].ResourceName() != components[c].ResourceName() { + return false + } + } + return true +} diff --git a/operator/pkg/controlplane/control_plane_test.go b/operator/pkg/controlplane/control_plane_test.go new file mode 100644 index 0000000..cf3bc51 --- /dev/null +++ b/operator/pkg/controlplane/control_plane_test.go @@ -0,0 +1,249 @@ +// Copyright Istio 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 controlplane + +import ( + "fmt" + "reflect" + "testing" + + "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/component" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/translate" + "github.com/jehawley/istio/operator/pkg/util" +) + +func TestOrderedKeys(t *testing.T) { + tests := []struct { + desc string + in map[string]*v1alpha1.ExternalComponentSpec + want []string + }{ + { + desc: "not-ordered", + in: map[string]*v1alpha1.ExternalComponentSpec{ + "graphql": nil, + "Abacus": nil, + "Astrology": nil, + "gRPC": nil, + "blackjack": nil, + }, + want: []string{ + "Abacus", + "Astrology", + "blackjack", + "gRPC", + "graphql", + }, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := orderedKeys(tt.in); !(reflect.DeepEqual(got, tt.want)) { + t.Errorf("%s: got %+v want %+v", tt.desc, got, tt.want) + } + }) + } +} + +func TestNewIstioOperator(t *testing.T) { + coreComponentOptions := &component.Options{ + InstallSpec: &v1alpha1.IstioOperatorSpec{}, + Translator: &translate.Translator{}, + } + tests := []struct { + desc string + inInstallSpec *v1alpha1.IstioOperatorSpec + inTranslator *translate.Translator + wantIstioOperator *IstioControlPlane + wantErr error + }{ + { + desc: "core-components", + inInstallSpec: &v1alpha1.IstioOperatorSpec{}, + inTranslator: &translate.Translator{ + ComponentMaps: map[name.ComponentName]*translate.ComponentMaps{ + "Pilot": { + ResourceName: "test-resource", + }, + }, + }, + wantErr: nil, + wantIstioOperator: &IstioControlPlane{ + components: []component.IstioComponent{ + &component.BaseComponent{ + IstioComponentBase: &component.IstioComponentBase{ + CommonComponentFields: &component.CommonComponentFields{ + Options: coreComponentOptions, + ComponentName: name.IstioBaseComponentName, + }, + }, + }, + &component.PilotComponent{ + IstioComponentBase: &component.IstioComponentBase{ + CommonComponentFields: &component.CommonComponentFields{ + Options: coreComponentOptions, + ResourceName: "test-resource", + ComponentName: name.PilotComponentName, + }, + }, + }, + &component.CNIComponent{ + IstioComponentBase: &component.IstioComponentBase{ + CommonComponentFields: &component.CommonComponentFields{ + ComponentName: name.CNIComponentName, + Options: coreComponentOptions, + }, + }, + }, + &component.IstiodRemoteComponent{ + IstioComponentBase: &component.IstioComponentBase{ + CommonComponentFields: &component.CommonComponentFields{ + ComponentName: name.IstiodRemoteComponentName, + Options: coreComponentOptions, + }, + }, + }, + &component.ZtunnelComponent{ + IstioComponentBase: &component.IstioComponentBase{ + CommonComponentFields: &component.CommonComponentFields{ + ComponentName: name.ZtunnelComponentName, + Options: coreComponentOptions, + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + gotOperator, err := NewIstioControlPlane(tt.inInstallSpec, tt.inTranslator, nil, nil) + if ((err != nil && tt.wantErr == nil) || (err == nil && tt.wantErr != nil)) || !gotOperator.componentsEqual(tt.wantIstioOperator.components) { + t.Errorf("%s: wanted components & err %+v %v, got components & err %+v %v", + tt.desc, tt.wantIstioOperator.components, tt.wantErr, gotOperator.components, err) + } + }) + } +} + +func TestIstioOperator_RenderManifest(t *testing.T) { + coreComponentOptions := &component.Options{ + InstallSpec: &v1alpha1.IstioOperatorSpec{}, + Translator: &translate.Translator{}, + } + tests := []struct { + desc string + testOperator *IstioControlPlane + wantManifests name.ManifestMap + wantErrs util.Errors + }{ + { + desc: "components-not-started-operator-started", + testOperator: &IstioControlPlane{ + components: []component.IstioComponent{ + &component.BaseComponent{ + IstioComponentBase: &component.IstioComponentBase{ + CommonComponentFields: &component.CommonComponentFields{ + Options: coreComponentOptions, + ComponentName: name.IstioBaseComponentName, + }, + }, + }, + &component.PilotComponent{ + IstioComponentBase: &component.IstioComponentBase{ + CommonComponentFields: &component.CommonComponentFields{ + Options: &component.Options{ + InstallSpec: &v1alpha1.IstioOperatorSpec{}, + Translator: &translate.Translator{}, + }, + ResourceName: "test-resource", + ComponentName: name.PilotComponentName, + }, + }, + }, + &component.CNIComponent{ + IstioComponentBase: &component.IstioComponentBase{ + CommonComponentFields: &component.CommonComponentFields{ + ComponentName: name.CNIComponentName, + Options: coreComponentOptions, + }, + }, + }, + }, + started: true, + }, + wantManifests: map[name.ComponentName][]string{}, + wantErrs: util.Errors{ + fmt.Errorf("component Base not started in RenderManifest"), + fmt.Errorf("component Pilot not started in RenderManifest"), + fmt.Errorf("component Cni not started in RenderManifest"), + }, + }, + { + desc: "operator-not-started", + testOperator: &IstioControlPlane{ + components: []component.IstioComponent{ + &component.BaseComponent{ + IstioComponentBase: &component.IstioComponentBase{ + CommonComponentFields: &component.CommonComponentFields{ + Options: coreComponentOptions, + ComponentName: name.IstioBaseComponentName, + }, + }, + }, + &component.PilotComponent{ + IstioComponentBase: &component.IstioComponentBase{ + CommonComponentFields: &component.CommonComponentFields{ + Options: &component.Options{ + InstallSpec: &v1alpha1.IstioOperatorSpec{}, + Translator: &translate.Translator{}, + }, + ResourceName: "test-resource", + ComponentName: name.PilotComponentName, + }, + }, + }, + &component.CNIComponent{ + IstioComponentBase: &component.IstioComponentBase{ + CommonComponentFields: &component.CommonComponentFields{ + ComponentName: name.CNIComponentName, + Options: coreComponentOptions, + }, + }, + }, + }, + started: false, + }, + wantManifests: map[name.ComponentName][]string{}, + wantErrs: util.Errors{ + fmt.Errorf("istioControlPlane must be Run before calling RenderManifest"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + gotManifests, gotErrs := tt.testOperator.RenderManifest() + if !reflect.DeepEqual(gotManifests, tt.wantManifests) || !util.EqualErrors(gotErrs, tt.wantErrs) { + // reflect.DeepEqual returns false on size 0 maps + if !(len(gotManifests) == 0) && (len(tt.wantManifests) == 0) { + t.Errorf("%s: expected manifest map %+v errs %+v, got manifest map %+v errs %+v", + tt.desc, tt.wantManifests, tt.wantErrs, gotManifests, gotErrs) + } + } + }) + } +} diff --git a/operator/pkg/helm/fs_renderer_test.go b/operator/pkg/helm/fs_renderer_test.go new file mode 100644 index 0000000..6f06dd0 --- /dev/null +++ b/operator/pkg/helm/fs_renderer_test.go @@ -0,0 +1,103 @@ +// Copyright Istio 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 helm + +import ( + "errors" + "os" + "testing" + + "helm.sh/helm/v3/pkg/chart" +) + +func TestRenderManifest(t *testing.T) { + tests := []struct { + desc string + inValues string + inChart chart.Chart + startRender bool + inPath string + objFileTemplateReader Renderer + wantResult string + wantErr error + }{ + { + desc: "not-started", + inValues: "", + startRender: false, + inChart: chart.Chart{}, + objFileTemplateReader: Renderer{}, + wantResult: "", + wantErr: errors.New("fileTemplateRenderer for not started in renderChart"), + }, + { + desc: "started-random-template", + inValues: ` +description: test +`, + inPath: "testdata/render/Chart.yaml", + startRender: true, + objFileTemplateReader: Renderer{ + namespace: "name-space", + componentName: "foo-component", + dir: "testdata/render", + files: os.DirFS("."), + }, + wantResult: `apiVersion: v1 +description: test +name: addon +version: 1.1.0 +appVersion: 1.1.0 +tillerVersion: ">=2.7.2" +keywords: + - istio-addon + +--- +`, + wantErr: nil, + }, + { + desc: "bad-file-path", + inValues: "", + inPath: "foo/bar/Chart.yaml", + startRender: true, + objFileTemplateReader: Renderer{ + namespace: "name-space", + componentName: "foo-component", + dir: "foo/bar", + files: os.DirFS("."), + }, + wantResult: "", + wantErr: errors.New(`component "foo-component" does not exist`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if tt.startRender { + err := tt.objFileTemplateReader.Run() + if err != nil && tt.wantErr != nil { + if err.Error() != tt.wantErr.Error() { + t.Errorf("%s: expected err: %q got %q", tt.desc, tt.wantErr.Error(), err.Error()) + } + } + } + if res, err := tt.objFileTemplateReader.RenderManifest(tt.inValues); res != tt.wantResult || + ((tt.wantErr != nil && err == nil) || (tt.wantErr == nil && err != nil)) { + t.Errorf("%s: \nexpected vals: \n%v\n\nexpected err:%v\ngot vals:\n%v\n\n got err %v", + tt.desc, tt.wantResult, tt.wantErr, res, err) + } + }) + } +} diff --git a/operator/pkg/helm/helm.go b/operator/pkg/helm/helm.go new file mode 100644 index 0000000..b877689 --- /dev/null +++ b/operator/pkg/helm/helm.go @@ -0,0 +1,259 @@ +// Copyright Istio 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 helm + +import ( + "fmt" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/engine" + "k8s.io/apimachinery/pkg/version" + "sigs.k8s.io/yaml" + + "istio.io/istio/istioctl/pkg/install/k8sversion" + "istio.io/istio/manifests" + "github.com/jehawley/istio/operator/pkg/util" + "istio.io/istio/pkg/log" +) + +const ( + // YAMLSeparator is a separator for multi-document YAML files. + YAMLSeparator = "\n---\n" + + // DefaultProfileString is the name of the default profile. + DefaultProfileString = "default" + + // NotesFileNameSuffix is the file name suffix for helm notes. + // see https://helm.sh/docs/chart_template_guide/notes_files/ + NotesFileNameSuffix = ".txt" +) + +var scope = log.RegisterScope("installer", "installer") + +// TemplateFilterFunc filters templates to render by their file name +type TemplateFilterFunc func(string) bool + +// TemplateRenderer defines a helm template renderer interface. +type TemplateRenderer interface { + // Run starts the renderer and should be called before using it. + Run() error + // RenderManifest renders the associated helm charts with the given values YAML string and returns the resulting + // string. + RenderManifest(values string) (string, error) + // RenderManifestFiltered filters manifests to render by template file name + RenderManifestFiltered(values string, filter TemplateFilterFunc) (string, error) +} + +// NewHelmRenderer creates a new helm renderer with the given parameters and returns an interface to it. +// The format of helmBaseDir and profile strings determines the type of helm renderer returned (compiled-in, file, +// HTTP etc.) +func NewHelmRenderer(operatorDataDir, helmSubdir, componentName, namespace string, version *version.Info) TemplateRenderer { + dir := strings.Join([]string{ChartsSubdirName, helmSubdir}, "/") + return NewGenericRenderer(manifests.BuiltinOrDir(operatorDataDir), dir, componentName, namespace, version) +} + +// ReadProfileYAML reads the YAML values associated with the given profile. It uses an appropriate reader for the +// profile format (compiled-in, file, HTTP, etc.). +func ReadProfileYAML(profile, manifestsPath string) (string, error) { + var err error + var globalValues string + + // Get global values from profile. + switch { + case util.IsFilePath(profile): + if globalValues, err = readFile(profile); err != nil { + return "", err + } + default: + if globalValues, err = LoadValues(profile, manifestsPath); err != nil { + return "", fmt.Errorf("failed to read profile %v from %v: %v", profile, manifestsPath, err) + } + } + + return globalValues, nil +} + +// renderChart renders the given chart with the given values and returns the resulting YAML manifest string. +func renderChart(namespace, values string, chrt *chart.Chart, filterFunc TemplateFilterFunc, version *version.Info) (string, error) { + options := chartutil.ReleaseOptions{ + Name: "istio", + Namespace: namespace, + } + valuesMap := map[string]any{} + if err := yaml.Unmarshal([]byte(values), &valuesMap); err != nil { + return "", fmt.Errorf("failed to unmarshal values: %v", err) + } + + caps := *chartutil.DefaultCapabilities + + // overwrite helm default capabilities + operatorVersion, _ := chartutil.ParseKubeVersion("1." + strconv.Itoa(k8sversion.MinK8SVersion) + ".0") + caps.KubeVersion = *operatorVersion + + if version != nil { + caps.KubeVersion = chartutil.KubeVersion{ + Version: version.GitVersion, + Major: version.Major, + Minor: version.Minor, + } + } + vals, err := chartutil.ToRenderValues(chrt, valuesMap, options, &caps) + if err != nil { + return "", err + } + + if filterFunc != nil { + filteredTemplates := []*chart.File{} + for _, t := range chrt.Templates { + // Always include required templates that do not produce any output + if filterFunc(t.Name) || strings.HasSuffix(t.Name, ".tpl") || t.Name == "templates/zzz_profile.yaml" { + filteredTemplates = append(filteredTemplates, t) + } + } + chrt.Templates = filteredTemplates + } + + files, err := engine.Render(chrt, vals) + crdFiles := chrt.CRDObjects() + if err != nil { + return "", err + } + if chrt.Metadata.Name == "base" { + base, _ := valuesMap["base"].(map[string]any) + if enableIstioConfigCRDs, ok := base["enableIstioConfigCRDs"].(bool); ok && !enableIstioConfigCRDs { + crdFiles = []chart.CRD{} + } + } + + // Create sorted array of keys to iterate over, to stabilize the order of the rendered templates + keys := make([]string, 0, len(files)) + for k := range files { + if strings.HasSuffix(k, NotesFileNameSuffix) { + continue + } + keys = append(keys, k) + } + sort.Strings(keys) + + var sb strings.Builder + for i := 0; i < len(keys); i++ { + f := files[keys[i]] + // add yaml separator if the rendered file doesn't have one at the end + f = strings.TrimSpace(f) + "\n" + if !strings.HasSuffix(f, YAMLSeparator) { + f += YAMLSeparator + } + _, err := sb.WriteString(f) + if err != nil { + return "", err + } + } + + // Sort crd files by name to ensure stable manifest output + sort.Slice(crdFiles, func(i, j int) bool { return crdFiles[i].Name < crdFiles[j].Name }) + for _, crdFile := range crdFiles { + f := string(crdFile.File.Data) + // add yaml separator if the rendered file doesn't have one at the end + f = strings.TrimSpace(f) + "\n" + if !strings.HasSuffix(f, YAMLSeparator) { + f += YAMLSeparator + } + _, err := sb.WriteString(f) + if err != nil { + return "", err + } + } + + return sb.String(), nil +} + +// GenerateHubTagOverlay creates an IstioOperatorSpec overlay YAML for hub and tag. +func GenerateHubTagOverlay(hub, tag string) (string, error) { + hubTagYAMLTemplate := ` +spec: + hub: {{.Hub}} + tag: {{.Tag}} +` + ts := struct { + Hub string + Tag string + }{ + Hub: hub, + Tag: tag, + } + return util.RenderTemplate(hubTagYAMLTemplate, ts) +} + +// DefaultFilenameForProfile returns the profile name of the default profile for the given profile. +func DefaultFilenameForProfile(profile string) string { + switch { + case util.IsFilePath(profile): + return filepath.Join(filepath.Dir(profile), DefaultProfileFilename) + default: + return DefaultProfileString + } +} + +// IsDefaultProfile reports whether the given profile is the default profile. +func IsDefaultProfile(profile string) bool { + return profile == "" || profile == DefaultProfileString || filepath.Base(profile) == DefaultProfileFilename +} + +func readFile(path string) (string, error) { + b, err := os.ReadFile(path) + return string(b), err +} + +// GetProfileYAML returns the YAML for the given profile name, using the given profileOrPath string, which may be either +// a profile label or a file path. +func GetProfileYAML(installPackagePath, profileOrPath string) (string, error) { + if profileOrPath == "" { + profileOrPath = "default" + } + profiles, err := readProfiles(installPackagePath) + if err != nil { + return "", fmt.Errorf("failed to read profiles: %v", err) + } + // If charts are a file path and profile is a name like default, transform it to the file path. + if profiles[profileOrPath] && installPackagePath != "" { + profileOrPath = filepath.Join(installPackagePath, "profiles", profileOrPath+".yaml") + } + // This contains the IstioOperator CR. + baseCRYAML, err := ReadProfileYAML(profileOrPath, installPackagePath) + if err != nil { + return "", err + } + + if !IsDefaultProfile(profileOrPath) { + // Profile definitions are relative to the default profileOrPath, so read that first. + dfn := DefaultFilenameForProfile(profileOrPath) + defaultYAML, err := ReadProfileYAML(dfn, installPackagePath) + if err != nil { + return "", err + } + baseCRYAML, err = util.OverlayIOP(defaultYAML, baseCRYAML) + if err != nil { + return "", err + } + } + + return baseCRYAML, nil +} diff --git a/operator/pkg/helm/renderer.go b/operator/pkg/helm/renderer.go new file mode 100644 index 0000000..2e986c4 --- /dev/null +++ b/operator/pkg/helm/renderer.go @@ -0,0 +1,182 @@ +// Copyright Istio 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 helm + +import ( + "fmt" + "io/fs" + "os" + "path/filepath" + "strings" + + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" + "k8s.io/apimachinery/pkg/version" + + "istio.io/istio/manifests" + "github.com/jehawley/istio/operator/pkg/util" +) + +const ( + // DefaultProfileFilename is the name of the default profile yaml file. + DefaultProfileFilename = "default.yaml" + ChartsSubdirName = "charts" + profilesRoot = "profiles" +) + +// Renderer is a helm template renderer for a fs.FS. +type Renderer struct { + namespace string + componentName string + chart *chart.Chart + started bool + files fs.FS + dir string + // Kubernetes cluster version + version *version.Info +} + +// NewFileTemplateRenderer creates a TemplateRenderer with the given parameters and returns a pointer to it. +// helmChartDirPath must be an absolute file path to the root of the helm charts. +func NewGenericRenderer(files fs.FS, dir, componentName, namespace string, version *version.Info) *Renderer { + return &Renderer{ + namespace: namespace, + componentName: componentName, + dir: dir, + files: files, + version: version, + } +} + +// Run implements the TemplateRenderer interface. +func (h *Renderer) Run() error { + if err := h.loadChart(); err != nil { + return err + } + + h.started = true + return nil +} + +// RenderManifest renders the current helm templates with the current values and returns the resulting YAML manifest string. +func (h *Renderer) RenderManifest(values string) (string, error) { + if !h.started { + return "", fmt.Errorf("fileTemplateRenderer for %s not started in renderChart", h.componentName) + } + return renderChart(h.namespace, values, h.chart, nil, h.version) +} + +// RenderManifestFiltered filters templates to render using the supplied filter function. +func (h *Renderer) RenderManifestFiltered(values string, filter TemplateFilterFunc) (string, error) { + if !h.started { + return "", fmt.Errorf("fileTemplateRenderer for %s not started in renderChart", h.componentName) + } + return renderChart(h.namespace, values, h.chart, filter, h.version) +} + +func GetFilesRecursive(f fs.FS, root string) ([]string, error) { + res := []string{} + err := fs.WalkDir(f, root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + res = append(res, path) + return nil + }) + return res, err +} + +// loadChart implements the TemplateRenderer interface. +func (h *Renderer) loadChart() error { + fnames, err := GetFilesRecursive(h.files, h.dir) + if err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("component %q does not exist", h.componentName) + } + return fmt.Errorf("list files: %v", err) + } + var bfs []*loader.BufferedFile + for _, fname := range fnames { + b, err := fs.ReadFile(h.files, fname) + if err != nil { + return fmt.Errorf("read file: %v", err) + } + // Helm expects unix / separator, but on windows this will be \ + name := strings.ReplaceAll(stripPrefix(fname, h.dir), string(filepath.Separator), "/") + bf := &loader.BufferedFile{ + Name: name, + Data: b, + } + bfs = append(bfs, bf) + scope.Debugf("Chart loaded: %s", bf.Name) + } + + h.chart, err = loader.LoadFiles(bfs) + if err != nil { + return fmt.Errorf("load files: %v", err) + } + return nil +} + +func builtinProfileToFilename(name string) string { + if name == "" { + return DefaultProfileFilename + } + return name + ".yaml" +} + +func LoadValues(profileName string, chartsDir string) (string, error) { + path := strings.Join([]string{profilesRoot, builtinProfileToFilename(profileName)}, "/") + by, err := fs.ReadFile(manifests.BuiltinOrDir(chartsDir), path) + if err != nil { + return "", err + } + return string(by), nil +} + +func readProfiles(chartsDir string) (map[string]bool, error) { + profiles := map[string]bool{} + f := manifests.BuiltinOrDir(chartsDir) + dir, err := fs.ReadDir(f, profilesRoot) + if err != nil { + return nil, err + } + for _, f := range dir { + trimmedString := strings.TrimSuffix(f.Name(), ".yaml") + if f.Name() != trimmedString { + profiles[trimmedString] = true + } + } + return profiles, nil +} + +// stripPrefix removes the given prefix from prefix. +func stripPrefix(path, prefix string) string { + pl := len(strings.Split(prefix, "/")) + pv := strings.Split(path, "/") + return strings.Join(pv[pl:], "/") +} + +// list all the profiles. +func ListProfiles(charts string) ([]string, error) { + profiles, err := readProfiles(charts) + if err != nil { + return nil, err + } + return util.StringBoolMapToSlice(profiles), nil +} diff --git a/operator/pkg/helm/testdata/addons/a/Chart.yaml b/operator/pkg/helm/testdata/addons/a/Chart.yaml new file mode 100644 index 0000000..c03e3c6 --- /dev/null +++ b/operator/pkg/helm/testdata/addons/a/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +description: A Helm chart for Kubernetes +name: addon +version: 1.1.0 +appVersion: 1.1.0 +tillerVersion: ">=2.7.2" +keywords: + - istio-addon diff --git a/operator/pkg/helm/testdata/addons/invalid/a/Chart.yaml b/operator/pkg/helm/testdata/addons/invalid/a/Chart.yaml new file mode 100644 index 0000000..c03e3c6 --- /dev/null +++ b/operator/pkg/helm/testdata/addons/invalid/a/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +description: A Helm chart for Kubernetes +name: addon +version: 1.1.0 +appVersion: 1.1.0 +tillerVersion: ">=2.7.2" +keywords: + - istio-addon diff --git a/operator/pkg/helm/testdata/addons/invalid/b/Chart.yaml b/operator/pkg/helm/testdata/addons/invalid/b/Chart.yaml new file mode 100644 index 0000000..c03e3c6 --- /dev/null +++ b/operator/pkg/helm/testdata/addons/invalid/b/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +description: A Helm chart for Kubernetes +name: addon +version: 1.1.0 +appVersion: 1.1.0 +tillerVersion: ">=2.7.2" +keywords: + - istio-addon diff --git a/operator/pkg/helm/testdata/istio-1.3.0-linux.tar.gz b/operator/pkg/helm/testdata/istio-1.3.0-linux.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..e1ee4ff117f06bfda8b47037be9a6af0a466725b GIT binary patch literal 617 zcmV-v0+#(BiwFR~SQ1?T1MQg0j+-zPhO_1=PL)k7b*Qh98EIDSqPvbXRkxKVIK(P9 z8I#M~*N}EHaU0PpgwbUDZ9vB0gM9w;b5ONW<&`RGBeG2D4|ihoS&GD-eOwA{!mY@& z$%!C@V$NX_cff@3m=g-^_zsQ$Q;rzpj8G1U5JKq$Hm5H9FEsYewGBmyd@YOP^D3=! zp|`0no9b`ZcW(lZ#}@)-(5U1#;@s{^KqAPE%H=KQ5oR;W2oBNg`g#^TcbuN{pd9@V zr}{m=^-pLVM-=~|e@uv@e?%fa0W@3)$9d)YPh>sUsbiS?trosgyeM4#@BM!zvPRZJl!AW$$B+8QF>3vv#mM{r7&!hP zANoI?u@FauG3I4`nYj8NPW5~G{2xWk(LdrO^8P;tQ~1{Ic(>uOP?-d|5qV{g?Poyi z?y5+GDeT<8%MM=(E{MI~JC$I-HU7u{5!-$L+cM$(e*}8`AK{QP%4Xb){xWg>{~T-d z`aeR4`o|GvR{#7Q!MMC~{U7iD!lle|l@_IzC$7<3|8WFQbHVn1G>&}zAA`gC|J>1d z#>UnEaH`+aSN}=m>K`#0`~GhXrf?^Wk-DhCl(5@)STAJ(4~@za+x$T#=08L#>(I7= zUze(ex~ZyC8+&4tC4)38A0QWIzO=L7gO=?N=2.7.2ffff" +keywords: + - istio-addon diff --git a/operator/pkg/helm/testdata/render/templates/fs_template.yaml b/operator/pkg/helm/testdata/render/templates/fs_template.yaml new file mode 100644 index 0000000..bf79187 --- /dev/null +++ b/operator/pkg/helm/testdata/render/templates/fs_template.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +description: {{.Values.description}} +name: addon +version: 1.1.0 +appVersion: 1.1.0 +tillerVersion: ">=2.7.2" +keywords: + - istio-addon \ No newline at end of file diff --git a/operator/pkg/helm/vfs_renderer_test.go b/operator/pkg/helm/vfs_renderer_test.go new file mode 100644 index 0000000..810364d --- /dev/null +++ b/operator/pkg/helm/vfs_renderer_test.go @@ -0,0 +1,37 @@ +// Copyright Istio 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 helm + +import "testing" + +func TestStripPrefix(t *testing.T) { + cases := []struct { + in string + prefix string + out string + }{ + {"foo/bar", "foo", "bar"}, + {"a/b/c/d", "a/b/c", "d"}, + {"a/b/c/d", "a/b", "c/d"}, + } + for _, tt := range cases { + t.Run(tt.in+"~"+tt.prefix, func(t *testing.T) { + got := stripPrefix(tt.in, tt.prefix) + if got != tt.out { + t.Fatalf("Wanted %v got %v", tt.out, got) + } + }) + } +} diff --git a/operator/pkg/manifest/shared.go b/operator/pkg/manifest/shared.go new file mode 100644 index 0000000..3fa66c2 --- /dev/null +++ b/operator/pkg/manifest/shared.go @@ -0,0 +1,613 @@ +// Copyright Istio 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 manifest + +import ( + "fmt" + "io" + "os" + "reflect" + "strconv" + "strings" + + "k8s.io/apimachinery/pkg/version" + "sigs.k8s.io/yaml" + + "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/apis/istio" + iopv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1/validation" + "github.com/jehawley/istio/operator/pkg/controlplane" + "github.com/jehawley/istio/operator/pkg/helm" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/object" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/translate" + "github.com/jehawley/istio/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util/clog" + "github.com/jehawley/istio/operator/pkg/validate" + "istio.io/istio/pkg/kube" + "istio.io/istio/pkg/log" + "istio.io/istio/pkg/util/sets" + pkgversion "istio.io/istio/pkg/version" +) + +// installerScope is the scope for shared manifest package. +var installerScope = log.RegisterScope("installer", "installer") + +// GenManifests generates a manifest map, keyed by the component name, from input file list and a YAML tree +// representation of path-values passed through the --set flag. +// If force is set, validation errors will not cause processing to abort but will result in warnings going to the +// supplied logger. +func GenManifests(inFilename []string, setFlags []string, force bool, filter []string, + client kube.Client, l clog.Logger, +) (name.ManifestMap, *iopv1alpha1.IstioOperator, error) { + mergedYAML, _, err := GenerateConfig(inFilename, setFlags, force, client, l) + if err != nil { + return nil, nil, err + } + mergedIOPS, err := unmarshalAndValidateIOP(mergedYAML, force, false, l) + if err != nil { + return nil, nil, err + } + + t := translate.NewTranslator() + var ver *version.Info + if client != nil { + ver, err = client.GetKubernetesVersion() + if err != nil { + return nil, nil, err + } + } + cp, err := controlplane.NewIstioControlPlane(mergedIOPS.Spec, t, filter, ver) + if err != nil { + return nil, nil, err + } + if err := cp.Run(); err != nil { + return nil, nil, err + } + + manifests, errs := cp.RenderManifest() + if errs != nil { + return manifests, mergedIOPS, errs.ToError() + } + return manifests, mergedIOPS, nil +} + +// GenerateConfig creates an IstioOperatorSpec from the following sources, overlaid sequentially: +// 1. Compiled in base, or optionally base from paths pointing to one or multiple ICP/IOP files at inFilenames. +// 2. Profile overlay, if non-default overlay is selected. This also comes either from compiled in or path specified in IOP contained in inFilenames. +// 3. User overlays stored in inFilenames. +// 4. setOverlayYAML, which comes from --set flag passed to manifest command. +// +// Note that the user overlay at inFilenames can optionally contain a file path to a set of profiles different from the +// ones that are compiled in. If it does, the starting point will be the base and profile YAMLs at that file path. +// Otherwise it will be the compiled in profile YAMLs. +// In step 3, the remaining fields in the same user overlay are applied on the resulting profile base. +// The force flag causes validation errors not to abort but only emit log/console warnings. +func GenerateConfig(inFilenames []string, setFlags []string, force bool, client kube.Client, + l clog.Logger, +) (string, *iopv1alpha1.IstioOperator, error) { + if err := validateSetFlags(setFlags); err != nil { + return "", nil, err + } + + fy, profile, err := ReadYamlProfile(inFilenames, setFlags, force, l) + if err != nil { + return "", nil, err + } + + return OverlayYAMLStrings(profile, fy, setFlags, force, client, l) +} + +func OverlayYAMLStrings(profile string, fy string, + setFlags []string, force bool, client kube.Client, l clog.Logger, +) (string, *iopv1alpha1.IstioOperator, error) { + iopsString, iops, err := GenIOPFromProfile(profile, fy, setFlags, force, false, client, l) + if err != nil { + return "", nil, err + } + + errs, warning := validation.ValidateConfig(false, iops.Spec) + if warning != "" { + l.LogAndError(warning) + } + + if errs.ToError() != nil { + return "", nil, fmt.Errorf("generated config failed semantic validation: %v", errs) + } + return iopsString, iops, nil +} + +// GenIOPFromProfile generates an IstioOperator from the given profile name or path, and overlay YAMLs from user +// files and the --set flag. If successful, it returns an IstioOperator string and struct. +func GenIOPFromProfile(profileOrPath, fileOverlayYAML string, setFlags []string, skipValidation, allowUnknownField bool, + client kube.Client, l clog.Logger, +) (string, *iopv1alpha1.IstioOperator, error) { + installPackagePath, err := getInstallPackagePath(fileOverlayYAML) + if err != nil { + return "", nil, err + } + if sfp := GetValueForSetFlag(setFlags, "installPackagePath"); sfp != "" { + // set flag installPackagePath has the highest precedence, if set. + installPackagePath = sfp + } + + // To generate the base profileOrPath for overlaying with user values, we need the installPackagePath where the profiles + // can be found, and the selected profileOrPath. Both of these can come from either the user overlay file or --set flag. + outYAML, err := helm.GetProfileYAML(installPackagePath, profileOrPath) + if err != nil { + return "", nil, err + } + + // Hub and tag are only known at build time and must be passed in here during runtime from build stamps. + outYAML, err = overlayHubAndTag(outYAML) + if err != nil { + return "", nil, err + } + + // Merge k8s specific values. + if client != nil { + kubeOverrides, err := getClusterSpecificValues(client) + if err != nil { + return "", nil, err + } + installerScope.Infof("Applying Cluster specific settings: %v", kubeOverrides) + outYAML, err = util.OverlayYAML(outYAML, kubeOverrides) + if err != nil { + return "", nil, err + } + } + + // Combine file and --set overlays and translate any K8s settings in values to IOP format. Users should not set + // these but we have to support this path until it's deprecated. + overlayYAML, err := overlaySetFlagValues(fileOverlayYAML, setFlags) + if err != nil { + return "", nil, err + } + t := translate.NewReverseTranslator() + overlayYAML, err = t.TranslateK8SfromValueToIOP(overlayYAML) + if err != nil { + return "", nil, fmt.Errorf("could not overlay k8s settings from values to IOP: %s", err) + } + + // Merge user file and --set flags. + outYAML, err = util.OverlayIOP(outYAML, overlayYAML) + if err != nil { + return "", nil, fmt.Errorf("could not overlay user config over base: %s", err) + } + + // If enablement came from user values overlay (file or --set), translate into addonComponents paths and overlay that. + outYAML, err = translate.OverlayValuesEnablement(outYAML, overlayYAML, overlayYAML) + if err != nil { + return "", nil, err + } + + // convertDefaultIOPMapValues converts default paths values into string, prevent errors when unmarshalling. + outYAML, err = convertDefaultIOPMapValues(outYAML, setFlags) + if err != nil { + return "", nil, err + } + + finalIOP, err := unmarshalAndValidateIOP(outYAML, skipValidation, allowUnknownField, l) + if err != nil { + return "", nil, err + } + + // Validate Final IOP config against K8s cluster + if client != nil { + err = util.ValidateIOPCAConfig(client, finalIOP) + if err != nil { + return "", nil, err + } + } + // InstallPackagePath may have been a URL, change to extracted to local file path. + finalIOP.Spec.InstallPackagePath = installPackagePath + if ns := GetValueForSetFlag(setFlags, "values.global.istioNamespace"); ns != "" { + finalIOP.Namespace = ns + } + if finalIOP.Spec.Profile == "" { + finalIOP.Spec.Profile = name.DefaultProfileName + } + return util.MustToYAMLGeneric(finalIOP), finalIOP, nil +} + +// ReadYamlProfile gets the overlay yaml file from list of files and return profile value from file overlay and set overlay. +func ReadYamlProfile(inFilenames []string, setFlags []string, force bool, l clog.Logger) (string, string, error) { + profile := name.DefaultProfileName + // Get the overlay YAML from the list of files passed in. Also get the profile from the overlay files. + fy, fp, err := ParseYAMLFiles(inFilenames, force, l) + if err != nil { + return "", "", err + } + if fp != "" { + profile = fp + } + // The profile coming from --set flag has the highest precedence. + psf := GetValueForSetFlag(setFlags, "profile") + if psf != "" { + profile = psf + } + return fy, profile, nil +} + +// ParseYAMLFiles parses the given slice of filenames containing YAML and merges them into a single IstioOperator +// format YAML strings. It returns the overlay YAML, the profile name and error result. +func ParseYAMLFiles(inFilenames []string, force bool, l clog.Logger) (overlayYAML string, profile string, err error) { + if inFilenames == nil { + return "", "", nil + } + y, err := ReadLayeredYAMLs(inFilenames) + if err != nil { + return "", "", err + } + var fileOverlayIOP *iopv1alpha1.IstioOperator + fileOverlayIOP, err = validate.UnmarshalIOP(y) + if err != nil { + return "", "", err + } + if err := validate.ValidIOP(fileOverlayIOP); err != nil { + if !force { + return "", "", fmt.Errorf("validation errors (use --force to override): \n%s", err) + } + l.LogAndErrorf("Validation errors (continuing because of --force):\n%s", err) + } + if fileOverlayIOP.Spec != nil && fileOverlayIOP.Spec.Profile != "" { + profile = fileOverlayIOP.Spec.Profile + } + return y, profile, nil +} + +func ReadLayeredYAMLs(filenames []string) (string, error) { + return readLayeredYAMLs(filenames, os.Stdin) +} + +func readLayeredYAMLs(filenames []string, stdinReader io.Reader) (string, error) { + var ly string + var stdin bool + for _, fn := range filenames { + var b []byte + var err error + if fn == "-" { + if stdin { + continue + } + stdin = true + b, err = io.ReadAll(stdinReader) + } else { + b, err = os.ReadFile(strings.TrimSpace(fn)) + } + if err != nil { + return "", err + } + multiple := false + multiple, err = hasMultipleIOPs(string(b)) + if err != nil { + return "", err + } + if multiple { + return "", fmt.Errorf("input file %s contains multiple IstioOperator CRs, only one per file is supported", fn) + } + ly, err = util.OverlayIOP(ly, string(b)) + if err != nil { + return "", err + } + } + return ly, nil +} + +func hasMultipleIOPs(s string) (bool, error) { + objs, err := object.ParseK8sObjectsFromYAMLManifest(s) + if err != nil { + return false, err + } + found := false + for _, o := range objs { + if o.Kind == name.IstioOperator { + if found { + return true, nil + } + found = true + } + } + return false, nil +} + +func GetProfile(iop *iopv1alpha1.IstioOperator) string { + profile := "default" + if iop != nil && iop.Spec != nil && iop.Spec.Profile != "" { + profile = iop.Spec.Profile + } + return profile +} + +func GetMergedIOP(userIOPStr, profile, manifestsPath, revision string, client kube.Client, + logger clog.Logger, +) (*iopv1alpha1.IstioOperator, error) { + extraFlags := make([]string, 0) + if manifestsPath != "" { + extraFlags = append(extraFlags, fmt.Sprintf("installPackagePath=%s", manifestsPath)) + } + if revision != "" { + extraFlags = append(extraFlags, fmt.Sprintf("revision=%s", revision)) + } + _, mergedIOP, err := OverlayYAMLStrings(profile, userIOPStr, extraFlags, false, client, logger) + if err != nil { + return nil, err + } + return mergedIOP, nil +} + +// validateSetFlags validates that setFlags all have path=value format. +func validateSetFlags(setFlags []string) error { + for _, sf := range setFlags { + pv := strings.Split(sf, "=") + if len(pv) != 2 { + return fmt.Errorf("set flag %s has incorrect format, must be path=value", sf) + } + if pv[0] == "profile" && pv[1] == "external" { + return fmt.Errorf("\"external\" profile has been removed, use \"remote\" profile instead") + } + } + return nil +} + +// Due to the fact that base profile is compiled in before a tag can be created, we must allow an additional +// override from variables that are set during release build time. +func overlayHubAndTag(yml string) (string, error) { + hub := pkgversion.DockerInfo.Hub + tag := pkgversion.DockerInfo.Tag + out := yml + if hub != "unknown" && tag != "unknown" { + buildHubTagOverlayYAML, err := helm.GenerateHubTagOverlay(hub, tag) + if err != nil { + return "", err + } + out, err = util.OverlayYAML(yml, buildHubTagOverlayYAML) + if err != nil { + return "", err + } + } + return out, nil +} + +func getClusterSpecificValues(client kube.Client) (string, error) { + overlays := []string{} + + cni := getCNISettings(client) + if cni != "" { + overlays = append(overlays, cni) + } + return makeTreeFromSetList(overlays) +} + +// getCNISettings gets auto-detected values based on the Kubernetes environment. +// Note: there are other settings as well; however, these are detected inline in the helm chart. +// This ensures helm users also get them. +func getCNISettings(client kube.Client) string { + ver, err := client.GetKubernetesVersion() + if err != nil { + return "" + } + // https://istio.io/latest/docs/setup/additional-setup/cni/#hosted-kubernetes-settings + // GKE requires deployment in kube-system namespace. + if strings.Contains(ver.GitVersion, "-gke") { + return "components.cni.namespace=kube-system" + } + // TODO: OpenShift + return "" +} + +// makeTreeFromSetList creates a YAML tree from a string slice containing key-value pairs in the format key=value. +func makeTreeFromSetList(setOverlay []string) (string, error) { + if len(setOverlay) == 0 { + return "", nil + } + tree := make(map[string]any) + for _, kv := range setOverlay { + kvv := strings.Split(kv, "=") + if len(kvv) != 2 { + return "", fmt.Errorf("bad argument %s: expect format key=value", kv) + } + k := kvv[0] + v := util.ParseValue(kvv[1]) + if err := tpath.WriteNode(tree, util.PathFromString(k), v); err != nil { + return "", err + } + // To make errors more user friendly, test the path and error out immediately if we cannot unmarshal. + testTree, err := yaml.Marshal(tree) + if err != nil { + return "", err + } + iops := &v1alpha1.IstioOperatorSpec{} + if err := util.UnmarshalWithJSONPB(string(testTree), iops, false); err != nil { + return "", fmt.Errorf("bad path=value %s: %v", kv, err) + } + } + out, err := yaml.Marshal(tree) + if err != nil { + return "", err + } + return tpath.AddSpecRoot(string(out)) +} + +// unmarshalAndValidateIOP unmarshals a string containing IstioOperator YAML, validates it, and returns a struct +// representation if successful. If force is set, validation errors are written to logger rather than causing an +// error. +func unmarshalAndValidateIOP(iopsYAML string, force, allowUnknownField bool, l clog.Logger) (*iopv1alpha1.IstioOperator, error) { + iop, err := istio.UnmarshalIstioOperator(iopsYAML, allowUnknownField) + if err != nil { + return nil, fmt.Errorf("could not unmarshal merged YAML: %s\n\nYAML:\n%s", err, iopsYAML) + } + if errs := validate.CheckIstioOperatorSpec(iop.Spec, true); len(errs) != 0 && !force { + l.LogAndError("Run the command with the --force flag if you want to ignore the validation error and proceed.") + return iop, fmt.Errorf(errs.Error()) + } + return iop, nil +} + +// getInstallPackagePath returns the installPackagePath in the given IstioOperator YAML string. +func getInstallPackagePath(iopYAML string) (string, error) { + iop, err := validate.UnmarshalIOP(iopYAML) + if err != nil { + return "", err + } + if iop.Spec == nil { + return "", nil + } + return iop.Spec.InstallPackagePath, nil +} + +// alwaysString represents types that should always be decoded as strings +// TODO: this could be automatically derived from the value_types.proto? +var alwaysString = sets.New("values.compatibilityVersion", "compatibilityVersion") + +// overlaySetFlagValues overlays each of the setFlags on top of the passed in IOP YAML string. +func overlaySetFlagValues(iopYAML string, setFlags []string) (string, error) { + iop := make(map[string]any) + if err := yaml.Unmarshal([]byte(iopYAML), &iop); err != nil { + return "", err + } + // Unmarshal returns nil for empty manifests but we need something to insert into. + if iop == nil { + iop = make(map[string]any) + } + + for _, sf := range setFlags { + p, v := getPV(sf) + p = strings.TrimPrefix(p, "spec.") + inc, _, err := tpath.GetPathContext(iop, util.PathFromString("spec."+p), true) + if err != nil { + return "", err + } + // input value type is always string, transform it to correct type before setting. + var val any = v + if !alwaysString.Contains(p) { + val = util.ParseValue(v) + } + if err := tpath.WritePathContext(inc, val, false); err != nil { + return "", err + } + } + + out, err := yaml.Marshal(iop) + if err != nil { + return "", err + } + + return string(out), nil +} + +var defaultSetFlagConvertPaths = []string{ + "meshConfig.defaultConfig.proxyMetadata", +} + +// convertDefaultIOPMapValues converts default map[string]string values into string. +func convertDefaultIOPMapValues(outYAML string, setFlags []string) (string, error) { + return convertIOPMapValues(outYAML, setFlags, defaultSetFlagConvertPaths) +} + +// convertIOPMapValues converts certain paths of map[string]string values into string. +func convertIOPMapValues(outYAML string, setFlags []string, convertPaths []string) (string, error) { + for _, setFlagConvertPath := range convertPaths { + if containParentPath(setFlags, setFlagConvertPath) { + var ( + converter = map[string]interface{}{} + convertedProxyMetadata = map[string]string{} + subPaths = strings.Split(setFlagConvertPath, ".") + ) + + if err := yaml.Unmarshal([]byte(outYAML), &converter); err != nil { + return outYAML, err + } + originMap, ok := converter["spec"].(map[string]any) + if !ok { + return outYAML, nil + } + + for index, subPath := range subPaths { + if _, ok := originMap[subPath].(map[string]any); !ok { + return outYAML, fmt.Errorf("can not convert subPath %s in setFlag path %s", + subPath, setFlagConvertPath) + } + + if index == len(subPaths)-1 { + for key, value := range originMap[subPath].(map[string]any) { + if reflect.TypeOf(value).Kind() == reflect.Int { + convertedProxyMetadata[key] = strconv.FormatInt(value.(int64), 10) + } + if reflect.TypeOf(value).Kind() == reflect.Bool { + convertedProxyMetadata[key] = strconv.FormatBool(value.(bool)) + } + if reflect.TypeOf(value).Kind() == reflect.Float64 { + convertedProxyMetadata[key] = fmt.Sprint(value) + } + if reflect.TypeOf(value).Kind() == reflect.String { + convertedProxyMetadata[key] = value.(string) + } + } + originMap[subPath] = convertedProxyMetadata + } else { + originMap = originMap[subPath].(map[string]any) + } + } + + convertedYaml, err := yaml.Marshal(converter) + if err != nil { + return outYAML, err + } + return string(convertedYaml), nil + } + } + + return outYAML, nil +} + +// containParentPath checks if setFlags contain parent path. +func containParentPath(setFlags []string, parentPath string) bool { + ret := false + for _, sf := range setFlags { + p, _ := getPV(sf) + if strings.Contains(p, parentPath) { + ret = true + break + } + } + return ret +} + +// GetValueForSetFlag parses the passed set flags which have format key=value and if any set the given path, +// returns the corresponding value, otherwise returns the empty string. setFlags must have valid format. +func GetValueForSetFlag(setFlags []string, path string) string { + ret := "" + for _, sf := range setFlags { + p, v := getPV(sf) + if p == path { + ret = v + } + // if set multiple times, return last set value + } + return ret +} + +// getPV returns the path and value components for the given set flag string, which must be in path=value format. +func getPV(setFlag string) (path string, value string) { + pv := strings.Split(setFlag, "=") + if len(pv) != 2 { + return setFlag, "" + } + path, value = strings.TrimSpace(pv[0]), strings.TrimSpace(pv[1]) + return +} diff --git a/operator/pkg/manifest/shared_test.go b/operator/pkg/manifest/shared_test.go new file mode 100644 index 0000000..89f620b --- /dev/null +++ b/operator/pkg/manifest/shared_test.go @@ -0,0 +1,154 @@ +// Copyright Istio 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 manifest + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/jehawley/istio/operator/pkg/util" + "istio.io/istio/pkg/test/env" +) + +func TestReadLayeredYAMLs(t *testing.T) { + testDataDir := filepath.Join(env.IstioSrc, "operator/pkg/util/testdata/yaml") + tests := []struct { + name string + overlays []string + wantErr bool + stdin bool + }{ + { + name: "layer1", + overlays: []string{"yaml_layer1"}, + wantErr: false, + }, + { + name: "layer1_stdin", + overlays: []string{"yaml_layer1"}, + wantErr: false, + stdin: true, + }, + { + name: "layer1_2", + overlays: []string{"yaml_layer1", "yaml_layer2"}, + wantErr: false, + }, + { + name: "layer1_2_3", + overlays: []string{"yaml_layer1", "yaml_layer2", "yaml_layer3"}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("%s stdin=%v", tt.name, tt.stdin), func(t *testing.T) { + inDir := filepath.Join(testDataDir, "input") + outPath := filepath.Join(testDataDir, "output", tt.name+".yaml") + wantBytes, err := os.ReadFile(outPath) + want := string(wantBytes) + if err != nil { + t.Errorf("os.ReadFile() error = %v, filename: %v", err, outPath) + } + + stdinReader := &bytes.Buffer{} + + var filenames []string + for _, ol := range tt.overlays { + filename := filepath.Join(inDir, ol+".yaml") + if tt.stdin { + b, err := os.ReadFile(filename) + if err != nil { + t.Fatalf("os.ReadFile() error = %v, filenaem: %v", err, filename) + } + if _, err := stdinReader.Write(b); err != nil { + t.Fatalf("failed to populate fake sdtin") + } + filenames = append(filenames, "-") + } else { + filenames = append(filenames, filename) + } + } + got, err := readLayeredYAMLs(filenames, stdinReader) + if (err != nil) != tt.wantErr { + t.Errorf("ReadLayeredYAMLs() error = %v, wantErr %v", err, tt.wantErr) + return + } + + diff := util.YAMLDiff(got, want) + if diff != "" { + t.Errorf("ReadLayeredYAMLs() got:\n%s\nwant:\n%s\ndiff:\n%s", got, want, diff) + } + }) + } +} + +func TestConvertIOPMapValues(t *testing.T) { + testDataDir := filepath.Join(env.IstioSrc, "operator/pkg/util/testdata/yaml") + tests := []struct { + name string + inputFlags []string + convertPaths []string + }{ + { + name: "convention_boolean", + convertPaths: defaultSetFlagConvertPaths, + inputFlags: []string{ + "meshConfig.defaultConfig.proxyMetadata.ISTIO_DUAL_STACK=false", + "meshConfig.defaultConfig.proxyMetadata.PROXY_XDS_VIA_AGENT=false", + }, + }, { + name: "convention_integer", + convertPaths: defaultSetFlagConvertPaths, + inputFlags: []string{ + "meshConfig.defaultConfig.proxyMetadata.ISTIO_MULTI_CLUSTERS=10", + "meshConfig.defaultConfig.proxyMetadata.PROXY_XDS_LISTENERS=20", + }, + }, { + name: "convention_float", + convertPaths: defaultSetFlagConvertPaths, + inputFlags: []string{ + "meshConfig.defaultConfig.proxyMetadata.PROXY_UPSTREAM_WEIGHT=0.85", + "meshConfig.defaultConfig.proxyMetadata.PROXY_DOWNSTREAM_WEIGHT=0.15", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + inPath := filepath.Join(testDataDir, "input", tt.name+".yaml") + outPath := filepath.Join(testDataDir, "output", tt.name+".yaml") + input, err := os.ReadFile(inPath) + if err != nil { + t.Fatalf(err.Error()) + } + actualOutput, err := convertIOPMapValues(string(input), tt.inputFlags, tt.convertPaths) + if err != nil { + t.Fatalf(err.Error()) + } + expectOutput, err := os.ReadFile(outPath) + if err != nil { + t.Fatalf(err.Error()) + } + + diff := util.YAMLDiff(actualOutput, string(expectOutput)) + if diff != "" { + t.Errorf("convertIOPMapValues() got:\n%s\nwant:\n%s\ndiff:\n%s", actualOutput, string(expectOutput), diff) + } + }) + } +} diff --git a/operator/pkg/metrics/monitoring.go b/operator/pkg/metrics/monitoring.go new file mode 100644 index 0000000..4a83829 --- /dev/null +++ b/operator/pkg/metrics/monitoring.go @@ -0,0 +1,210 @@ +// Copyright Istio 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 metrics defines metrics and monitoring functionality +// used throughout operator. +package metrics + +import ( + "istio.io/istio/pkg/monitoring" +) + +var ( + // OperatorVersionLabel describes version of running binary. + OperatorVersionLabel = monitoring.CreateLabel("version") + + // MergeErrorLabel describes the type of merge error. + MergeErrorLabel = monitoring.CreateLabel("error_type") + + // RenderErrorLabel describes the type of the error while rendering. + RenderErrorLabel = monitoring.CreateLabel("render_error") + + // CRFetchErrorReasonLabel describes the reason/HTTP code + // for failing to fetch CR. + CRFetchErrorReasonLabel = monitoring.CreateLabel("reason") + + // ComponentNameLabel represents istio component name - like + // core, pilot, istio-cni etc. + ComponentNameLabel = monitoring.CreateLabel("component") + + // ResourceKindLabel indicates the kind of resource owned + // or created or updated or deleted or pruned by operator. + ResourceKindLabel = monitoring.CreateLabel("kind") + + // ReconcileRequestReasonLabel describes reason of reconcile request. + ReconcileRequestReasonLabel = monitoring.CreateLabel("reason") +) + +// MergeErrorType describes the class of errors that could +// occur while merging profile, user supplied YAML, values +// overridden by --set and so on. +type MergeErrorType string + +const ( + // CannotFetchProfileError occurs when profile cannot be found. + CannotFetchProfileError MergeErrorType = "cannot_fetch_profile" + + // OverlayError overlaying YAMLs to combine profile, user + // defined settings in CR, Hub-tag etc. fails. + OverlayError MergeErrorType = "overlay" + + // IOPFormatError occurs when supplied CR cannot be marshaled + // or unmarshaled to/from YAML. + IOPFormatError MergeErrorType = "iop_format" + + // TranslateValuesError occurs when translating from legacy API fails. + TranslateValuesError MergeErrorType = "translate_values" + + // InternalYAMLParseError occurs when spec section in merged CR + // cannot be accessed for some reason (either missing or multiple). + InternalYAMLParseError MergeErrorType = "internal_yaml_parse" +) + +// RenderErrorType describes the class of errors that could +// occur while rendering Kubernetes manifest from given CR. +type RenderErrorType string + +const ( + RenderNotStartedError RenderErrorType = "render_not_started" + + // HelmTranslateIOPToValuesError describes render error where renderer for + // a component cannot create values.yaml tree from given CR. + HelmTranslateIOPToValuesError RenderErrorType = "helm_translate_iop_to_values" + + // HelmChartRenderError describes error where Helm charts cannot be rendered + // for the generated values.yaml tree. + HelmChartRenderError RenderErrorType = "helm_chart_render" + + // K8SSettingsOverlayError describes the K8s overlay error after + // rendering Helm charts successfully. + K8SSettingsOverlayError RenderErrorType = "k8s_settings_overlay" + + // K8SManifestPatchError describes errors while patching generated manifest. + K8SManifestPatchError RenderErrorType = "k8s_manifest_patch" +) + +var ( + // Version is the version of the operator binary running currently. + Version = monitoring.NewGauge( + "version", + "Version of operator binary", + ) + + ReconcileRequestTotal = monitoring.NewSum( + "reconcile_request_total", + "Number of times requesting Reconcile", + ) + + // GetCRErrorTotal counts the number of times fetching + // CR fails from API server. + GetCRErrorTotal = monitoring.NewSum( + "get_cr_error_total", + "Number of times fetching CR from apiserver failed", + ) + + // CRMergeFailureTotal counts number of CR merge failures. + CRMergeFailureTotal = monitoring.NewSum( + "cr_merge_failure_total", + "Number of IstioOperator CR merge failures", + ) + + // CRDeletionTotal counts the number of times + // IstioOperator CR was deleted. + CRDeletionTotal = monitoring.NewSum( + "cr_deletion_total", + "Number of IstioOperator CR deleted", + ) + + // CRValidationErrorTotal counts the number of CR + // validation failures. + CRValidationErrorTotal = monitoring.NewSum( + "cr_validation_error_total", + "Number of IstioOperator CR validation failures", + ) + + // RenderManifestTotal counts the number of manifest + // renders at each component level. + RenderManifestTotal = monitoring.NewSum( + "render_manifest_total", + "Number of component manifests rendered", + ) + + // OwnedResourceTotal indicates the number of resources + // currently owned by the CR with given name and revision. + OwnedResourceTotal = monitoring.NewGauge( + "owned_resource_total", + "Number of resources currently owned by the operator", + ) + + // ResourceCreationTotal indicates the number of resources + // created by the operator for a CR and revision. + ResourceCreationTotal = monitoring.NewSum( + "resource_creation_total", + "Number of resources created by the operator", + ) + + // ResourceUpdateTotal indicates the number of resources updated by + // the operator in response to CR updates for a revision. + ResourceUpdateTotal = monitoring.NewSum( + "resource_update_total", + "Number of resources updated by the operator", + ) + + // ResourceDeletionTotal indicates the number of resources deleted + // by the operator in response to CR update or delete operation (like + // ingress-gateway which was enabled could be disabled and this requires + // deleting ingress-gateway deployment). + ResourceDeletionTotal = monitoring.NewSum( + "resource_deletion_total", + "Number of resources deleted by the operator", + ) + + // ResourcePruneTotal indicates the resources pruned as a result of update. + ResourcePruneTotal = monitoring.NewSum( + "resource_prune_total", + "Number of resources pruned by the operator", + ) + + // ManifestPatchErrorTotal counts the total number of K8S patch errors. + ManifestPatchErrorTotal = monitoring.NewSum( + "manifest_patch_error_total", + "Number of times K8S patch overlays failed", + ) + + // ManifestRenderErrorTotal counts errors occurred while rendering manifest. + ManifestRenderErrorTotal = monitoring.NewSum( + "manifest_render_error_total", + "Number of times error occurred during rendering output manifest", + ) + + // LegacyPathTranslationTotal counts the translations from legacy API to new one. + LegacyPathTranslationTotal = monitoring.NewSum( + "legacy_path_translation_total", + "Number of times a legacy API path is translated", + ) + + // CacheFlushTotal counts number of cache flushes. + CacheFlushTotal = monitoring.NewSum( + "cache_flush_total", + "number of times operator cache was flushed", + ) +) + +func init() { + initOperatorCrdResourceMetrics() +} + +func IncrementReconcileRequest(reason string) { + ReconcileRequestTotal.With(ReconcileRequestReasonLabel.Value(reason)).Increment() +} diff --git a/operator/pkg/metrics/resource_counts.go b/operator/pkg/metrics/resource_counts.go new file mode 100644 index 0000000..31c1d25 --- /dev/null +++ b/operator/pkg/metrics/resource_counts.go @@ -0,0 +1,68 @@ +// Copyright Istio 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 metrics + +import ( + "sync" + + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/jehawley/istio/operator/pkg/util" +) + +// resourceCounts keeps track of the number of resources owned by each +// IstioOperator resource. The reported metric is the sum across all these. +type resourceCounts struct { + mu *sync.Mutex + resources map[schema.GroupKind]map[string]struct{} +} + +var rc *resourceCounts + +func initOperatorCrdResourceMetrics() { + rc = &resourceCounts{ + mu: &sync.Mutex{}, + resources: map[schema.GroupKind]map[string]struct{}{}, + } +} + +// AddResource adds the resource of given kind to the set of owned objects +func AddResource(name string, gk schema.GroupKind) { + rc.mu.Lock() + defer rc.mu.Unlock() + if _, present := rc.resources[gk]; !present { + rc.resources[gk] = map[string]struct{}{} + } + rc.resources[gk][name] = struct{}{} +} + +// RemoveResource removes the resource of given kind to the set of owned objects +func RemoveResource(name string, gk schema.GroupKind) { + rc.mu.Lock() + defer rc.mu.Unlock() + delete(rc.resources[gk], name) +} + +// ReportOwnedResourceCounts reports the owned resource count +// metric by Group and Kind. +func ReportOwnedResourceCounts() { + rc.mu.Lock() + defer rc.mu.Unlock() + for gk, r := range rc.resources { + OwnedResourceTotal. + With(ResourceKindLabel.Value(util.GKString(gk))). + Record(float64(len(r))) + } +} diff --git a/operator/pkg/metrics/utils.go b/operator/pkg/metrics/utils.go new file mode 100644 index 0000000..1473708 --- /dev/null +++ b/operator/pkg/metrics/utils.go @@ -0,0 +1,58 @@ +// Copyright Istio 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 metrics + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/jehawley/istio/operator/pkg/name" +) + +// CountCRMergeFail increments the count of CR merge failure +// for the given merge error type. +func CountCRMergeFail(reason MergeErrorType) { + CRMergeFailureTotal. + With(MergeErrorLabel.Value(string(reason))). + Increment() +} + +// CountManifestRenderError increments the count of manifest +// render errors. +func CountManifestRenderError(cn name.ComponentName, reason RenderErrorType) { + ManifestRenderErrorTotal. + With(ComponentNameLabel.Value(string(cn))). + With(RenderErrorLabel.Value(string(reason))). + Increment() +} + +// CountCRFetchFail increments the count of CR fetch failure +// for a given name and the error status. +func CountCRFetchFail(reason metav1.StatusReason) { + errorReason := string(reason) + if reason == metav1.StatusReasonUnknown { + errorReason = "unknown" + } + GetCRErrorTotal. + With(CRFetchErrorReasonLabel.Value(errorReason)). + Increment() +} + +// CountManifestRender increments the count of rendered +// manifest from IstioOperator CR by component name. +func CountManifestRender(name name.ComponentName) { + RenderManifestTotal. + With(ComponentNameLabel.Value(string(name))). + Increment() +} diff --git a/operator/pkg/name/name.go b/operator/pkg/name/name.go new file mode 100644 index 0000000..bb8c9b2 --- /dev/null +++ b/operator/pkg/name/name.go @@ -0,0 +1,226 @@ +// Copyright Istio 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 name + +import ( + "fmt" + "strings" + + "istio.io/api/123/operator/v1alpha1" + iop "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/helm" + "github.com/jehawley/istio/operator/pkg/tpath" +) + +// Kubernetes Kind strings. +const ( + CRDStr = "CustomResourceDefinition" + ClusterRoleStr = "ClusterRole" + ClusterRoleBindingStr = "ClusterRoleBinding" + CMStr = "ConfigMap" + DaemonSetStr = "DaemonSet" + DeploymentStr = "Deployment" + EndpointStr = "Endpoints" + HPAStr = "HorizontalPodAutoscaler" + IstioOperator = "IstioOperator" + MutatingWebhookConfigurationStr = "MutatingWebhookConfiguration" + NamespaceStr = "Namespace" + NetworkAttachmentDefinitionStr = "NetworkAttachmentDefinition" + PodStr = "Pod" + PDBStr = "PodDisruptionBudget" + ReplicaSetStr = "ReplicaSet" + RoleStr = "Role" + RoleBindingStr = "RoleBinding" + SAStr = "ServiceAccount" + ServiceStr = "Service" + SecretStr = "Secret" + StatefulSetStr = "StatefulSet" + ValidatingWebhookConfigurationStr = "ValidatingWebhookConfiguration" +) + +const ( + // IstioOperatorStr is the kind name of the IstioOperator CRD. + IstioOperatorStr = "IstioOperator" + + // OperatorAPINamespace is the API namespace for operator config. + // TODO: move this to a base definitions file when one is created. + OperatorAPINamespace = "operator.istio.io" + + // DefaultProfileName is the name of the default profile. + DefaultProfileName = "default" +) + +// ComponentName is a component name string, typed to constrain allowed values. +type ComponentName string + +const ( + // IstioComponent names corresponding to the IstioOperator proto component names. Must be the same, since these + // are used for struct traversal. + IstioBaseComponentName ComponentName = "Base" + PilotComponentName ComponentName = "Pilot" + + CNIComponentName ComponentName = "Cni" + ZtunnelComponentName ComponentName = "Ztunnel" + + // istiod remote component + IstiodRemoteComponentName ComponentName = "IstiodRemote" + + // Gateway components + IngressComponentName ComponentName = "IngressGateways" + EgressComponentName ComponentName = "EgressGateways" + + // Operator components + IstioOperatorComponentName ComponentName = "IstioOperator" + IstioOperatorCustomResourceName ComponentName = "IstioOperatorCustomResource" +) + +var IstioComponentIcons = map[ComponentName]string{ + IstioBaseComponentName: "⛵️", + PilotComponentName: "🧠", + CNIComponentName: "🪢", + ZtunnelComponentName: "🔒", + IngressComponentName: "🛬", + EgressComponentName: "🛫", +} + +// ComponentNamesConfig is used for unmarshaling legacy and addon naming data. +type ComponentNamesConfig struct { + DeprecatedComponentNames []string +} + +var ( + AllCoreComponentNames = []ComponentName{ + IstioBaseComponentName, + PilotComponentName, + CNIComponentName, + IstiodRemoteComponentName, + ZtunnelComponentName, + } + + // AllComponentNames is a list of all Istio components. + AllComponentNames = append(AllCoreComponentNames, IngressComponentName, EgressComponentName, + IstioOperatorComponentName, IstioOperatorCustomResourceName) + + // ValuesEnablementPathMap defines a mapping between legacy values enablement paths and the corresponding enablement + // paths in IstioOperator. + ValuesEnablementPathMap = map[string]string{ + "spec.values.gateways.istio-ingressgateway.enabled": "spec.components.ingressGateways.[name:istio-ingressgateway].enabled", + "spec.values.gateways.istio-egressgateway.enabled": "spec.components.egressGateways.[name:istio-egressgateway].enabled", + } + + // userFacingComponentNames are the names of components that are displayed to the user in high level CLIs + // (like progress log). + userFacingComponentNames = map[ComponentName]string{ + IstioBaseComponentName: "Istio core", + PilotComponentName: "Istiod", + CNIComponentName: "CNI", + ZtunnelComponentName: "Ztunnel", + IngressComponentName: "Ingress gateways", + EgressComponentName: "Egress gateways", + IstioOperatorComponentName: "Istio operator", + IstioOperatorCustomResourceName: "Istio operator CRDs", + IstiodRemoteComponentName: "Istiod remote", + } +) + +// Manifest defines a manifest for a component. +type Manifest struct { + Name ComponentName + Content string +} + +// ManifestMap is a map of ComponentName to its manifest string. +type ManifestMap map[ComponentName][]string + +// Consolidated returns a representation of mm where all manifests in the slice under a key are combined into a single +// manifest. +func (mm ManifestMap) Consolidated() map[string]string { + out := make(map[string]string) + for cname, ms := range mm { + allM := "" + for _, m := range ms { + allM += m + helm.YAMLSeparator + } + out[string(cname)] = allM + } + return out +} + +// MergeManifestSlices merges a slice of manifests into a single manifest string. +func MergeManifestSlices(manifests []string) string { + return strings.Join(manifests, helm.YAMLSeparator) +} + +// String implements the Stringer interface. +func (mm ManifestMap) String() string { + out := "" + for _, ms := range mm { + for _, m := range ms { + out += m + helm.YAMLSeparator + } + } + return out +} + +// IsGateway reports whether cn is a gateway component. +func (cn ComponentName) IsGateway() bool { + return cn == IngressComponentName || cn == EgressComponentName +} + +// Namespace returns the namespace for the component. It follows these rules: +// 1. If DefaultNamespace is unset, log and error and return the empty string. +// 2. If the feature and component namespaces are unset, return DefaultNamespace. +// 3. If the feature namespace is set but component name is unset, return the feature namespace. +// 4. Otherwise return the component namespace. +// Namespace assumes that controlPlaneSpec has been validated. +// TODO: remove extra validations when comfort level is high enough. +func Namespace(componentName ComponentName, controlPlaneSpec *v1alpha1.IstioOperatorSpec) (string, error) { + defaultNamespace := iop.Namespace(controlPlaneSpec) + + componentNodeI, found, err := tpath.GetFromStructPath(controlPlaneSpec, "Components."+string(componentName)+".Namespace") + if err != nil { + return "", fmt.Errorf("error in Namespace GetFromStructPath componentNamespace for component=%s: %s", componentName, err) + } + if !found { + return defaultNamespace, nil + } + if componentNodeI == nil { + return defaultNamespace, nil + } + componentNamespace, ok := componentNodeI.(string) + if !ok { + return "", fmt.Errorf("component %s enabled has bad type %T, expect string", componentName, componentNodeI) + } + if componentNamespace == "" { + return defaultNamespace, nil + } + return componentNamespace, nil +} + +// TitleCase returns a capitalized version of n. +func TitleCase(n ComponentName) ComponentName { + s := string(n) + return ComponentName(strings.ToUpper(s[0:1]) + s[1:]) +} + +// UserFacingComponentName returns the name of the given component that should be displayed to the user in high +// level CLIs (like progress log). +func UserFacingComponentName(name ComponentName) string { + ret, ok := userFacingComponentNames[name] + if !ok { + return "Unknown" + } + return ret +} diff --git a/operator/pkg/name/name_test.go b/operator/pkg/name/name_test.go new file mode 100644 index 0000000..ac22541 --- /dev/null +++ b/operator/pkg/name/name_test.go @@ -0,0 +1,367 @@ +// Copyright Istio 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 name + +import ( + "reflect" + "testing" + + "sigs.k8s.io/yaml" + + "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" +) + +func TestGetFromTreePath(t *testing.T) { + type args struct { + inputTree string + path util.Path + } + + tests := []struct { + name string + args args + want string + found bool + wantErr bool + }{ + { + name: "found string node", + args: args{ + inputTree: ` +k1: v1 +`, + path: util.Path{"k1"}, + }, + want: ` +v1 +`, + found: true, + wantErr: false, + }, + { + name: "found tree node", + args: args{ + inputTree: ` +k1: + k2: v2 +`, + path: util.Path{"k1"}, + }, + want: ` +k2: v2 +`, + found: true, + wantErr: false, + }, + { + name: "path is longer than tree depth, string node", + args: args{ + inputTree: ` +k1: + k2: v1 +`, + path: util.Path{"k1", "k2", "k3"}, + }, + want: "", + found: false, + wantErr: false, + }, + { + name: "path not in map array tree", + args: args{ + inputTree: ` +a: + b: + - name: n1 + value: v1 + - name: n2 + list: + - v21 + - v22 +`, + path: util.Path{"a", "b", "unknown"}, + }, + want: ``, + found: false, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tree := make(map[string]any) + if err := yaml.Unmarshal([]byte(tt.args.inputTree), &tree); err != nil { + t.Fatal(err) + } + got, found, err := tpath.Find(tree, tt.args.path) + if (err != nil) != tt.wantErr { + t.Errorf("Find() error = %v, wantErr %v", err, tt.wantErr) + return + } + + var wantTree any + if err := yaml.Unmarshal([]byte(tt.want), &wantTree); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(got, wantTree) { + t.Errorf("Find() got = %v, want %v", got, tt.want) + } + if found != tt.found { + t.Errorf("Find() found = %v, want %v", found, tt.found) + } + }) + } +} + +func TestManifestMap_Consolidated(t *testing.T) { + tests := []struct { + name string + mm ManifestMap + want map[string]string + }{ + { + name: "consolidate output from manifest map", + mm: ManifestMap{ + "key1": []string{"value1", "value2"}, + "key2": []string{}, + }, + want: map[string]string{ + "key1": "value1\n---\nvalue2\n---\n", + "key2": "", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.mm.Consolidated(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ManifestMap.Consolidated() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMergeManifestSlices(t *testing.T) { + tests := []struct { + name string + manifests []string + want string + }{ + { + name: "merge manifest slices", + manifests: []string{"key1", "key2", "key3"}, + want: "key1\n---\nkey2\n---\nkey3", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := MergeManifestSlices(tt.manifests); got != tt.want { + t.Errorf("MergeManifestSlices() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestManifestMap_String(t *testing.T) { + tests := []struct { + name string + mm ManifestMap + want1 string + want2 string + }{ + { + name: "consolidate from manifest map", + mm: ManifestMap{ + "key1": []string{"value1", "value2"}, + "key2": []string{"value2", "value3"}, + }, + want1: "value1\n---\nvalue2\n---\nvalue2\n---\nvalue3\n---\n", + want2: "value2\n---\nvalue3\n---\nvalue1\n---\nvalue2\n---\n", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.mm.String() + if got != tt.want1 && got != tt.want2 { + t.Errorf("ManifestMap.String() = %v, want %v or %v", got, tt.want1, tt.want2) + } + }) + } +} + +func TestComponentName_IsGateway(t *testing.T) { + tests := []struct { + name string + cn ComponentName + want bool + }{ + { + name: "ComponentName is IngressGateways", + cn: IngressComponentName, + want: true, + }, + { + name: "ComponentName is EgressGateways", + cn: EgressComponentName, + want: true, + }, + { + name: "ComponentName is others", + cn: CNIComponentName, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.cn.IsGateway(); got != tt.want { + t.Errorf("ComponentName.IsGateway() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTitleCase(t *testing.T) { + tests := []struct { + name string + n ComponentName + want ComponentName + }{ + { + name: "to upper title", + n: "ingressGateways", + want: "IngressGateways", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := TitleCase(tt.n); got != tt.want { + t.Errorf("TitleCase() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestUserFacingComponentName(t *testing.T) { + tests := []struct { + name string + n ComponentName + want string + }{ + { + name: "ComponentName is unknown", + n: "foo", + want: "Unknown", + }, + { + name: "ComponentName is istio core", + n: IstioBaseComponentName, + want: "Istio core", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := UserFacingComponentName(tt.n); got != tt.want { + t.Errorf("UserFacingComponentName() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNamespace(t *testing.T) { + type args struct { + componentName ComponentName + controlPlaneSpec *v1alpha1.IstioOperatorSpec + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "DefaultNamespace and componentNamespace are unset", + args: args{ + componentName: CNIComponentName, + controlPlaneSpec: &v1alpha1.IstioOperatorSpec{ + Hub: "docker.io", + }, + }, + want: "", + wantErr: false, + }, + { + name: "DefaultNamespace is set and componentNamespace in empty", + args: args{ + componentName: CNIComponentName, + controlPlaneSpec: &v1alpha1.IstioOperatorSpec{ + Hub: "docker.io", + Namespace: "istio-system", + Components: &v1alpha1.IstioComponentSetSpec{ + Cni: &v1alpha1.ComponentSpec{ + Namespace: "", + }, + }, + }, + }, + want: "istio-system", + wantErr: false, + }, + { + name: "DefaultNamespace is set and componentNamespace is unset", + args: args{ + componentName: CNIComponentName, + controlPlaneSpec: &v1alpha1.IstioOperatorSpec{ + Hub: "docker.io", + Namespace: "istio-system", + Components: &v1alpha1.IstioComponentSetSpec{ + Cni: &v1alpha1.ComponentSpec{}, + }, + }, + }, + want: "istio-system", + wantErr: false, + }, + { + name: "DefaultNamespace and componentNamespace are set and not empty", + args: args{ + componentName: CNIComponentName, + controlPlaneSpec: &v1alpha1.IstioOperatorSpec{ + Hub: "docker.io", + Namespace: "istio-system", + Components: &v1alpha1.IstioComponentSetSpec{ + Cni: &v1alpha1.ComponentSpec{ + Namespace: "istio-test", + }, + }, + }, + }, + want: "istio-test", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Namespace(tt.args.componentName, tt.args.controlPlaneSpec) + if (err != nil) != tt.wantErr { + t.Errorf("Namespace() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("Namespace() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/operator/pkg/object/objects.go b/operator/pkg/object/objects.go new file mode 100644 index 0000000..954c277 --- /dev/null +++ b/operator/pkg/object/objects.go @@ -0,0 +1,579 @@ +// Copyright Istio 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 manifest provides functions for going between in-memory k8s objects (unstructured.Unstructured) and their JSON +or YAML representations. +*/ +package object + +import ( + "bytes" + "fmt" + "io" + "sort" + "strings" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer/json" + "k8s.io/apimachinery/pkg/util/intstr" + k8syaml "k8s.io/apimachinery/pkg/util/yaml" + "sigs.k8s.io/yaml" + + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/helm" + names "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" + "istio.io/istio/pkg/log" +) + +const ( + // YAMLSeparator is a separator for multi-document YAML files. + YAMLSeparator = "\n---\n" +) + +// K8sObject is an in-memory representation of a k8s object, used for moving between different representations +// (Unstructured, JSON, YAML) with cached rendering. +type K8sObject struct { + object *unstructured.Unstructured + + Group string + Kind string + Name string + Namespace string + + json []byte + yaml []byte +} + +// NewK8sObject creates a new K8sObject and returns a ptr to it. +func NewK8sObject(u *unstructured.Unstructured, json, yaml []byte) *K8sObject { + o := &K8sObject{ + object: u, + json: json, + yaml: yaml, + } + + gvk := u.GetObjectKind().GroupVersionKind() + o.Group = gvk.Group + o.Kind = gvk.Kind + o.Name = u.GetName() + o.Namespace = u.GetNamespace() + + return o +} + +// Hash returns a unique, insecure hash based on kind, namespace and name. +func Hash(kind, namespace, name string) string { + switch kind { + case names.ClusterRoleStr, names.ClusterRoleBindingStr: + namespace = "" + } + return strings.Join([]string{kind, namespace, name}, ":") +} + +// FromHash parses kind, namespace and name from a hash. +func FromHash(hash string) (kind, namespace, name string) { + hv := strings.Split(hash, ":") + if len(hv) != 3 { + return "Bad hash string: " + hash, "", "" + } + kind, namespace, name = hv[0], hv[1], hv[2] + return +} + +// HashNameKind returns a unique, insecure hash based on kind and name. +func HashNameKind(kind, name string) string { + return strings.Join([]string{kind, name}, ":") +} + +// ParseJSONToK8sObject parses JSON to an K8sObject. +func ParseJSONToK8sObject(json []byte) (*K8sObject, error) { + o, _, err := unstructured.UnstructuredJSONScheme.Decode(json, nil, nil) + if err != nil { + return nil, fmt.Errorf("error parsing json into unstructured object: %v", err) + } + + u, ok := o.(*unstructured.Unstructured) + if !ok { + return nil, fmt.Errorf("parsed unexpected type %T", o) + } + + return NewK8sObject(u, json, nil), nil +} + +// ParseYAMLToK8sObject parses YAML to an Object. +func ParseYAMLToK8sObject(yaml []byte) (*K8sObject, error) { + objects, err := ParseK8sObjectsFromYAMLManifest(string(yaml)) + if err != nil { + return nil, err + } + if len(objects) > 1 { + return nil, fmt.Errorf("expect one object, actually: %d", len(objects)) + } + if len(objects) == 0 || objects[0] == nil { + return nil, fmt.Errorf("decoding object %v: %v", string(yaml), "no object found") + } + return objects[0], nil +} + +// UnstructuredObject exposes the raw object, primarily for testing +func (o *K8sObject) UnstructuredObject() *unstructured.Unstructured { + return o.object +} + +// ResolveK8sConflict - This method resolves k8s object possible +// conflicting settings. Which K8sObjects may need such method +// depends on the type of the K8sObject. +func (o *K8sObject) ResolveK8sConflict() *K8sObject { + if o.Kind == names.PDBStr { + return resolvePDBConflict(o) + } + return o +} + +// Unstructured exposes the raw object content, primarily for testing +func (o *K8sObject) Unstructured() map[string]any { + return o.UnstructuredObject().UnstructuredContent() +} + +// Container returns a container subtree for Deployment objects if one is found, or nil otherwise. +func (o *K8sObject) Container(name string) map[string]any { + u := o.Unstructured() + path := fmt.Sprintf("spec.template.spec.containers.[name:%s]", name) + node, f, err := tpath.GetPathContext(u, util.PathFromString(path), false) + if err == nil && f { + // Must be the type from the schema. + return node.Node.(map[string]any) + } + return nil +} + +// GroupVersionKind returns the GroupVersionKind for the K8sObject +func (o *K8sObject) GroupVersionKind() schema.GroupVersionKind { + return o.object.GroupVersionKind() +} + +// Version returns the APIVersion of the K8sObject +func (o *K8sObject) Version() string { + return o.object.GetAPIVersion() +} + +// Hash returns a unique hash for the K8sObject +func (o *K8sObject) Hash() string { + return Hash(o.Kind, o.Namespace, o.Name) +} + +// HashNameKind returns a hash for the K8sObject based on the name and kind only. +func (o *K8sObject) HashNameKind() string { + return HashNameKind(o.Kind, o.Name) +} + +// JSON returns a JSON representation of the K8sObject, using an internal cache. +func (o *K8sObject) JSON() ([]byte, error) { + if o.json != nil { + return o.json, nil + } + + b, err := o.object.MarshalJSON() + if err != nil { + return nil, err + } + return b, nil +} + +// YAML returns a YAML representation of the K8sObject, using an internal cache. +func (o *K8sObject) YAML() ([]byte, error) { + if o == nil { + return nil, nil + } + if o.yaml != nil { + return o.yaml, nil + } + oj, err := o.JSON() + if err != nil { + return nil, err + } + o.json = oj + y, err := yaml.JSONToYAML(oj) + if err != nil { + return nil, err + } + o.yaml = y + return y, nil +} + +// YAMLDebugString returns a YAML representation of the K8sObject, or an error string if the K8sObject cannot be rendered to YAML. +func (o *K8sObject) YAMLDebugString() string { + y, err := o.YAML() + if err != nil { + return err.Error() + } + return string(y) +} + +// K8sObjects holds a collection of k8s objects, so that we can filter / sequence them +type K8sObjects []*K8sObject + +// String implements the Stringer interface. +func (os K8sObjects) String() string { + var out []string + for _, oo := range os { + out = append(out, oo.YAMLDebugString()) + } + return strings.Join(out, helm.YAMLSeparator) +} + +// Keys returns a slice with the keys of os. +func (os K8sObjects) Keys() []string { + out := make([]string, 0, len(os)) + for _, oo := range os { + out = append(out, oo.Hash()) + } + return out +} + +// UnstructuredItems returns the list of items of unstructured.Unstructured. +func (os K8sObjects) UnstructuredItems() []unstructured.Unstructured { + usList := make([]unstructured.Unstructured, 0, len(os)) + for _, obj := range os { + usList = append(usList, *obj.UnstructuredObject()) + } + return usList +} + +// ParseK8sObjectsFromYAMLManifest returns a K8sObjects representation of manifest. +func ParseK8sObjectsFromYAMLManifest(manifest string) (K8sObjects, error) { + return ParseK8sObjectsFromYAMLManifestFailOption(manifest, true) +} + +// ParseK8sObjectsFromYAMLManifestFailOption returns a K8sObjects representation of manifest. Continues parsing when a bad object +// is found if failOnError is set to false. +func ParseK8sObjectsFromYAMLManifestFailOption(manifest string, failOnError bool) (K8sObjects, error) { + jsonDecoder := k8syaml.NewYAMLToJSONDecoder(bytes.NewReader([]byte(manifest))) + var objects K8sObjects + + wrapErr := func(err error) error { + return fmt.Errorf("failed to parse YAML to a k8s object: %v", err) + } + + s := json.NewSerializerWithOptions(json.DefaultMetaFactory, nil, nil, json.SerializerOptions{ + Yaml: true, + Pretty: true, + Strict: true, + }) + for { + var obj unstructured.Unstructured + if err := jsonDecoder.Decode(&obj); err != nil { + if err == io.EOF { + break + } + err = wrapErr(err) + if failOnError { + return nil, err + } + log.Error(err.Error()) + continue + } + if obj.Object == nil { + continue + } + + if !isValidKubernetesObject(obj) { + if failOnError { + err := wrapErr(fmt.Errorf("failed to parse YAML to a k8s object: object is an invalid k8s object: %v", obj)) + return nil, err + } + } + + // Convert the unstructured object back into YAML, without comments + var buf bytes.Buffer + if err := s.Encode(&obj, &buf); err != nil { + err = wrapErr(err) + if failOnError { + return nil, err + } + log.Error(err.Error()) + continue + } + cleanedYaml := buf.String() + + k8sObj := NewK8sObject(&obj, nil, []byte(cleanedYaml)) + if k8sObj.Valid() { + objects = append(objects, k8sObj) + } + } + return objects, nil +} + +// YAMLManifest returns a YAML representation of K8sObjects os. +func (os K8sObjects) YAMLManifest() (string, error) { + var b bytes.Buffer + + for i, item := range os { + if i != 0 { + if _, err := b.WriteString("\n\n"); err != nil { + return "", err + } + } + ym, err := item.YAML() + if err != nil { + return "", fmt.Errorf("error building yaml: %v", err) + } + if _, err := b.Write(ym); err != nil { + return "", err + } + if _, err := b.WriteString(YAMLSeparator); err != nil { + return "", err + } + + } + + return b.String(), nil +} + +// Sort will order the items in K8sObjects in order of score, group, kind, name. The intent is to +// have a deterministic ordering in which K8sObjects are applied. +func (os K8sObjects) Sort(score func(o *K8sObject) int) { + sort.Slice(os, func(i, j int) bool { + iScore := score(os[i]) + jScore := score(os[j]) + return iScore < jScore || + (iScore == jScore && + os[i].Group < os[j].Group) || + (iScore == jScore && + os[i].Group == os[j].Group && + os[i].Kind < os[j].Kind) || + (iScore == jScore && + os[i].Group == os[j].Group && + os[i].Kind == os[j].Kind && + os[i].Name < os[j].Name) + }) +} + +// ToMap returns a map of K8sObject hash to K8sObject. +func (os K8sObjects) ToMap() map[string]*K8sObject { + ret := make(map[string]*K8sObject) + for _, oo := range os { + if oo.Valid() { + ret[oo.Hash()] = oo + } + } + return ret +} + +// ToNameKindMap returns a map of K8sObject name/kind hash to K8sObject. +func (os K8sObjects) ToNameKindMap() map[string]*K8sObject { + ret := make(map[string]*K8sObject) + for _, oo := range os { + if oo.Valid() { + ret[oo.HashNameKind()] = oo + } + } + return ret +} + +// Valid checks returns true if Kind of K8sObject is not empty. +func (o *K8sObject) Valid() bool { + return o.Kind != "" +} + +// FullName returns namespace/name of K8s object +func (o *K8sObject) FullName() string { + return fmt.Sprintf("%s/%s", o.Namespace, o.Name) +} + +// Equal returns true if o and other are both valid and equal to each other. +func (o *K8sObject) Equal(other *K8sObject) bool { + if o == nil { + return other == nil + } + if other == nil { + return o == nil + } + + ay, err := o.YAML() + if err != nil { + return false + } + by, err := other.YAML() + if err != nil { + return false + } + + return util.IsYAMLEqual(string(ay), string(by)) +} + +// DefaultObjectOrder is default sorting function used to sort k8s objects. +func DefaultObjectOrder() func(o *K8sObject) int { + return func(o *K8sObject) int { + gk := o.Group + "/" + o.Kind + switch { + // Create CRDs asap - both because they are slow and because we will likely create instances of them soon + case gk == "apiextensions.k8s.io/CustomResourceDefinition": + return -1000 + + // We need to create ServiceAccounts, Roles before we bind them with a RoleBinding + case gk == "/ServiceAccount" || gk == "rbac.authorization.k8s.io/ClusterRole": + return 1 + case gk == "rbac.authorization.k8s.io/ClusterRoleBinding": + return 2 + + // validatingwebhookconfiguration is configured to FAIL-OPEN in the default install. For the + // re-install case we want to apply the validatingwebhookconfiguration first to reset any + // orphaned validatingwebhookconfiguration that is FAIL-CLOSE. + case gk == "admissionregistration.k8s.io/ValidatingWebhookConfiguration": + return 3 + + // Pods might need configmap or secrets - avoid backoff by creating them first + case gk == "/ConfigMap" || gk == "/Secrets": + return 100 + + // Create the pods after we've created other things they might be waiting for + case gk == "extensions/Deployment" || gk == "apps/Deployment": + return 1000 + + // Autoscalers typically act on a deployment + case gk == "autoscaling/HorizontalPodAutoscaler": + return 1001 + + // Create services late - after pods have been started + case gk == "/Service": + return 10000 + + default: + return 1000 + } + } +} + +func ObjectsNotInLists(objects K8sObjects, lists ...K8sObjects) K8sObjects { + var ret K8sObjects + + filterMap := make(map[*K8sObject]bool) + for _, list := range lists { + for _, object := range list { + filterMap[object] = true + } + } + + for _, o := range objects { + if !filterMap[o] { + ret = append(ret, o) + } + } + return ret +} + +// KindObjects returns the subset of objs with the given kind. +func KindObjects(objs K8sObjects, kind string) K8sObjects { + var ret K8sObjects + for _, o := range objs { + if o.Kind == kind { + ret = append(ret, o) + } + } + return ret +} + +// ParseK8SYAMLToIstioOperator parses a IstioOperator CustomResource YAML string and unmarshals in into +// an IstioOperatorSpec object. It returns the object and an API group/version with it. +func ParseK8SYAMLToIstioOperator(yml string) (*v1alpha1.IstioOperator, *schema.GroupVersionKind, error) { + o, err := ParseYAMLToK8sObject([]byte(yml)) + if err != nil { + return nil, nil, err + } + iop := &v1alpha1.IstioOperator{} + if err := yaml.UnmarshalStrict([]byte(yml), iop); err != nil { + return nil, nil, err + } + gvk := o.GroupVersionKind() + v1alpha1.SetNamespace(iop.Spec, o.Namespace) + return iop, &gvk, nil +} + +// AllObjectHashes returns a map with object hashes of all the objects contained in cmm as the keys. +func AllObjectHashes(m string) map[string]bool { + ret := make(map[string]bool) + objs, err := ParseK8sObjectsFromYAMLManifest(m) + if err != nil { + log.Error(err.Error()) + } + for _, o := range objs { + ret[o.Hash()] = true + } + + return ret +} + +// resolvePDBConflict When user uses both minAvailable and +// maxUnavailable to configure istio instances, these two +// parameters are mutually exclusive, care must be taken +// to resolve the issue +func resolvePDBConflict(o *K8sObject) *K8sObject { + if o.json == nil { + return o + } + if o.object.Object["spec"] == nil { + return o + } + spec := o.object.Object["spec"].(map[string]any) + isDefault := func(item any) bool { + var ii intstr.IntOrString + switch item := item.(type) { + case int: + ii = intstr.FromInt32(int32(item)) + case int64: + ii = intstr.FromInt32(int32(item)) + case string: + ii = intstr.FromString(item) + default: + ii = intstr.FromInt32(0) + } + intVal, err := intstr.GetScaledValueFromIntOrPercent(&ii, 100, false) + if err != nil || intVal == 0 { + return true + } + return false + } + if spec["maxUnavailable"] != nil && spec["minAvailable"] != nil { + // When both maxUnavailable and minAvailable present and + // neither has value 0, this is considered a conflict, + // then maxUnavailable will take precedence. + if !isDefault(spec["maxUnavailable"]) && !isDefault(spec["minAvailable"]) { + delete(spec, "minAvailable") + // Make sure that the json and yaml representation of the object + // is consistent with the changed object + o.json = nil + o.json, _ = o.JSON() + if o.yaml != nil { + o.yaml = nil + o.yaml, _ = o.YAML() + } + } + } + return o +} + +func isValidKubernetesObject(obj unstructured.Unstructured) bool { + if _, ok := obj.Object["apiVersion"]; !ok { + return false + } + if _, ok := obj.Object["kind"]; !ok { + return false + } + return true +} diff --git a/operator/pkg/object/objects_test.go b/operator/pkg/object/objects_test.go new file mode 100644 index 0000000..fb7fd7f --- /dev/null +++ b/operator/pkg/object/objects_test.go @@ -0,0 +1,713 @@ +// Copyright Istio 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 object + +import ( + "fmt" + "io" + "os" + "reflect" + "strings" + "testing" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + k8syaml "k8s.io/apimachinery/pkg/util/yaml" + + "github.com/jehawley/istio/operator/pkg/util" + "istio.io/istio/pkg/test/util/assert" +) + +func TestHash(t *testing.T) { + hashTests := []struct { + desc string + kind string + namespace string + name string + want string + }{ + {"CalculateHashForObjectWithNormalCharacter", "Service", "default", "ingressgateway", "Service:default:ingressgateway"}, + {"CalculateHashForObjectWithDash", "Deployment", "istio-system", "istio-pilot", "Deployment:istio-system:istio-pilot"}, + {"CalculateHashForObjectWithDot", "ConfigMap", "istio-system", "my.config", "ConfigMap:istio-system:my.config"}, + } + + for _, tt := range hashTests { + t.Run(tt.desc, func(t *testing.T) { + got := Hash(tt.kind, tt.namespace, tt.name) + if got != tt.want { + t.Errorf("Hash(%s): got %s for kind %s, namespace %s, name %s, want %s", tt.desc, got, tt.kind, tt.namespace, tt.name, tt.want) + } + }) + } +} + +func TestFromHash(t *testing.T) { + hashTests := []struct { + desc string + hash string + kind string + namespace string + name string + }{ + {"ParseHashWithNormalCharacter", "Service:default:ingressgateway", "Service", "default", "ingressgateway"}, + {"ParseHashForObjectWithDash", "Deployment:istio-system:istio-pilot", "Deployment", "istio-system", "istio-pilot"}, + {"ParseHashForObjectWithDot", "ConfigMap:istio-system:my.config", "ConfigMap", "istio-system", "my.config"}, + {"InvalidHash", "test", "Bad hash string: test", "", ""}, + } + + for _, tt := range hashTests { + t.Run(tt.desc, func(t *testing.T) { + k, ns, name := FromHash(tt.hash) + if k != tt.kind || ns != tt.namespace || name != tt.name { + t.Errorf("FromHash(%s): got kind %s, namespace %s, name %s, want kind %s, namespace %s, name %s", tt.desc, k, ns, name, tt.kind, tt.namespace, tt.name) + } + }) + } +} + +func TestHashNameKind(t *testing.T) { + hashNameKindTests := []struct { + desc string + kind string + name string + want string + }{ + {"CalculateHashNameKindForObjectWithNormalCharacter", "Service", "ingressgateway", "Service:ingressgateway"}, + {"CalculateHashNameKindForObjectWithDash", "Deployment", "istio-pilot", "Deployment:istio-pilot"}, + {"CalculateHashNameKindForObjectWithDot", "ConfigMap", "my.config", "ConfigMap:my.config"}, + } + + for _, tt := range hashNameKindTests { + t.Run(tt.desc, func(t *testing.T) { + got := HashNameKind(tt.kind, tt.name) + if got != tt.want { + t.Errorf("HashNameKind(%s): got %s for kind %s, name %s, want %s", tt.desc, got, tt.kind, tt.name, tt.want) + } + }) + } +} + +func TestParseJSONToK8sObject(t *testing.T) { + testDeploymentJSON := `{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": { + "name": "istio-citadel", + "namespace": "istio-system", + "labels": { + "istio": "citadel" + } + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "istio": "citadel" + } + }, + "template": { + "metadata": { + "labels": { + "istio": "citadel" + } + }, + "spec": { + "containers": [ + { + "name": "citadel", + "image": "docker.io/istio/citadel:1.1.8", + "args": [ + "--append-dns-names=true", + "--grpc-port=8060", + "--grpc-hostname=citadel", + "--citadel-storage-namespace=istio-system", + "--custom-dns-names=istio-pilot-service-account.istio-system:istio-pilot.istio-system", + "--monitoring-port=15014", + "--self-signed-ca=true" + ] + } + ] + } + } + } +}` + testPodJSON := `{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "name": "istio-galley-75bcd59768-hpt5t", + "namespace": "istio-system", + "labels": { + "istio": "galley" + } + }, + "spec": { + "containers": [ + { + "name": "galley", + "image": "docker.io/istio/galley:1.1.8", + "command": [ + "/usr/local/bin/galley", + "server", + "--meshConfigFile=/etc/mesh-config/mesh", + "--livenessProbeInterval=1s", + "--livenessProbePath=/healthliveness", + "--readinessProbePath=/healthready", + "--readinessProbeInterval=1s", + "--deployment-namespace=istio-system", + "--insecure=true", + "--validation-webhook-config-file", + "/etc/config/validatingwebhookconfiguration.yaml", + "--monitoringPort=15014", + "--log_output_level=default:info" + ], + "ports": [ + { + "containerPort": 443, + "protocol": "TCP" + }, + { + "containerPort": 15014, + "protocol": "TCP" + }, + { + "containerPort": 9901, + "protocol": "TCP" + } + ] + } + ] + } +}` + testServiceJSON := `{ + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "labels": { + "app": "pilot" + }, + "name": "istio-pilot", + "namespace": "istio-system" + }, + "spec": { + "clusterIP": "10.102.230.31", + "ports": [ + { + "name": "grpc-xds", + "port": 15010, + "protocol": "TCP", + "targetPort": 15010 + }, + { + "name": "https-xds", + "port": 15011, + "protocol": "TCP", + "targetPort": 15011 + }, + { + "name": "http-legacy-discovery", + "port": 8080, + "protocol": "TCP", + "targetPort": 8080 + }, + { + "name": "http-monitoring", + "port": 15014, + "protocol": "TCP", + "targetPort": 15014 + } + ], + "selector": { + "istio": "pilot" + }, + "sessionAffinity": "None", + "type": "ClusterIP" + } +}` + + testInvalidJSON := `invalid json` + + parseJSONToK8sObjectTests := []struct { + desc string + objString string + wantGroup string + wantKind string + wantName string + wantNamespace string + wantErr bool + }{ + {"ParseJsonToK8sDeployment", testDeploymentJSON, "apps", "Deployment", "istio-citadel", "istio-system", false}, + {"ParseJsonToK8sPod", testPodJSON, "", "Pod", "istio-galley-75bcd59768-hpt5t", "istio-system", false}, + {"ParseJsonToK8sService", testServiceJSON, "", "Service", "istio-pilot", "istio-system", false}, + {"ParseJsonError", testInvalidJSON, "", "", "", "", true}, + } + + for _, tt := range parseJSONToK8sObjectTests { + t.Run(tt.desc, func(t *testing.T) { + k8sObj, err := ParseJSONToK8sObject([]byte(tt.objString)) + if err == nil { + if tt.wantErr { + t.Errorf("ParseJsonToK8sObject(%s): should be error", tt.desc) + } + k8sObjStr := k8sObj.YAMLDebugString() + if k8sObj.Group != tt.wantGroup { + t.Errorf("ParseJsonToK8sObject(%s): got group %s for k8s object %s, want %s", tt.desc, k8sObj.Group, k8sObjStr, tt.wantGroup) + } + if k8sObj.Kind != tt.wantKind { + t.Errorf("ParseJsonToK8sObject(%s): got kind %s for k8s object %s, want %s", tt.desc, k8sObj.Kind, k8sObjStr, tt.wantKind) + } + if k8sObj.Name != tt.wantName { + t.Errorf("ParseJsonToK8sObject(%s): got name %s for k8s object %s, want %s", tt.desc, k8sObj.Name, k8sObjStr, tt.wantName) + } + if k8sObj.Namespace != tt.wantNamespace { + t.Errorf("ParseJsonToK8sObject(%s): got group %s for k8s object %s, want %s", tt.desc, k8sObj.Namespace, k8sObjStr, tt.wantNamespace) + } + } else if !tt.wantErr { + t.Errorf("ParseJsonToK8sObject(%s): got unexpected error: %v", tt.desc, err) + } + }) + } +} + +func TestParseK8sObjectsFromYAMLManifest(t *testing.T) { + testDeploymentYaml := `apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system + labels: + istio: citadel +spec: + replicas: 1 + selector: + matchLabels: + istio: citadel + template: + metadata: + labels: + istio: citadel + spec: + containers: + - name: citadel + image: docker.io/istio/citadel:1.1.8 + args: + - "--append-dns-names=true" + - "--grpc-port=8060" + - "--grpc-hostname=citadel" + - "--citadel-storage-namespace=istio-system" + - "--custom-dns-names=istio-pilot-service-account.istio-system:istio-pilot.istio-system" + - "--monitoring-port=15014" + - "--self-signed-ca=true"` + + testPodYaml := `apiVersion: v1 +kind: Pod +metadata: + name: istio-galley-75bcd59768-hpt5t + namespace: istio-system + labels: + istio: galley +spec: + containers: + - name: galley + image: docker.io/istio/galley:1.1.8 + command: + - "/usr/local/bin/galley" + - server + - "--meshConfigFile=/etc/mesh-config/mesh" + - "--livenessProbeInterval=1s" + - "--livenessProbePath=/healthliveness" + - "--readinessProbePath=/healthready" + - "--readinessProbeInterval=1s" + - "--deployment-namespace=istio-system" + - "--insecure=true" + - "--validation-webhook-config-file" + - "/etc/config/validatingwebhookconfiguration.yaml" + - "--monitoringPort=15014" + - "--log_output_level=default:info" + ports: + - containerPort: 443 + protocol: TCP + - containerPort: 15014 + protocol: TCP + - containerPort: 9901 + protocol: TCP` + + testServiceYaml := `apiVersion: v1 +kind: Service +metadata: + labels: + app: pilot + name: istio-pilot + namespace: istio-system +spec: + clusterIP: 10.102.230.31 + ports: + - name: grpc-xds + port: 15010 + protocol: TCP + targetPort: 15010 + - name: https-xds + port: 15011 + protocol: TCP + targetPort: 15011 + - name: http-legacy-discovery + port: 8080 + protocol: TCP + targetPort: 8080 + - name: http-monitoring + port: 15014 + protocol: TCP + targetPort: 15014 + selector: + istio: pilot + sessionAffinity: None + type: ClusterIP` + + parseK8sObjectsFromYAMLManifestTests := []struct { + desc string + objsMap map[string]string + }{ + { + "FromHybridYAMLManifest", + map[string]string{ + "Deployment:istio-system:istio-citadel": testDeploymentYaml, + "Pod:istio-system:istio-galley-75bcd59768-hpt5t": testPodYaml, + "Service:istio-system:istio-pilot": testServiceYaml, + }, + }, + } + + for _, tt := range parseK8sObjectsFromYAMLManifestTests { + t.Run(tt.desc, func(t *testing.T) { + testManifestYaml := strings.Join([]string{testDeploymentYaml, testPodYaml, testServiceYaml}, YAMLSeparator) + gotK8sObjs, err := ParseK8sObjectsFromYAMLManifest(testManifestYaml) + if err != nil { + gotK8sObjsMap := gotK8sObjs.ToMap() + for objHash, want := range tt.objsMap { + if gotObj, ok := gotK8sObjsMap[objHash]; ok { + gotObjYaml := gotObj.YAMLDebugString() + if !util.IsYAMLEqual(gotObjYaml, want) { + t.Errorf("ParseK8sObjectsFromYAMLManifest(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, gotObjYaml, want, util.YAMLDiff(gotObjYaml, want)) + } + } + } + } + }) + } +} + +func TestK8sObject_Equal(t *testing.T) { + obj1 := K8sObject{ + object: &unstructured.Unstructured{Object: map[string]any{ + "key": "value1", + }}, + } + obj2 := K8sObject{ + object: &unstructured.Unstructured{Object: map[string]any{ + "key": "value2", + }}, + } + cases := []struct { + desc string + o1 *K8sObject + o2 *K8sObject + want bool + }{ + { + desc: "Equals", + o1: &obj1, + o2: &obj1, + want: true, + }, + { + desc: "NotEquals", + o1: &obj1, + o2: &obj2, + want: false, + }, + { + desc: "NilSource", + o1: nil, + o2: &obj2, + want: false, + }, + { + desc: "NilDest", + o1: &obj1, + o2: nil, + want: false, + }, + { + desc: "TwoNils", + o1: nil, + o2: nil, + want: true, + }, + } + for _, tt := range cases { + t.Run(tt.desc, func(t *testing.T) { + res := tt.o1.Equal(tt.o2) + if res != tt.want { + t.Errorf("got %v, want: %v", res, tt.want) + } + }) + } +} + +func TestK8sObject_ResolveK8sConflict(t *testing.T) { + getK8sObject := func(ystr string) *K8sObject { + o, err := ParseYAMLToK8sObject([]byte(ystr)) + if err != nil { + panic(err) + } + // Ensure that json data is in sync. + // Since the object was created using yaml, json is empty. + // make sure the object json is set correctly. + o.json, _ = o.JSON() + return o + } + + cases := []struct { + desc string + o1 *K8sObject + o2 *K8sObject + }{ + { + desc: "not applicable kind", + o1: getK8sObject(` + apiVersion: v1 + kind: Service + metadata: + labels: + app: pilot + name: istio-pilot + namespace: istio-system + spec: + clusterIP: 10.102.230.31`), + o2: getK8sObject(` + apiVersion: v1 + kind: Service + metadata: + labels: + app: pilot + name: istio-pilot + namespace: istio-system + spec: + clusterIP: 10.102.230.31`), + }, + { + desc: "only minAvailable is set", + o1: getK8sObject(` + apiVersion: policy/v1 + kind: PodDisruptionBudget + metadata: + name: zk-pdb + spec: + minAvailable: 2`), + o2: getK8sObject(` + apiVersion: policy/v1 + kind: PodDisruptionBudget + metadata: + name: zk-pdb + spec: + minAvailable: 2`), + }, + { + desc: "only maxUnavailable is set", + o1: getK8sObject(` + apiVersion: policy/v1 + kind: PodDisruptionBudget + metadata: + name: istio + spec: + maxUnavailable: 3`), + o2: getK8sObject(` + apiVersion: policy/v1 + kind: PodDisruptionBudget + metadata: + name: istio + spec: + maxUnavailable: 3`), + }, + { + desc: "minAvailable and maxUnavailable are set to none zero values", + o1: getK8sObject(` + apiVersion: policy/v1 + kind: PodDisruptionBudget + metadata: + name: istio + spec: + maxUnavailable: 50% + minAvailable: 3`), + o2: getK8sObject(` + apiVersion: policy/v1 + kind: PodDisruptionBudget + metadata: + name: istio + spec: + maxUnavailable: 50%`), + }, + { + desc: "both minAvailable and maxUnavailable are set default", + o1: getK8sObject(` + apiVersion: policy/v1 + kind: PodDisruptionBudget + metadata: + name: istio + spec: + minAvailable: 0 + maxUnavailable: 0`), + o2: getK8sObject(` + apiVersion: policy/v1 + kind: PodDisruptionBudget + metadata: + name: istio + spec: + maxUnavailable: 0 + minAvailable: 0`), + }, + } + for _, tt := range cases { + t.Run(tt.desc, func(t *testing.T) { + newObj := tt.o1.ResolveK8sConflict() + if !newObj.Equal(tt.o2) { + newObjjson, _ := newObj.JSON() + wantedObjjson, _ := tt.o2.JSON() + t.Errorf("Got: %s, want: %s", string(newObjjson), string(wantedObjjson)) + } + }) + } +} + +func TestParseK8sObjectsFromYAMLManifestFailOption(t *testing.T) { + cases := []struct { + name string + input string + failOnError bool + expectErr bool + expectCount int + expectOut bool + }{ + { + name: "well formed yaml, no errors", + input: "well-formed", + failOnError: false, + expectErr: false, + expectCount: 2, + }, + { + name: "malformed yaml, fail on error", + input: "malformed", + failOnError: true, + expectErr: true, + expectCount: 0, + }, + { + name: "malformed yaml, continue on error", + input: "malformed", + failOnError: false, + expectErr: false, + expectCount: 1, + }, + { + name: "space in the end of the manifest", + input: "well-formed-with-space", + expectCount: 1, + expectOut: true, + }, + { + name: "some random comments", + input: "well-formed-with-comments", + expectCount: 1, + expectOut: true, + }, + { + name: "invalid k8s object - missing kind", + input: "invalid", + failOnError: true, + expectErr: true, + expectCount: 0, + }, + { + name: "invalid k8s object - missing kind - skip error", + input: "invalid", + failOnError: false, + expectErr: false, + expectCount: 0, + }, + { + name: "empty object - do not have errors", + input: "empty", + failOnError: true, + expectErr: false, + expectCount: 0, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + inputFileName := fmt.Sprintf("testdata/%s.yaml", tc.input) + inputFile, err := os.Open(inputFileName) + if err != nil { + t.Errorf("error opening test data file: %v", err) + } + defer inputFile.Close() + manifest, err := io.ReadAll(inputFile) + if err != nil { + t.Errorf("error reading test data file: %v", err) + } + objects, err := ParseK8sObjectsFromYAMLManifestFailOption(string(manifest), tc.failOnError) + if tc.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tc.expectCount, len(objects)) + if tc.expectOut { + outputFileName := fmt.Sprintf("testdata/%s.out.yaml", tc.input) + outputFile, err := os.Open(outputFileName) + if err != nil { + t.Errorf("error opening test data file: %v", err) + } + defer outputFile.Close() + expectedYAML, err := io.ReadAll(outputFile) + if err != nil { + t.Errorf("error reading test data file: %v", err) + } + expectedYAMLs := strings.Split(string(expectedYAML), "---") + if len(expectedYAMLs) != len(objects) { + t.Errorf("expected %d objects, got %d", len(expectedYAMLs), len(objects)) + } + for i, obj := range objects { + assert.Equal(t, true, compareYAMLContent(string(obj.yaml), expectedYAMLs[i])) + } + } + }) + } +} + +// compareYAMLContent compares two yaml resources and returns true if they are equal. If they have same content but different +// order of fields, it will return true as well. +func compareYAMLContent(yaml1, yaml2 string) bool { + var obj1, obj2 interface{} + err := k8syaml.Unmarshal([]byte(yaml1), &obj1) + if err != nil { + return false + } + err = k8syaml.Unmarshal([]byte(yaml2), &obj2) + if err != nil { + return false + } + return reflect.DeepEqual(obj1, obj2) +} diff --git a/operator/pkg/object/testdata/empty.yaml b/operator/pkg/object/testdata/empty.yaml new file mode 100644 index 0000000..a427fa1 --- /dev/null +++ b/operator/pkg/object/testdata/empty.yaml @@ -0,0 +1,2 @@ +# comments + # another comments diff --git a/operator/pkg/object/testdata/invalid.yaml b/operator/pkg/object/testdata/invalid.yaml new file mode 100644 index 0000000..bcd855c --- /dev/null +++ b/operator/pkg/object/testdata/invalid.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +metadata: + name: myconfigmap + namespace: default +data: + mydata: |- + First line of data + Second line of data diff --git a/operator/pkg/object/testdata/malformed.yaml b/operator/pkg/object/testdata/malformed.yaml new file mode 100644 index 0000000..8c6ef9f --- /dev/null +++ b/operator/pkg/object/testdata/malformed.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Pod +metadata: + name: mypod + namespace: default +spec: + containers: + - name: mycontainer + image: nginx +--- +apiVersion: v1 +metadata: + name: myservice + namespace: default +spec: + selector: + app: MyApp diff --git a/operator/pkg/object/testdata/well-formed-with-comments.out.yaml b/operator/pkg/object/testdata/well-formed-with-comments.out.yaml new file mode 100644 index 0000000..86c4e69 --- /dev/null +++ b/operator/pkg/object/testdata/well-formed-with-comments.out.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: myconfigmap + namespace: default +data: + mydata: |- + First line of data # some random comments should not be deleted + Second line of data diff --git a/operator/pkg/object/testdata/well-formed-with-comments.yaml b/operator/pkg/object/testdata/well-formed-with-comments.yaml new file mode 100644 index 0000000..1cdec5c --- /dev/null +++ b/operator/pkg/object/testdata/well-formed-with-comments.yaml @@ -0,0 +1,12 @@ +# some random comments + # some random comments +apiVersion: v1 +kind: ConfigMap +metadata: + name: myconfigmap + namespace: default # some random comments should be deleted +data: + mydata: |- + First line of data # some random comments should not be deleted + Second line of data +# some random comments diff --git a/operator/pkg/object/testdata/well-formed-with-space.out.yaml b/operator/pkg/object/testdata/well-formed-with-space.out.yaml new file mode 100644 index 0000000..a31ea60 --- /dev/null +++ b/operator/pkg/object/testdata/well-formed-with-space.out.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: myconfigmap + namespace: default +data: + mydata: "First line of data\nSecond line of data " diff --git a/operator/pkg/object/testdata/well-formed-with-space.yaml b/operator/pkg/object/testdata/well-formed-with-space.yaml new file mode 100644 index 0000000..07d4678 --- /dev/null +++ b/operator/pkg/object/testdata/well-formed-with-space.yaml @@ -0,0 +1,8 @@ +# there are some spaces at the end +apiVersion: v1 +kind: ConfigMap +metadata: + name: myconfigmap + namespace: default +data: + mydata: "First line of data\nSecond line of data " diff --git a/operator/pkg/object/testdata/well-formed.yaml b/operator/pkg/object/testdata/well-formed.yaml new file mode 100644 index 0000000..b814c44 --- /dev/null +++ b/operator/pkg/object/testdata/well-formed.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Pod +metadata: + name: mypod + namespace: default +spec: + containers: + - name: mycontainer + image: nginx +--- +apiVersion: v1 +kind: Service +metadata: + name: myservice + namespace: default +spec: + selector: + app: MyApp + ports: + - protocol: TCP + port: 80 + targetPort: 9376 diff --git a/operator/pkg/patch/patch.go b/operator/pkg/patch/patch.go new file mode 100644 index 0000000..f3ec151 --- /dev/null +++ b/operator/pkg/patch/patch.go @@ -0,0 +1,211 @@ +// Copyright Istio 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 patch implements a simple patching mechanism for k8s resources. +Paths are specified in the form a.b.c.[key:value].d.[list_entry_value], where: + - [key:value] selects a list entry in list c which contains an entry with key:value + - [list_entry_value] selects a list entry in list d which is a regex match of list_entry_value. + +Some examples are given below. Given a resource: + + kind: Deployment + metadata: + name: istio-citadel + namespace: istio-system + a: + b: + - name: n1 + value: v1 + - name: n2 + list: + - "vv1" + - vv2=foo + +values and list entries can be added, modified or deleted. + +# MODIFY + +1. set v1 to v1new + + path: a.b.[name:n1].value + value: v1new + +2. set vv1 to vv3 + + // Note the lack of quotes around vv1 (see NOTES below). + path: a.b.[name:n2].list.[vv1] + value: vv3 + +3. set vv2=foo to vv2=bar (using regex match) + + path: a.b.[name:n2].list.[vv2] + value: vv2=bar + +4. replace a port whose port was 15010 + + - path: spec.ports.[port:15010] + value: + port: 15020 + name: grpc-xds + protocol: TCP + +# DELETE + +1. Delete container with name: n1 + + path: a.b.[name:n1] + +2. Delete list value vv1 + + path: a.b.[name:n2].list.[vv1] + +# ADD + +1. Add vv3 to list + + path: a.b.[name:n2].list.[1000] + value: vv3 + +Note: the value 1000 is an example. That value used in the patch should +be a value greater than number of the items in the list. Choose 1000 is +just an example which normally is greater than the most of the lists used. + +2. Add new key:value to container name: n1 + + path: a.b.[name:n1] + value: + new_attr: v3 + +*NOTES* +- Due to loss of string quoting during unmarshaling, keys and values should not be string quoted, even if they appear +that way in the object being patched. +- [key:value] treats ':' as a special separator character. Any ':' in the key or value string must be escaped as \:. +*/ +package patch + +import ( + "fmt" + "strings" + + yaml2 "gopkg.in/yaml.v2" + + "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/helm" + "github.com/jehawley/istio/operator/pkg/metrics" + "github.com/jehawley/istio/operator/pkg/object" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" + "istio.io/istio/pkg/log" +) + +var scope = log.RegisterScope("patch", "patch") + +// overlayMatches reports whether obj matches the overlay for either the default namespace or no namespace (cluster scope). +func overlayMatches(overlay *v1alpha1.K8SObjectOverlay, obj *object.K8sObject, defaultNamespace string) bool { + oh := obj.Hash() + if oh == object.Hash(overlay.Kind, defaultNamespace, overlay.Name) || + oh == object.Hash(overlay.Kind, "", overlay.Name) { + return true + } + return false +} + +// YAMLManifestPatch patches a base YAML in the given namespace with a list of overlays. +// Each overlay has the format described in the K8SObjectOverlay definition. +// It returns the patched manifest YAML. +func YAMLManifestPatch(baseYAML string, defaultNamespace string, overlays []*v1alpha1.K8SObjectOverlay) (string, error) { + var ret strings.Builder + var errs util.Errors + objs, err := object.ParseK8sObjectsFromYAMLManifest(baseYAML) + if err != nil { + return "", err + } + + matches := make(map[*v1alpha1.K8SObjectOverlay]object.K8sObjects) + // Try to apply the defined overlays. + for _, obj := range objs { + oy, err := obj.YAML() + if err != nil { + errs = util.AppendErr(errs, fmt.Errorf("object to YAML error (%s) for base object: \n%s", err, obj.YAMLDebugString())) + continue + } + oys := string(oy) + for _, overlay := range overlays { + if overlayMatches(overlay, obj, defaultNamespace) { + matches[overlay] = append(matches[overlay], obj) + var errs2 util.Errors + oys, errs2 = applyPatches(obj, overlay.Patches) + errs = util.AppendErrs(errs, errs2) + } + } + if _, err := ret.WriteString(oys + helm.YAMLSeparator); err != nil { + errs = util.AppendErr(errs, fmt.Errorf("writeString: %s", err)) + } + } + + for _, overlay := range overlays { + // Each overlay should have exactly one match in the output manifest. + switch { + case len(matches[overlay]) == 0: + errs = util.AppendErr(errs, fmt.Errorf("overlay for %s:%s does not match any object in output manifest. Available objects are:\n%s", + overlay.Kind, overlay.Name, strings.Join(objs.Keys(), "\n"))) + case len(matches[overlay]) > 1: + errs = util.AppendErr(errs, fmt.Errorf("overlay for %s:%s matches multiple objects in output manifest:\n%s", + overlay.Kind, overlay.Name, strings.Join(objs.Keys(), "\n"))) + } + } + + return ret.String(), errs.ToError() +} + +// applyPatches applies the given patches against the given object. It returns the resulting patched YAML if successful, +// or a list of errors otherwise. +func applyPatches(base *object.K8sObject, patches []*v1alpha1.K8SObjectOverlay_PathValue) (outYAML string, errs util.Errors) { + bo := make(map[any]any) + by, err := base.YAML() + if err != nil { + return "", util.NewErrs(err) + } + // Use yaml2 specifically to allow interface{} as key which WritePathContext treats specially + err = yaml2.Unmarshal(by, &bo) + if err != nil { + return "", util.NewErrs(err) + } + for _, p := range patches { + v := p.Value.AsInterface() + if strings.TrimSpace(p.Path) == "" { + scope.Warnf("value=%s has empty path, skip\n", v) + continue + } + scope.Debugf("applying path=%s, value=%s\n", p.Path, v) + inc, _, err := tpath.GetPathContext(bo, util.PathFromString(p.Path), true) + if err != nil { + errs = util.AppendErr(errs, err) + metrics.ManifestPatchErrorTotal.Increment() + continue + } + + err = tpath.WritePathContext(inc, v, false) + if err != nil { + errs = util.AppendErr(errs, err) + metrics.ManifestPatchErrorTotal.Increment() + } + } + oy, err := yaml2.Marshal(bo) + if err != nil { + return "", util.AppendErr(errs, err) + } + return string(oy), errs +} diff --git a/operator/pkg/patch/patch_test.go b/operator/pkg/patch/patch_test.go new file mode 100644 index 0000000..779d199 --- /dev/null +++ b/operator/pkg/patch/patch_test.go @@ -0,0 +1,517 @@ +// Copyright Istio 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 patch + +import ( + "fmt" + "testing" + + "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/util" +) + +func TestPatchYAMLManifestSuccess(t *testing.T) { + base := ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +a: + b: + - name: n1 + value: v1 + - name: n2 + list: + - v1 + - v2 + - v3_regex + c: +` + tests := []struct { + desc string + path string + value string + want string + wantErr string + }{ + { + desc: "ModifyListEntryValue", + path: `a.b.[name:n1].value`, + value: `v2`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +a: + b: + - name: n1 + value: v2 + - list: + - v1 + - v2 + - v3_regex + name: n2 + c: +`, + }, + { + desc: "ModifyListEntryValueQuoted", + path: `a.b.[name:n1].value`, + value: `v2`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +a: + b: + - name: "n1" + value: v2 + - list: + - v1 + - v2 + - v3_regex + name: n2 + c: +`, + }, + { + desc: "ModifyListEntry", + path: `a.b.[name:n2].list.[v2]`, + value: `v3`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +a: + b: + - name: n1 + value: v1 + - list: + - v1 + - v3 + - v3_regex + name: n2 + c: +`, + }, + { + desc: "DeleteListEntry", + path: `a.b.[name:n1]`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +a: + b: + - list: + - v1 + - v2 + - v3_regex + name: n2 + c: +`, + }, + { + desc: "DeleteListEntryValue", + path: `a.b.[name:n2].list.[v2]`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +a: + b: + - name: n1 + value: v1 + - list: + - v1 + - v3_regex + name: n2 + c: +`, + }, + { + desc: "DeleteListEntryValueRegex", + path: `a.b.[name:n2].list.[v3]`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +a: + b: + - name: n1 + value: v1 + - list: + - v1 + - v2 + name: n2 + c: +`, + }, + { + desc: "UpdateNullNode", + path: `a.c`, + value: ` + d: n3`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +a: + b: + - name: n1 + value: v1 + - name: n2 + list: + - v1 + - v2 + - v3_regex + c: + d: n3 +`, + }, + { + desc: "AppendToListEntry", + path: `a.b.[name:n2].list.[3]`, + value: `v4`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +a: + b: + - name: n1 + value: v1 + - list: + - v1 + - v2 + - v3_regex + - v4 + name: n2 + c: +`, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + rc := &v1alpha1.KubernetesResourcesSpec{} + oh := makeOverlayHeader(tt.path, tt.value) + err := util.UnmarshalWithJSONPB(oh, rc, false) + if err != nil { + t.Fatalf("unmarshalWithJSONPB(%s): got error %s for string:\n%s\n", tt.desc, err, oh) + } + got, err := YAMLManifestPatch(base, "istio-system", rc.Overlays) + if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { + t.Fatalf("YAMLManifestPatch(%s): gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) + } + if want := tt.want; !util.IsYAMLEqual(got, want) { + t.Errorf("YAMLManifestPatch(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, got, want, util.YAMLDiff(got, want)) + } + }) + } +} + +func TestPatchYAMLManifestRealYAMLSuccess(t *testing.T) { + base := ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +spec: + template: + spec: + containers: + - name: deleteThis + foo: bar + - name: galley + ports: + - containerPort: 443 + - containerPort: 15014 + - containerPort: 9901 + command: + - /usr/local/bin/galley + - server + - --meshConfigFile=/etc/mesh-config/mesh + - --livenessProbeInterval=1s + - --validation-webhook-config-file +` + + tests := []struct { + desc string + path string + value string + want string + wantErr string + }{ + { + desc: "DeleteLeafListLeaf", + path: `spec.template.spec.containers.[name:galley].command.[--validation-webhook-config-file]`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +spec: + template: + spec: + containers: + - foo: bar + name: deleteThis + - command: + - /usr/local/bin/galley + - server + - --meshConfigFile=/etc/mesh-config/mesh + - --livenessProbeInterval=1s + name: galley + ports: + - containerPort: 443 + - containerPort: 15014 + - containerPort: 9901 +`, + }, + { + desc: "UpdateListItem", + path: `spec.template.spec.containers.[name:galley].command.[--livenessProbeInterval]`, + value: `--livenessProbeInterval=1111s`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +spec: + template: + spec: + containers: + - foo: bar + name: deleteThis + - command: + - /usr/local/bin/galley + - server + - --meshConfigFile=/etc/mesh-config/mesh + - --livenessProbeInterval=1111s + - --validation-webhook-config-file + name: galley + ports: + - containerPort: 443 + - containerPort: 15014 + - containerPort: 9901 +`, + }, + { + desc: "UpdateLeaf", + path: `spec.template.spec.containers.[name:galley].ports.[containerPort:15014].containerPort`, + value: `22222`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +spec: + template: + spec: + containers: + - foo: bar + name: deleteThis + - command: + - /usr/local/bin/galley + - server + - --meshConfigFile=/etc/mesh-config/mesh + - --livenessProbeInterval=1s + - --validation-webhook-config-file + name: galley + ports: + - containerPort: 443 + - containerPort: 22222 + - containerPort: 9901 +`, + }, + { + desc: "DeleteLeafList", + path: `spec.template.spec.containers.[name:galley].ports.[containerPort:9901]`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +spec: + template: + spec: + containers: + - foo: bar + name: deleteThis + - command: + - /usr/local/bin/galley + - server + - --meshConfigFile=/etc/mesh-config/mesh + - --livenessProbeInterval=1s + - --validation-webhook-config-file + name: galley + ports: + - containerPort: 443 + - containerPort: 15014 +`, + }, + { + desc: "DeleteInternalNode", + path: `spec.template.spec.containers.[name:deleteThis]`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +spec: + template: + spec: + containers: + - command: + - /usr/local/bin/galley + - server + - --meshConfigFile=/etc/mesh-config/mesh + - --livenessProbeInterval=1s + - --validation-webhook-config-file + name: galley + ports: + - containerPort: 443 + - containerPort: 15014 + - containerPort: 9901 +`, + }, + { + desc: "DeleteLeafListEntry", + path: `spec.template.spec.containers.[name:galley].command.[--validation-webhook-config-file]`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +spec: + template: + spec: + containers: + - foo: bar + name: deleteThis + - command: + - /usr/local/bin/galley + - server + - --meshConfigFile=/etc/mesh-config/mesh + - --livenessProbeInterval=1s + name: galley + ports: + - containerPort: 443 + - containerPort: 15014 + - containerPort: 9901 +`, + }, + { + desc: "UpdateInteriorNode", + path: `spec.template.spec.containers.[name:galley].ports.[containerPort:15014]`, + value: ` + fooPort: 15015`, + want: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-citadel + namespace: istio-system +spec: + template: + spec: + containers: + - foo: bar + name: deleteThis + - command: + - /usr/local/bin/galley + - server + - --meshConfigFile=/etc/mesh-config/mesh + - --livenessProbeInterval=1s + - --validation-webhook-config-file + name: galley + ports: + - containerPort: 443 + - fooPort: 15015 + - containerPort: 9901 + +`, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + rc := &v1alpha1.KubernetesResourcesSpec{} + t.Log(makeOverlayHeader(tt.path, tt.value)) + err := util.UnmarshalWithJSONPB(makeOverlayHeader(tt.path, tt.value), rc, false) + if err != nil { + t.Fatalf("unmarshalWithJSONPB(%s): got error %s", tt.desc, err) + } + got, err := YAMLManifestPatch(base, "istio-system", rc.Overlays) + if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { + t.Fatalf("YAMLManifestPatch(%s): gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) + } + if want := tt.want; !util.IsYAMLEqual(got, want) { + t.Errorf("YAMLManifestPatch(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, got, want, util.YAMLDiff(got, want)) + } + }) + } +} + +func makeOverlayHeader(path, value string) string { + const ( + patchCommon = `overlays: +- kind: Deployment + name: istio-citadel + patches: + - path: ` + valueStr = ` value: ` + ) + + ret := patchCommon + ret += fmt.Sprintf("%s\n", path) + if value != "" { + ret += fmt.Sprintf("%s%s\n", valueStr, value) + } + return ret +} + +// errToString returns the string representation of err and the empty string if +// err is nil. +func errToString(err error) string { + if err == nil { + return "" + } + return err.Error() +} diff --git a/operator/pkg/tpath/struct.go b/operator/pkg/tpath/struct.go new file mode 100644 index 0000000..9add224 --- /dev/null +++ b/operator/pkg/tpath/struct.go @@ -0,0 +1,134 @@ +// Copyright Istio 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. + +/* +struct.go contains functions for traversing and modifying trees of Go structs. +*/ +package tpath + +import ( + "fmt" + "reflect" + "strconv" + + "google.golang.org/protobuf/types/known/structpb" + + "github.com/jehawley/istio/operator/pkg/util" +) + +// GetFromStructPath returns the value at path from the given node, or false if the path does not exist. +func GetFromStructPath(node any, path string) (any, bool, error) { + return getFromStructPath(node, util.PathFromString(path)) +} + +// getFromStructPath is the internal implementation of GetFromStructPath which recurses through a tree of Go structs +// given a path. It terminates when the end of the path is reached or a path element does not exist. +func getFromStructPath(node any, path util.Path) (any, bool, error) { + scope.Debugf("getFromStructPath path=%s, node(%T)", path, node) + if len(path) == 0 { + scope.Debugf("getFromStructPath returning node(%T)%v", node, node) + return node, !util.IsValueNil(node), nil + } + // For protobuf types, switch them out with standard types; otherwise we will traverse protobuf internals rather + // than the standard representation + if v, ok := node.(*structpb.Struct); ok { + node = v.AsMap() + } + if v, ok := node.(*structpb.Value); ok { + node = v.AsInterface() + } + val := reflect.ValueOf(node) + kind := reflect.TypeOf(node).Kind() + var structElems reflect.Value + + switch kind { + case reflect.Map: + if path[0] == "" { + return nil, false, fmt.Errorf("getFromStructPath path %s, empty map key value", path) + } + mapVal := val.MapIndex(reflect.ValueOf(path[0])) + if !mapVal.IsValid() { + return nil, false, fmt.Errorf("getFromStructPath path %s, path does not exist", path) + } + return getFromStructPath(mapVal.Interface(), path[1:]) + case reflect.Slice: + idx, err := strconv.Atoi(path[0]) + if err != nil { + return nil, false, fmt.Errorf("getFromStructPath path %s, expected index number, got %s", path, path[0]) + } + return getFromStructPath(val.Index(idx).Interface(), path[1:]) + case reflect.Ptr: + structElems = reflect.ValueOf(node).Elem() + if !util.IsStruct(structElems) { + return nil, false, fmt.Errorf("getFromStructPath path %s, expected struct ptr, got %T", path, node) + } + default: + return nil, false, fmt.Errorf("getFromStructPath path %s, unsupported type %T", path, node) + } + + if util.IsNilOrInvalidValue(structElems) { + return nil, false, nil + } + + for i := 0; i < structElems.NumField(); i++ { + fieldName := structElems.Type().Field(i).Name + + if fieldName != path[0] { + continue + } + + fv := structElems.Field(i) + return getFromStructPath(fv.Interface(), path[1:]) + } + + return nil, false, nil +} + +// SetFromPath sets out with the value at path from node. out is not set if the path doesn't exist or the value is nil. +// All intermediate along path must be type struct ptr. Out must be either a struct ptr or map ptr. +// TODO: move these out to a separate package (istio/istio#15494). +func SetFromPath(node any, path string, out any) (bool, error) { + val, found, err := GetFromStructPath(node, path) + if err != nil { + return false, err + } + if !found { + return false, nil + } + + return true, Set(val, out) +} + +// Set sets out with the value at path from node. out is not set if the path doesn't exist or the value is nil. +func Set(val, out any) error { + // Special case: map out type must be set through map ptr. + if util.IsMap(val) && util.IsMapPtr(out) { + reflect.ValueOf(out).Elem().Set(reflect.ValueOf(val)) + return nil + } + if util.IsSlice(val) && util.IsSlicePtr(out) { + reflect.ValueOf(out).Elem().Set(reflect.ValueOf(val)) + return nil + } + + if reflect.TypeOf(val) != reflect.TypeOf(out) { + return fmt.Errorf("setFromPath from type %T != to type %T, %v", val, out, util.IsSlicePtr(out)) + } + + if !reflect.ValueOf(out).CanSet() { + return fmt.Errorf("can't set %v(%T) to out type %T", val, val, out) + } + reflect.ValueOf(out).Set(reflect.ValueOf(val)) + return nil +} diff --git a/operator/pkg/tpath/struct_test.go b/operator/pkg/tpath/struct_test.go new file mode 100644 index 0000000..7c06107 --- /dev/null +++ b/operator/pkg/tpath/struct_test.go @@ -0,0 +1,162 @@ +// Copyright Istio 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 tpath + +import ( + "testing" + + "sigs.k8s.io/yaml" + + "github.com/jehawley/istio/operator/pkg/util" +) + +func TestGetFromStructPath(t *testing.T) { + tests := []struct { + desc string + nodeYAML string + path string + wantYAML string + wantFound bool + wantErr string + }{ + { + desc: "GetStructItem", + nodeYAML: ` +a: va +b: vb +c: + d: vd + e: + f: vf +g: + h: + - i: vi + j: vj + k: + l: + m: vm + n: vn +`, + path: "c", + wantYAML: ` +d: vd +e: + f: vf +`, + wantFound: true, + }, + { + desc: "GetSliceEntryItem", + nodeYAML: ` +a: va +b: vb +c: + d: vd + e: + f: vf +g: + h: + - i: vi + j: vj + k: + l: + m: vm + n: vm +`, + path: "g.h.0", + wantYAML: ` +i: vi +j: vj +k: + l: + m: vm + n: vm +`, + wantFound: true, + }, + { + desc: "GetMapEntryItem", + nodeYAML: ` +a: va +b: vb +c: + d: vd + e: + f: vf +g: + h: + - i: vi + j: vj + k: + l: + m: vm + n: vm +`, + path: "g.h.0.k", + wantYAML: ` +l: + m: vm + n: vm +`, + wantFound: true, + }, + { + desc: "GetPathNotExists", + nodeYAML: ` +a: va +b: vb +c: + d: vd + e: + f: vf +g: + h: + - i: vi + j: vj + k: + l: + m: vm + n: vm +`, + path: "c.d.e", + wantFound: false, + wantErr: "getFromStructPath path e, unsupported type string", + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + rnode := make(map[string]any) + if err := yaml.Unmarshal([]byte(tt.nodeYAML), &rnode); err != nil { + t.Fatal(err) + } + GotOut, GotFound, gotErr := GetFromStructPath(rnode, tt.path) + if GotFound != tt.wantFound { + t.Fatalf("GetFromStructPath(%s): gotFound:%v, wantFound:%v", tt.desc, GotFound, tt.wantFound) + } + if gotErr, wantErr := errToString(gotErr), tt.wantErr; gotErr != wantErr { + t.Fatalf("GetFromStructPath(%s): gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) + } + if tt.wantErr != "" || !tt.wantFound { + return + } + gotYAML := util.ToYAML(GotOut) + diff := util.YAMLDiff(gotYAML, tt.wantYAML) + if diff != "" { + t.Errorf("GetFromStructPath(%s): YAML of gotOut:\n%s\n, YAML of wantOut:\n%s\n, diff:\n%s\n", tt.desc, gotYAML, tt.wantYAML, diff) + } + }) + } +} diff --git a/operator/pkg/tpath/tree.go b/operator/pkg/tpath/tree.go new file mode 100644 index 0000000..0f8f3b4 --- /dev/null +++ b/operator/pkg/tpath/tree.go @@ -0,0 +1,574 @@ +// Copyright Istio 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. + +/* +tree.go contains functions for traversing and updating a tree constructed from yaml or json.Unmarshal. +Nodes in such trees have the form map[interface{}]interface{} or map[interface{}][]interface{}. +For some tree updates, like delete or append, it's necessary to have access to the parent node. PathContext is a +tree constructed during tree traversal that gives access to ancestor nodes all the way up to the root, which can be +used for this purpose. +*/ +package tpath + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "regexp" + "strconv" + "strings" + + "gopkg.in/yaml.v2" + yaml2 "sigs.k8s.io/yaml" + + "github.com/jehawley/istio/operator/pkg/util" + "istio.io/istio/pkg/log" +) + +var scope = log.RegisterScope("tpath", "tree traverser") + +// PathContext provides a means for traversing a tree towards the root. +type PathContext struct { + // Parent in the Parent of this PathContext. + Parent *PathContext + // KeyToChild is the key required to reach the child. + KeyToChild any + // Node is the actual Node in the data tree. + Node any +} + +// String implements the Stringer interface. +func (nc *PathContext) String() string { + ret := "\n--------------- NodeContext ------------------\n" + if nc.Parent != nil { + ret += fmt.Sprintf("Parent.Node=\n%s\n", nc.Parent.Node) + ret += fmt.Sprintf("KeyToChild=%v\n", nc.Parent.KeyToChild) + } + + ret += fmt.Sprintf("Node=\n%s\n", nc.Node) + ret += "----------------------------------------------\n" + + return ret +} + +// GetPathContext returns the PathContext for the Node which has the given path from root. +// It returns false and no error if the given path is not found, or an error code in other error situations, like +// a malformed path. +// It also creates a tree of PathContexts during the traversal so that Parent nodes can be updated if required. This is +// required when (say) appending to a list, where the parent list itself must be updated. +func GetPathContext(root any, path util.Path, createMissing bool) (*PathContext, bool, error) { + return getPathContext(&PathContext{Node: root}, path, path, createMissing) +} + +// WritePathContext writes the given value to the Node in the given PathContext. +func WritePathContext(nc *PathContext, value any, merge bool) error { + scope.Debugf("WritePathContext PathContext=%s, value=%v", nc, value) + + if !util.IsValueNil(value) { + return setPathContext(nc, value, merge) + } + + scope.Debug("delete") + if nc.Parent == nil { + return errors.New("cannot delete root element") + } + + switch { + case isSliceOrPtrInterface(nc.Parent.Node): + if err := util.DeleteFromSlicePtr(nc.Parent.Node, nc.Parent.KeyToChild.(int)); err != nil { + return err + } + if isMapOrInterface(nc.Parent.Parent.Node) { + return util.InsertIntoMap(nc.Parent.Parent.Node, nc.Parent.Parent.KeyToChild, nc.Parent.Node) + } + // TODO: The case of deleting a list.list.node element is not currently supported. + return fmt.Errorf("cannot delete path: unsupported parent.parent type %T for delete", nc.Parent.Parent.Node) + case util.IsMap(nc.Parent.Node): + return util.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild) + default: + } + return fmt.Errorf("cannot delete path: unsupported parent type %T for delete", nc.Parent.Node) +} + +// WriteNode writes value to the tree in root at the given path, creating any required missing internal nodes in path. +func WriteNode(root any, path util.Path, value any) error { + pc, _, err := getPathContext(&PathContext{Node: root}, path, path, true) + if err != nil { + return err + } + return WritePathContext(pc, value, false) +} + +// MergeNode merges value to the tree in root at the given path, creating any required missing internal nodes in path. +func MergeNode(root any, path util.Path, value any) error { + pc, _, err := getPathContext(&PathContext{Node: root}, path, path, true) + if err != nil { + return err + } + return WritePathContext(pc, value, true) +} + +// Find returns the value at path from the given tree, or false if the path does not exist. +// It behaves differently from GetPathContext in that it never creates map entries at the leaf and does not provide +// a way to mutate the parent of the found node. +func Find(inputTree map[string]any, path util.Path) (any, bool, error) { + scope.Debugf("Find path=%s", path) + if len(path) == 0 { + return nil, false, fmt.Errorf("path is empty") + } + node, found := find(inputTree, path) + return node, found, nil +} + +// Delete sets value at path of input untyped tree to nil +func Delete(root map[string]any, path util.Path) (bool, error) { + pc, _, err := getPathContext(&PathContext{Node: root}, path, path, false) + if err != nil { + return false, err + } + return true, WritePathContext(pc, nil, false) +} + +// getPathContext is the internal implementation of GetPathContext. +// If createMissing is true, it creates any missing map (but NOT list) path entries in root. +func getPathContext(nc *PathContext, fullPath, remainPath util.Path, createMissing bool) (*PathContext, bool, error) { + scope.Debugf("getPathContext remainPath=%s, Node=%v", remainPath, nc.Node) + if len(remainPath) == 0 { + return nc, true, nil + } + pe := remainPath[0] + + if nc.Node == nil { + if !createMissing { + return nil, false, fmt.Errorf("node %s is zero", pe) + } + if util.IsNPathElement(pe) || util.IsKVPathElement(pe) { + nc.Node = []any{} + } else { + nc.Node = make(map[string]any) + } + } + + v := reflect.ValueOf(nc.Node) + if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + v = v.Elem() + } + ncNode := v.Interface() + + // For list types, we need a key to identify the selected list item. This can be either a value key of the + // form :matching_value in the case of a leaf list, or a matching key:value in the case of a non-leaf list. + if lst, ok := ncNode.([]any); ok { + scope.Debug("list type") + // If the path element has the form [N], a list element is being selected by index. Return the element at index + // N if it exists. + if util.IsNPathElement(pe) { + idx, err := util.PathN(pe) + if err != nil { + return nil, false, fmt.Errorf("path %s, index %s: %s", fullPath, pe, err) + } + var foundNode any + if idx >= len(lst) || idx < 0 { + if !createMissing { + return nil, false, fmt.Errorf("index %d exceeds list length %d at path %s", idx, len(lst), remainPath) + } + idx = len(lst) + foundNode = make(map[string]any) + } else { + foundNode = lst[idx] + } + nn := &PathContext{ + Parent: nc, + Node: foundNode, + } + nc.KeyToChild = idx + return getPathContext(nn, fullPath, remainPath[1:], createMissing) + } + + // Otherwise the path element must have form [key:value]. In this case, go through all list elements, which + // must have map type, and try to find one which has a matching key:value. + for idx, le := range lst { + // non-leaf list, expect to match item by key:value. + if lm, ok := le.(map[any]any); ok { + k, v, err := util.PathKV(pe) + if err != nil { + return nil, false, fmt.Errorf("path %s: %s", fullPath, err) + } + if stringsEqual(lm[k], v) { + scope.Debugf("found matching kv %v:%v", k, v) + nn := &PathContext{ + Parent: nc, + Node: lm, + } + nc.KeyToChild = idx + nn.KeyToChild = k + if len(remainPath) == 1 { + scope.Debug("KV terminate") + return nn, true, nil + } + return getPathContext(nn, fullPath, remainPath[1:], createMissing) + } + continue + } + // repeat of the block above for the case where tree unmarshals to map[string]interface{}. There doesn't + // seem to be a way to merge this case into the above block. + if lm, ok := le.(map[string]any); ok { + k, v, err := util.PathKV(pe) + if err != nil { + return nil, false, fmt.Errorf("path %s: %s", fullPath, err) + } + if stringsEqual(lm[k], v) { + scope.Debugf("found matching kv %v:%v", k, v) + nn := &PathContext{ + Parent: nc, + Node: lm, + } + nc.KeyToChild = idx + nn.KeyToChild = k + if len(remainPath) == 1 { + scope.Debug("KV terminate") + return nn, true, nil + } + return getPathContext(nn, fullPath, remainPath[1:], createMissing) + } + continue + } + // leaf list, expect path element [V], match based on value V. + v, err := util.PathV(pe) + if err != nil { + return nil, false, fmt.Errorf("path %s: %s", fullPath, err) + } + if matchesRegex(v, le) { + scope.Debugf("found matching key %v, index %d", le, idx) + nn := &PathContext{ + Parent: nc, + Node: le, + } + nc.KeyToChild = idx + return getPathContext(nn, fullPath, remainPath[1:], createMissing) + } + } + return nil, false, fmt.Errorf("path %s: element %s not found", fullPath, pe) + } + + if util.IsMap(ncNode) { + scope.Debug("map type") + var nn any + if m, ok := ncNode.(map[any]any); ok { + nn, ok = m[pe] + if !ok { + // remainPath == 1 means the patch is creation of a new leaf. + if createMissing || len(remainPath) == 1 { + m[pe] = make(map[any]any) + nn = m[pe] + } else { + return nil, false, fmt.Errorf("path not found at element %s in path %s", pe, fullPath) + } + } + } + if reflect.ValueOf(ncNode).IsNil() { + ncNode = make(map[string]any) + nc.Node = ncNode + } + if m, ok := ncNode.(map[string]any); ok { + nn, ok = m[pe] + if !ok { + // remainPath == 1 means the patch is creation of a new leaf. + if createMissing || len(remainPath) == 1 { + nextElementNPath := len(remainPath) > 1 && util.IsNPathElement(remainPath[1]) + if nextElementNPath { + scope.Debug("map type, slice child") + m[pe] = make([]any, 0) + } else { + scope.Debug("map type, map child") + m[pe] = make(map[string]any) + } + nn = m[pe] + } else { + return nil, false, fmt.Errorf("path not found at element %s in path %s", pe, fullPath) + } + } + } + + npc := &PathContext{ + Parent: nc, + Node: nn, + } + // for slices, use the address so that the slice can be mutated. + if util.IsSlice(nn) { + npc.Node = &nn + } + nc.KeyToChild = pe + return getPathContext(npc, fullPath, remainPath[1:], createMissing) + } + + return nil, false, fmt.Errorf("leaf type %T in non-leaf Node %s", nc.Node, remainPath) +} + +// setPathContext writes the given value to the Node in the given PathContext, +// enlarging all PathContext lists to ensure all indexes are valid. +func setPathContext(nc *PathContext, value any, merge bool) error { + processParent, err := setValueContext(nc, value, merge) + if err != nil || !processParent { + return err + } + + // If the path included insertions, process them now + if nc.Parent.Parent == nil { + return nil + } + return setPathContext(nc.Parent, nc.Parent.Node, false) // note: tail recursive +} + +// setValueContext writes the given value to the Node in the given PathContext. +// If setting the value requires growing the final slice, grows it. +func setValueContext(nc *PathContext, value any, merge bool) (bool, error) { + if nc.Parent == nil { + return false, nil + } + + vv, mapFromString := tryToUnmarshalStringToYAML(value) + + switch parentNode := nc.Parent.Node.(type) { + case *any: + switch vParentNode := (*parentNode).(type) { + case []any: + idx := nc.Parent.KeyToChild.(int) + if idx == -1 { + // Treat -1 as insert-at-end of list + idx = len(vParentNode) + } + + if idx >= len(vParentNode) { + newElements := make([]any, idx-len(vParentNode)+1) + vParentNode = append(vParentNode, newElements...) + *parentNode = vParentNode + } + + merged, err := mergeConditional(vv, nc.Node, merge) + if err != nil { + return false, err + } + + vParentNode[idx] = merged + nc.Node = merged + default: + return false, fmt.Errorf("don't know about vtype %T", vParentNode) + } + case map[string]any: + key := nc.Parent.KeyToChild.(string) + + // Update is treated differently depending on whether the value is a scalar or map type. If scalar, + // insert a new element into the terminal node, otherwise replace the terminal node with the new subtree. + if ncNode, ok := nc.Node.(*any); ok && !mapFromString { + switch vNcNode := (*ncNode).(type) { + case []any: + switch vv.(type) { + case map[string]any: + // the vv is a map, and the node is a slice + mergedValue := append(vNcNode, vv) + parentNode[key] = mergedValue + case *any: + merged, err := mergeConditional(vv, vNcNode, merge) + if err != nil { + return false, err + } + + parentNode[key] = merged + nc.Node = merged + default: + // the vv is an basic JSON type (int, float, string, bool) + vv = append(vNcNode, vv) + parentNode[key] = vv + nc.Node = vv + } + default: + return false, fmt.Errorf("don't know about vnc type %T", vNcNode) + } + } else { + // For map passed as string type, the root is the new key. + if mapFromString { + if err := util.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild); err != nil { + return false, err + } + vm := vv.(map[string]any) + newKey := getTreeRoot(vm) + return false, util.InsertIntoMap(nc.Parent.Node, newKey, vm[newKey]) + } + parentNode[key] = vv + nc.Node = vv + } + // TODO `map[interface{}]interface{}` is used by tests in operator/cmd/mesh, we should add our own tests + case map[any]any: + key := nc.Parent.KeyToChild.(string) + parentNode[key] = vv + nc.Node = vv + default: + return false, fmt.Errorf("don't know about type %T", parentNode) + } + + return true, nil +} + +// mergeConditional returns a merge of newVal and originalVal if merge is true, otherwise it returns newVal. +func mergeConditional(newVal, originalVal any, merge bool) (any, error) { + if !merge || util.IsValueNilOrDefault(originalVal) { + return newVal, nil + } + newS, err := yaml.Marshal(newVal) + if err != nil { + return nil, err + } + if util.IsYAMLEmpty(string(newS)) { + return originalVal, nil + } + originalS, err := yaml.Marshal(originalVal) + if err != nil { + return nil, err + } + if util.IsYAMLEmpty(string(originalS)) { + return newVal, nil + } + + mergedS, err := util.OverlayYAML(string(originalS), string(newS)) + if err != nil { + return nil, err + } + + if util.IsMap(originalVal) { + // For JSON compatibility + out := make(map[string]any) + if err := yaml.Unmarshal([]byte(mergedS), &out); err != nil { + return nil, err + } + return out, nil + } + // For scalars and slices, copy the type + out := originalVal + if err := yaml.Unmarshal([]byte(mergedS), &out); err != nil { + return nil, err + } + return out, nil +} + +// find returns the value at path from the given tree, or false if the path does not exist. +func find(treeNode any, path util.Path) (any, bool) { + if len(path) == 0 || treeNode == nil { + return nil, false + } + switch nt := treeNode.(type) { + case map[any]any: + val := nt[path[0]] + if val == nil { + return nil, false + } + if len(path) == 1 { + return val, true + } + return find(val, path[1:]) + case map[string]any: + val := nt[path[0]] + if val == nil { + return nil, false + } + if len(path) == 1 { + return val, true + } + return find(val, path[1:]) + case []any: + idx, err := strconv.Atoi(path[0]) + if err != nil { + return nil, false + } + if idx >= len(nt) { + return nil, false + } + val := nt[idx] + return find(val, path[1:]) + default: + return nil, false + } +} + +// stringsEqual reports whether the string representations of a and b are equal. a and b may have different types. +func stringsEqual(a, b any) bool { + return fmt.Sprint(a) == fmt.Sprint(b) +} + +// matchesRegex reports whether str regex matches pattern. +func matchesRegex(pattern, str any) bool { + match, err := regexp.MatchString(fmt.Sprint(pattern), fmt.Sprint(str)) + if err != nil { + log.Errorf("bad regex expression %s", fmt.Sprint(pattern)) + return false + } + scope.Debugf("%v regex %v? %v\n", pattern, str, match) + return match +} + +// isSliceOrPtrInterface reports whether v is a slice, a ptr to slice or interface to slice. +func isSliceOrPtrInterface(v any) bool { + vv := reflect.ValueOf(v) + if vv.Kind() == reflect.Ptr { + vv = vv.Elem() + } + if vv.Kind() == reflect.Interface { + vv = vv.Elem() + } + return vv.Kind() == reflect.Slice +} + +// isMapOrInterface reports whether v is a map, or interface to a map. +func isMapOrInterface(v any) bool { + vv := reflect.ValueOf(v) + if vv.Kind() == reflect.Interface { + vv = vv.Elem() + } + return vv.Kind() == reflect.Map +} + +// tryToUnmarshalStringToYAML tries to unmarshal something that may be a YAML list or map into a structure. If not +// possible, returns original scalar value. +func tryToUnmarshalStringToYAML(s any) (any, bool) { + // If value type is a string it could either be a literal string or a map type passed as a string. Try to unmarshal + // to discover it's the latter. + vv := s + + if reflect.TypeOf(vv).Kind() == reflect.String { + sv := strings.Split(vv.(string), "\n") + // Need to be careful not to transform string literals into maps unless they really are maps, since scalar handling + // is different for inserts. + if len(sv) == 1 && strings.Contains(s.(string), ": ") || + len(sv) > 1 && strings.Contains(s.(string), ":") { + nv := make(map[string]any) + if err := json.Unmarshal([]byte(vv.(string)), &nv); err == nil { + // treat JSON as string + return vv, false + } + if err := yaml2.Unmarshal([]byte(vv.(string)), &nv); err == nil { + return nv, true + } + } + } + // looks like a literal or failed unmarshal, return original type. + return vv, false +} + +// getTreeRoot returns the first key found in m. It assumes a single root tree. +func getTreeRoot(m map[string]any) string { + for k := range m { + return k + } + return "" +} diff --git a/operator/pkg/tpath/tree_test.go b/operator/pkg/tpath/tree_test.go new file mode 100644 index 0000000..c9ae9cb --- /dev/null +++ b/operator/pkg/tpath/tree_test.go @@ -0,0 +1,844 @@ +// Copyright Istio 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 tpath + +import ( + "testing" + + "sigs.k8s.io/yaml" + + "github.com/jehawley/istio/operator/pkg/util" +) + +func TestWritePathContext(t *testing.T) { + rootYAML := ` +a: + b: + - name: n1 + value: v1 + - name: n2 + list: + - v1 + - v2 + - v3_regex +` + tests := []struct { + desc string + path string + value any + want string + wantFound bool + wantErr string + }{ + { + desc: "AddListEntry", + path: `a.b.[name:n2].list`, + value: `foo`, + wantFound: true, + want: ` +a: + b: + - name: n1 + value: v1 + - name: n2 + list: + - v1 + - v2 + - v3_regex + - foo +`, + }, + { + desc: "ModifyListEntryValue", + path: `a.b.[name:n1].value`, + value: `v2`, + wantFound: true, + want: ` +a: + b: + - name: n1 + value: v2 + - list: + - v1 + - v2 + - v3_regex + name: n2 +`, + }, + { + desc: "ModifyListEntryValueQuoted", + path: `a.b.[name:n1].value`, + value: `v2`, + wantFound: true, + want: ` +a: + b: + - name: "n1" + value: v2 + - list: + - v1 + - v2 + - v3_regex + name: n2 +`, + }, + { + desc: "ModifyListEntry", + path: `a.b.[name:n2].list.[:v2]`, + value: `v3`, + wantFound: true, + want: ` +a: + b: + - name: n1 + value: v1 + - list: + - v1 + - v3 + - v3_regex + name: n2 +`, + }, + { + desc: "ModifyListEntryMapValue", + path: `a.b.[name:n2]`, + value: `name: n2 +list: + - nk1: nv1 + - nk2: nv2`, + wantFound: true, + want: ` +a: + b: + - name: n1 + value: v1 + - name: n2 + list: + - nk1: nv1 + - nk2: nv2 +`, + }, + { + desc: "ModifyNthListEntry", + path: `a.b.[1].list.[:v2]`, + value: `v-the-second`, + wantFound: true, + want: ` +a: + b: + - name: n1 + value: v1 + - list: + - v1 + - v-the-second + - v3_regex + name: n2 +`, + }, + { + desc: "ModifyNthLeafListEntry", + path: `a.b.[1].list.[2]`, + value: `v-the-third`, + wantFound: true, + want: ` +a: + b: + - name: n1 + value: v1 + - list: + - v1 + - v2 + - v-the-third + name: n2 +`, + }, + { + desc: "ModifyListEntryValueDotless", + path: `a.b[name:n1].value`, + value: `v2`, + wantFound: true, + want: ` +a: + b: + - name: n1 + value: v2 + - list: + - v1 + - v2 + - v3_regex + name: n2 +`, + }, + { + desc: "DeleteListEntry", + path: `a.b.[name:n1]`, + wantFound: true, + want: ` +a: + b: + - list: + - v1 + - v2 + - v3_regex + name: n2 +`, + }, + { + desc: "DeleteListEntryValue", + path: `a.b.[name:n2].list.[:v2]`, + wantFound: true, + want: ` +a: + b: + - name: n1 + value: v1 + - list: + - v1 + - v3_regex + name: n2 +`, + }, + { + desc: "DeleteListEntryIndex", + path: `a.b.[name:n2].list.[1]`, + wantFound: true, + want: ` +a: + b: + - name: n1 + value: v1 + - list: + - v1 + - v3_regex + name: n2 +`, + }, + { + desc: "DeleteListEntryValueRegex", + path: `a.b.[name:n2].list.[:v3]`, + wantFound: true, + want: ` +a: + b: + - name: n1 + value: v1 + - list: + - v1 + - v2 + name: n2 +`, + }, + { + desc: "DeleteListLeafEntryBogusIndex", + path: `a.b.[name:n2].list.[-200]`, + wantFound: false, + wantErr: `path a.b.[name:n2].list.[-200]: element [-200] not found`, + }, + { + desc: "DeleteListEntryBogusIndex", + path: `a.b.[1000000].list.[:v2]`, + wantFound: false, + wantErr: `index 1000000 exceeds list length 2 at path [1000000].list.[:v2]`, + }, + { + desc: "AddMapEntry", + path: `a.new_key`, + value: `new_val`, + wantFound: true, + want: ` +a: + b: + - name: n1 + value: v1 + - name: n2 + list: + - v1 + - v2 + - v3_regex + new_key: new_val +`, + }, + { + desc: "AddMapEntryMapValue", + path: `a.new_key`, + value: `new_key: + nk1: + nk2: nv2`, + wantFound: true, + want: ` +a: + b: + - name: n1 + value: v1 + - name: n2 + list: + - v1 + - v2 + - v3_regex + new_key: + nk1: + nk2: nv2 +`, + }, + { + desc: "ModifyMapEntryMapValue", + path: `a.b`, + value: `nk1: + nk2: nv2`, + wantFound: true, + want: ` +a: + nk1: + nk2: nv2 +`, + }, + { + desc: "DeleteMapEntry", + path: `a.b`, + wantFound: true, + want: ` +a: {} +`, + }, + { + desc: "path not found", + path: `a.c.[name:n2].list.[:v3]`, + wantFound: false, + wantErr: `path not found at element c in path a.c.[name:n2].list.[:v3]`, + }, + { + desc: "error key", + path: `a.b.[].list`, + wantFound: false, + wantErr: `path a.b.[].list: [] is not a valid key:value path element`, + }, + { + desc: "invalid index", + path: `a.c.[n2].list.[:v3]`, + wantFound: false, + wantErr: `path not found at element c in path a.c.[n2].list.[:v3]`, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + root := make(map[string]any) + if err := yaml.Unmarshal([]byte(rootYAML), &root); err != nil { + t.Fatal(err) + } + pc, gotFound, gotErr := GetPathContext(root, util.PathFromString(tt.path), false) + if gotErr, wantErr := errToString(gotErr), tt.wantErr; gotErr != wantErr { + t.Fatalf("GetPathContext(%s): gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) + } + if gotFound != tt.wantFound { + t.Fatalf("GetPathContext(%s): gotFound:%v, wantFound:%v", tt.desc, gotFound, tt.wantFound) + } + if tt.wantErr != "" || !tt.wantFound { + if tt.want != "" { + t.Error("tt.want is set but never checked") + } + return + } + + err := WritePathContext(pc, tt.value, false) + if err != nil { + t.Fatal(err) + } + + gotYAML := util.ToYAML(root) + diff := util.YAMLDiff(gotYAML, tt.want) + if diff != "" { + t.Errorf("%s: (got:-, want:+):\n%s\n", tt.desc, diff) + } + }) + } +} + +func TestWriteNode(t *testing.T) { + testTreeYAML := ` +a: + b: + c: val1 + list1: + - i1: val1 + - i2: val2 + - i3a: key1 + i3b: + list2: + - i1: val1 + - i2: val2 + - i3a: key1 + i3b: + i1: va11 +` + tests := []struct { + desc string + baseYAML string + path string + value string + want string + wantErr string + }{ + { + desc: "insert empty", + path: "a.b.c", + value: "val1", + want: ` +a: + b: + c: val1 +`, + }, + { + desc: "overwrite", + baseYAML: testTreeYAML, + path: "a.b.c", + value: "val2", + want: ` +a: + b: + c: val2 + list1: + - i1: val1 + - i2: val2 + - i3a: key1 + i3b: + list2: + - i1: val1 + - i2: val2 + - i3a: key1 + i3b: + i1: va11 +`, + }, + { + desc: "partial create", + baseYAML: testTreeYAML, + path: "a.b.d", + value: "val3", + want: ` +a: + b: + c: val1 + d: val3 + list1: + - i1: val1 + - i2: val2 + - i3a: key1 + i3b: + list2: + - i1: val1 + - i2: val2 + - i3a: key1 + i3b: + i1: va11 +`, + }, + { + desc: "list keys", + baseYAML: testTreeYAML, + path: "a.b.list1.[i3a:key1].i3b.list2.[i3a:key1].i3b.i1", + value: "val2", + want: ` +a: + b: + c: val1 + list1: + - i1: val1 + - i2: val2 + - i3a: key1 + i3b: + list2: + - i1: val1 + - i2: val2 + - i3a: key1 + i3b: + i1: val2 +`, + }, + // For https://github.com/istio/istio/issues/20950 + { + desc: "with initial list", + baseYAML: ` +components: + ingressGateways: + - enabled: true +`, + path: "components.ingressGateways[0].enabled", + value: "false", + want: ` +components: + ingressGateways: + - enabled: "false" +`, + }, + { + desc: "no initial list", + baseYAML: "", + path: "components.ingressGateways[0].enabled", + value: "false", + want: ` +components: + ingressGateways: + - enabled: "false" +`, + }, + { + desc: "no initial list for entry", + baseYAML: ` +a: {} +`, + path: "a.list.[0]", + value: "v1", + want: ` +a: + list: + - v1 +`, + }, + { + desc: "ExtendNthLeafListEntry", + baseYAML: ` +a: + list: + - v1 +`, + path: `a.list.[1]`, + value: `v2`, + want: ` +a: + list: + - v1 + - v2 +`, + }, + { + desc: "ExtendLeafListEntryLargeIndex", + baseYAML: ` +a: + list: + - v1 +`, + path: `a.list.[999]`, + value: `v2`, + want: ` +a: + list: + - v1 + - v2 +`, + }, + { + desc: "ExtendLeafListEntryNegativeIndex", + baseYAML: ` +a: + list: + - v1 +`, + path: `a.list.[-1]`, + value: `v2`, + want: ` +a: + list: + - v1 + - v2 +`, + }, + { + desc: "ExtendNthListEntry", + baseYAML: ` +a: + list: + - name: foo +`, + path: `a.list.[1].name`, + value: `bar`, + want: ` +a: + list: + - name: foo + - name: bar +`, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + root := make(map[string]any) + if tt.baseYAML != "" { + if err := yaml.Unmarshal([]byte(tt.baseYAML), &root); err != nil { + t.Fatal(err) + } + } + p := util.PathFromString(tt.path) + err := WriteNode(root, p, tt.value) + if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { + t.Errorf("%s: gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) + return + } + if got, want := util.ToYAML(root), tt.want; err == nil && util.YAMLDiff(got, want) != "" { + t.Errorf("%s: got:\n%s\nwant:\n%s\ndiff:\n%s\n", tt.desc, got, want, util.YAMLDiff(got, want)) + } + }) + } +} + +func TestMergeNode(t *testing.T) { + testTreeYAML := ` +a: + b: + c: val1 + list1: + - i1: val1 + - i2: val2 +` + tests := []struct { + desc string + baseYAML string + path string + value string + want string + wantErr string + }{ + { + desc: "merge list entry", + baseYAML: testTreeYAML, + path: "a.b.list1.[i1:val1]", + value: ` +i2b: val2`, + want: ` +a: + b: + c: val1 + list1: + - i1: val1 + i2b: val2 + - i2: val2 +`, + }, + { + desc: "merge list 2", + baseYAML: testTreeYAML, + path: "a.b.list1", + value: ` +i3: + a: val3 +`, + want: ` +a: + b: + c: val1 + list1: + - i1: val1 + - i2: val2 + - i3: + a: val3 +`, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + root := make(map[string]any) + if tt.baseYAML != "" { + if err := yaml.Unmarshal([]byte(tt.baseYAML), &root); err != nil { + t.Fatal(err) + } + } + p := util.PathFromString(tt.path) + iv := make(map[string]any) + err := yaml.Unmarshal([]byte(tt.value), &iv) + if err != nil { + t.Fatal(err) + } + err = MergeNode(root, p, iv) + if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { + t.Errorf("%s: gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) + return + } + if got, want := util.ToYAML(root), tt.want; err == nil && util.YAMLDiff(got, want) != "" { + t.Errorf("%s: got:\n%s\nwant:\n%s\ndiff:\n%s\n", tt.desc, got, want, util.YAMLDiff(got, want)) + } + }) + } +} + +// errToString returns the string representation of err and the empty string if +// err is nil. +func errToString(err error) string { + if err == nil { + return "" + } + return err.Error() +} + +// TestSecretVolumes simulates https://github.com/istio/istio/issues/20381 +func TestSecretVolumes(t *testing.T) { + rootYAML := ` +values: + gateways: + istio-egressgateway: + secretVolumes: [] +` + root := make(map[string]any) + if err := yaml.Unmarshal([]byte(rootYAML), &root); err != nil { + t.Fatal(err) + } + overrides := []struct { + path string + value any + }{ + { + path: "values.gateways.istio-egressgateway.secretVolumes[0].name", + value: "egressgateway-certs", + }, + { + path: "values.gateways.istio-egressgateway.secretVolumes[0].secretName", + value: "istio-egressgateway-certs", + }, + { + path: "values.gateways.istio-egressgateway.secretVolumes[0].mountPath", + value: "/etc/istio/egressgateway-certs", + }, + { + path: "values.gateways.istio-egressgateway.secretVolumes[1].name", + value: "egressgateway-ca-certs", + }, + { + path: "values.gateways.istio-egressgateway.secretVolumes[1].secretName", + value: "istio-egressgateway-ca-certs", + }, + { + path: "values.gateways.istio-egressgateway.secretVolumes[1].mountPath", + value: "/etc/istio/egressgateway-ca-certs", + }, + { + path: "values.gateways.istio-egressgateway.secretVolumes[2].name", + value: "nginx-client-certs", + }, + { + path: "values.gateways.istio-egressgateway.secretVolumes[2].secretName", + value: "nginx-client-certs", + }, + { + path: "values.gateways.istio-egressgateway.secretVolumes[2].mountPath", + value: "/etc/istio/nginx-client-certs", + }, + { + path: "values.gateways.istio-egressgateway.secretVolumes[3].name", + value: "nginx-ca-certs", + }, + { + path: "values.gateways.istio-egressgateway.secretVolumes[3].secretName", + value: "nginx-ca-certs", + }, + { + path: "values.gateways.istio-egressgateway.secretVolumes[3].mountPath", + value: "/etc/istio/nginx-ca-certs", + }, + } + + for _, override := range overrides { + + pc, _, err := GetPathContext(root, util.PathFromString(override.path), true) + if err != nil { + t.Fatalf("GetPathContext(%q): %v", override.path, err) + } + err = WritePathContext(pc, override.value, false) + if err != nil { + t.Fatalf("WritePathContext(%q): %v", override.path, err) + } + } + + want := ` +values: + gateways: + istio-egressgateway: + secretVolumes: + - mountPath: /etc/istio/egressgateway-certs + name: egressgateway-certs + secretName: istio-egressgateway-certs + - mountPath: /etc/istio/egressgateway-ca-certs + name: egressgateway-ca-certs + secretName: istio-egressgateway-ca-certs + - mountPath: /etc/istio/nginx-client-certs + name: nginx-client-certs + secretName: nginx-client-certs + - mountPath: /etc/istio/nginx-ca-certs + name: nginx-ca-certs + secretName: nginx-ca-certs +` + gotYAML := util.ToYAML(root) + diff := util.YAMLDiff(gotYAML, want) + if diff != "" { + t.Errorf("TestSecretVolumes: diff:\n%s\n", diff) + } +} + +// Simulates https://github.com/istio/istio/issues/19196 +func TestWriteEscapedPathContext(t *testing.T) { + rootYAML := ` +values: + sidecarInjectorWebhook: + injectedAnnotations: {} +` + tests := []struct { + desc string + path string + value any + want string + wantFound bool + wantErr string + }{ + { + desc: "ModifyEscapedPathValue", + path: `values.sidecarInjectorWebhook.injectedAnnotations.container\.apparmor\.security\.beta\.kubernetes\.io/istio-proxy`, + value: `runtime/default`, + wantFound: true, + want: ` +values: + sidecarInjectorWebhook: + injectedAnnotations: + container.apparmor.security.beta.kubernetes.io/istio-proxy: runtime/default +`, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + root := make(map[string]any) + if err := yaml.Unmarshal([]byte(rootYAML), &root); err != nil { + t.Fatal(err) + } + pc, gotFound, gotErr := GetPathContext(root, util.PathFromString(tt.path), false) + if gotErr, wantErr := errToString(gotErr), tt.wantErr; gotErr != wantErr { + t.Fatalf("GetPathContext(%s): gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) + } + if gotFound != tt.wantFound { + t.Fatalf("GetPathContext(%s): gotFound:%v, wantFound:%v", tt.desc, gotFound, tt.wantFound) + } + if tt.wantErr != "" || !tt.wantFound { + return + } + + err := WritePathContext(pc, tt.value, false) + if err != nil { + t.Fatal(err) + } + + gotYAML := util.ToYAML(root) + diff := util.YAMLDiff(gotYAML, tt.want) + if diff != "" { + t.Errorf("%s: diff:\n%s\n", tt.desc, diff) + } + }) + } +} diff --git a/operator/pkg/tpath/util.go b/operator/pkg/tpath/util.go new file mode 100644 index 0000000..de45b16 --- /dev/null +++ b/operator/pkg/tpath/util.go @@ -0,0 +1,63 @@ +// Copyright Istio 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. + +/* +util.go contains utility function for dealing with trees. +*/ + +package tpath + +import ( + "gopkg.in/yaml.v2" + yaml2 "sigs.k8s.io/yaml" + + "github.com/jehawley/istio/operator/pkg/util" +) + +// AddSpecRoot adds a root node called "spec" to the given tree and returns the resulting tree. +func AddSpecRoot(tree string) (string, error) { + t, nt := make(map[string]any), make(map[string]any) + if err := yaml.Unmarshal([]byte(tree), &t); err != nil { + return "", err + } + nt["spec"] = t + out, err := yaml.Marshal(nt) + if err != nil { + return "", err + } + return string(out), nil +} + +// GetSpecSubtree returns the subtree under "spec". +func GetSpecSubtree(yml string) (string, error) { + return GetConfigSubtree(yml, "spec") +} + +// GetConfigSubtree returns the subtree at the given path. +func GetConfigSubtree(manifest, path string) (string, error) { + root := make(map[string]any) + if err := yaml2.Unmarshal([]byte(manifest), &root); err != nil { + return "", err + } + + nc, _, err := GetPathContext(root, util.PathFromString(path), false) + if err != nil { + return "", err + } + out, err := yaml2.Marshal(nc.Node) + if err != nil { + return "", err + } + return string(out), nil +} diff --git a/operator/pkg/tpath/util_test.go b/operator/pkg/tpath/util_test.go new file mode 100644 index 0000000..9a00f4b --- /dev/null +++ b/operator/pkg/tpath/util_test.go @@ -0,0 +1,122 @@ +// Copyright Istio 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 tpath + +import ( + "errors" + "testing" +) + +func TestAddSpecRoot(t *testing.T) { + tests := []struct { + desc string + in string + expect string + err error + }{ + { + desc: "empty", + in: ``, + expect: `spec: {} +`, + err: nil, + }, + { + desc: "add-root", + in: ` +a: va +b: foo`, + expect: `spec: + a: va + b: foo +`, + err: nil, + }, + { + desc: "err", + in: `i can't be yaml, can I?`, + expect: ``, + err: errors.New(""), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got, err := AddSpecRoot(tt.in); got != tt.expect || + ((err != nil && tt.err == nil) || (err == nil && tt.err != nil)) { + t.Errorf("%s AddSpecRoot(%s) => %s, want %s", tt.desc, tt.in, got, tt.expect) + } + }) + } +} + +func TestGetConfigSubtree(t *testing.T) { + tests := []struct { + desc string + manifest string + path string + expect string + err bool + }{ + { + desc: "empty", + manifest: ``, + path: ``, + expect: `{} +`, + err: false, + }, + { + desc: "subtree", + manifest: ` +a: + b: + - name: n1 + value: v2 + - list: + - v1 + - v2 + - v3_regex + name: n2 +`, + path: `a`, + expect: `b: +- name: n1 + value: v2 +- list: + - v1 + - v2 + - v3_regex + name: n2 +`, + err: false, + }, + { + desc: "err", + manifest: "not-yaml", + path: "not-subnode", + expect: ``, + err: true, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got, err := GetConfigSubtree(tt.manifest, tt.path); got != tt.expect || (err == nil) == tt.err { + t.Errorf("%s GetConfigSubtree(%s, %s) => %s, want %s", tt.desc, tt.manifest, tt.path, got, tt.expect) + } + }) + } +} diff --git a/operator/pkg/translate/strategic_port_merge_test.go b/operator/pkg/translate/strategic_port_merge_test.go new file mode 100644 index 0000000..f91c649 --- /dev/null +++ b/operator/pkg/translate/strategic_port_merge_test.go @@ -0,0 +1,189 @@ +// Copyright Istio 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 translate + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +var ( + httpsPort = &v1.ServicePort{ + Name: "https", + Protocol: v1.ProtocolTCP, + Port: 443, + TargetPort: intstr.IntOrString{IntVal: 8443}, + } + quicPort = &v1.ServicePort{ + Name: "http3-quic", + Protocol: v1.ProtocolUDP, + Port: 443, + TargetPort: intstr.IntOrString{IntVal: 8443}, + } + httpPort = &v1.ServicePort{ + Name: "http-port", + Protocol: v1.ProtocolTCP, + Port: 80, + TargetPort: intstr.IntOrString{IntVal: 8080}, + } + httpNoProtoPort = &v1.ServicePort{ + Name: "http-port", + Port: 80, + TargetPort: intstr.IntOrString{IntVal: 8080}, + } + mysqlPort = &v1.ServicePort{ + Name: "mysql-port", + Protocol: v1.ProtocolTCP, + Port: 3306, + } + istioHealthcheckPort = &v1.ServicePort{ + Name: "status-port", + Protocol: v1.ProtocolTCP, + Port: 15021, + } + istioMetricsPort = &v1.ServicePort{ + Name: "metrics-port", + Protocol: v1.ProtocolTCP, + Port: 15020, + } + httpBaseBarPort = &v1.ServicePort{ + Name: "http-bar-base", + Protocol: v1.ProtocolTCP, + Port: 9000, + } + httpOverlayBarPort = &v1.ServicePort{ + Name: "http-bar-overlay", + Protocol: v1.ProtocolTCP, + Port: 9000, + } + httpOverlayDiffProtocolBarPort = &v1.ServicePort{ + Name: "http3-bar-overlay", + Protocol: v1.ProtocolUDP, + Port: 9000, + } + httpFooPort = &v1.ServicePort{ + Name: "http-foo", + Protocol: v1.ProtocolTCP, + Port: 8080, + } + httpFooProtocolOmittedPort = &v1.ServicePort{ + Name: "http-foo", + Port: 8080, + } +) + +func TestStrategicPortMergeByPortAndProtocol(t *testing.T) { + for _, tt := range []struct { + name string + basePorts []*v1.ServicePort + overlayPorts []*v1.ServicePort + expectedMergedPorts []*v1.ServicePort + }{ + { + name: "both base and overlay are nil", + basePorts: nil, + overlayPorts: nil, + expectedMergedPorts: nil, + }, + { + name: "overlay is nil", + basePorts: []*v1.ServicePort{httpPort, httpsPort, quicPort}, + overlayPorts: nil, + expectedMergedPorts: []*v1.ServicePort{httpPort, httpsPort, quicPort}, + }, + { + name: "base is nil", + basePorts: nil, + overlayPorts: []*v1.ServicePort{httpPort, httpsPort, quicPort}, + expectedMergedPorts: []*v1.ServicePort{httpPort, httpsPort, quicPort}, + }, + { + name: "same base and overlay", + basePorts: []*v1.ServicePort{httpPort, httpsPort}, + overlayPorts: []*v1.ServicePort{httpsPort, httpPort}, + expectedMergedPorts: []*v1.ServicePort{httpPort, httpsPort}, + }, + { + name: "base and overlay for the same port, different protocol", + basePorts: []*v1.ServicePort{httpPort, httpsPort, mysqlPort}, + overlayPorts: []*v1.ServicePort{quicPort}, + expectedMergedPorts: []*v1.ServicePort{httpPort, httpsPort, mysqlPort, quicPort}, + }, + { + name: "base and overlay with different ports", + basePorts: []*v1.ServicePort{httpPort}, + overlayPorts: []*v1.ServicePort{httpsPort}, + expectedMergedPorts: []*v1.ServicePort{httpPort, httpsPort}, + }, + { + name: "implicit ports", + basePorts: []*v1.ServicePort{httpPort}, + overlayPorts: []*v1.ServicePort{httpNoProtoPort}, + expectedMergedPorts: []*v1.ServicePort{httpPort}, + }, + { + name: "status and metrics port are present", + basePorts: []*v1.ServicePort{istioHealthcheckPort, istioMetricsPort, httpsPort}, + overlayPorts: []*v1.ServicePort{httpsPort, httpPort}, + expectedMergedPorts: []*v1.ServicePort{istioHealthcheckPort, istioMetricsPort, httpsPort, httpPort}, + }, + { + name: "status port is present", + basePorts: []*v1.ServicePort{istioHealthcheckPort, httpsPort, httpPort}, + overlayPorts: []*v1.ServicePort{httpsPort, httpPort}, + expectedMergedPorts: []*v1.ServicePort{istioHealthcheckPort, httpsPort, httpPort}, + }, + { + name: "metrics port is present", + basePorts: []*v1.ServicePort{istioMetricsPort, httpsPort, httpPort}, + overlayPorts: []*v1.ServicePort{httpsPort, httpPort}, + expectedMergedPorts: []*v1.ServicePort{istioMetricsPort, httpsPort, httpPort}, + }, + { + name: "overlay with port name changed", + basePorts: []*v1.ServicePort{httpBaseBarPort}, + overlayPorts: []*v1.ServicePort{httpOverlayBarPort}, + expectedMergedPorts: []*v1.ServicePort{httpOverlayBarPort}, + }, + { + name: "overlay with different protocol", + basePorts: []*v1.ServicePort{httpBaseBarPort}, + overlayPorts: []*v1.ServicePort{httpOverlayDiffProtocolBarPort}, + expectedMergedPorts: []*v1.ServicePort{httpBaseBarPort, httpOverlayDiffProtocolBarPort}, + }, + { + name: "same base and overlay with protocol omitted for overlay", + basePorts: []*v1.ServicePort{httpFooPort}, + overlayPorts: []*v1.ServicePort{httpFooProtocolOmittedPort}, + expectedMergedPorts: []*v1.ServicePort{httpFooPort}, + }, + { + name: "same base and overlay with protocol omitted for base", + basePorts: []*v1.ServicePort{httpFooProtocolOmittedPort}, + overlayPorts: []*v1.ServicePort{httpFooPort}, + expectedMergedPorts: []*v1.ServicePort{httpFooPort}, + }, + } { + t.Run(tt.name, func(t *testing.T) { + actualMergedPorts := strategicMergePorts(tt.basePorts, tt.overlayPorts) + if diff := cmp.Diff(actualMergedPorts, tt.expectedMergedPorts); diff != "" { + t.Fatalf("expected differs from actual. Diff:\n%s", diff) + } + }) + } +} diff --git a/operator/pkg/translate/translate.go b/operator/pkg/translate/translate.go new file mode 100644 index 0000000..2d9dc3e --- /dev/null +++ b/operator/pkg/translate/translate.go @@ -0,0 +1,1062 @@ +// Copyright Istio 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 translate defines translations from installer proto to values.yaml. +package translate + +import ( + "encoding/json" + "fmt" + "reflect" + "sort" + "strings" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/yaml" + + "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/apis/istio" + iopv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/object" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/version" + oversion "github.com/jehawley/istio/operator/version" + "istio.io/istio/pkg/log" + "istio.io/istio/pkg/util/sets" +) + +const ( + // HelmValuesEnabledSubpath is the subpath from the component root to the enabled parameter. + HelmValuesEnabledSubpath = "enabled" + // HelmValuesNamespaceSubpath is the subpath from the component root to the namespace parameter. + HelmValuesNamespaceSubpath = "namespace" + // HelmValuesHubSubpath is the subpath from the component root to the hub parameter. + HelmValuesHubSubpath = "hub" + // HelmValuesTagSubpath is the subpath from the component root to the tag parameter. + HelmValuesTagSubpath = "tag" +) + +var scope = log.RegisterScope("translator", "API translator") + +// Translator is a set of mappings to translate between API paths, charts, values.yaml and k8s paths. +type Translator struct { + // Translations remain the same within a minor version. + Version version.MinorVersion + // APIMapping is a mapping between an API path and the corresponding values.yaml path using longest prefix + // match. If the path is a non-leaf node, the output path is the matching portion of the path, plus any remaining + // output path. + APIMapping map[string]*Translation `yaml:"apiMapping"` + // KubernetesMapping defines mappings from an IstioOperator API paths to k8s resource paths. + KubernetesMapping map[string]*Translation `yaml:"kubernetesMapping"` + // GlobalNamespaces maps feature namespaces to Helm global namespace definitions. + GlobalNamespaces map[name.ComponentName]string `yaml:"globalNamespaces"` + // ComponentMaps is a set of mappings for each Istio component. + ComponentMaps map[name.ComponentName]*ComponentMaps `yaml:"componentMaps"` +} + +// ComponentMaps is a set of mappings for an Istio component. +type ComponentMaps struct { + // ResourceType maps a ComponentName to the type of the rendered k8s resource. + ResourceType string + // ResourceName maps a ComponentName to the name of the rendered k8s resource. + ResourceName string + // ContainerName maps a ComponentName to the name of the container in a Deployment. + ContainerName string + // HelmSubdir is a mapping between a component name and the subdirectory of the component Chart. + HelmSubdir string + // ToHelmValuesTreeRoot is the tree root in values YAML files for the component. + ToHelmValuesTreeRoot string + // SkipReverseTranslate defines whether reverse translate of this component need to be skipped. + SkipReverseTranslate bool + // FlattenValues, if true, means the component expects values not prefixed with ToHelmValuesTreeRoot + // For example `.name=foo` instead of `.component.name=foo`. + FlattenValues bool +} + +// TranslationFunc maps a yamlStr API path into a YAML values tree. +type TranslationFunc func(t *Translation, root map[string]any, valuesPath string, value any) error + +// Translation is a mapping to an output path using a translation function. +type Translation struct { + // OutPath defines the position in the yaml file + OutPath string `yaml:"outPath"` + translationFunc TranslationFunc +} + +// NewTranslator creates a new translator for minorVersion and returns a ptr to it. +func NewTranslator() *Translator { + t := &Translator{ + Version: oversion.OperatorBinaryVersion.MinorVersion, + APIMapping: map[string]*Translation{ + "hub": {OutPath: "global.hub"}, + "tag": {OutPath: "global.tag"}, + "revision": {OutPath: "revision"}, + "meshConfig": {OutPath: "meshConfig"}, + "compatibilityVersion": {OutPath: "compatibilityVersion"}, + }, + GlobalNamespaces: map[name.ComponentName]string{ + name.PilotComponentName: "istioNamespace", + }, + ComponentMaps: map[name.ComponentName]*ComponentMaps{ + name.IstioBaseComponentName: { + HelmSubdir: "base", + ToHelmValuesTreeRoot: "global", + SkipReverseTranslate: true, + }, + name.PilotComponentName: { + ResourceType: "Deployment", + ResourceName: "istiod", + ContainerName: "discovery", + HelmSubdir: "istio-control/istio-discovery", + ToHelmValuesTreeRoot: "pilot", + }, + name.IngressComponentName: { + ResourceType: "Deployment", + ResourceName: "istio-ingressgateway", + ContainerName: "istio-proxy", + HelmSubdir: "gateways/istio-ingress", + ToHelmValuesTreeRoot: "gateways.istio-ingressgateway", + }, + name.EgressComponentName: { + ResourceType: "Deployment", + ResourceName: "istio-egressgateway", + ContainerName: "istio-proxy", + HelmSubdir: "gateways/istio-egress", + ToHelmValuesTreeRoot: "gateways.istio-egressgateway", + }, + name.CNIComponentName: { + ResourceType: "DaemonSet", + ResourceName: "istio-cni-node", + ContainerName: "install-cni", + HelmSubdir: "istio-cni", + ToHelmValuesTreeRoot: "cni", + }, + name.IstiodRemoteComponentName: { + HelmSubdir: "istiod-remote", + ToHelmValuesTreeRoot: "global", + SkipReverseTranslate: true, + }, + name.ZtunnelComponentName: { + ResourceType: "DaemonSet", + ResourceName: "ztunnel", + HelmSubdir: "ztunnel", + ToHelmValuesTreeRoot: "ztunnel", + ContainerName: "istio-proxy", + FlattenValues: true, + }, + }, + // nolint: lll + KubernetesMapping: map[string]*Translation{ + "Components.{{.ComponentName}}.K8S.Affinity": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.affinity"}, + "Components.{{.ComponentName}}.K8S.Env": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.containers.[name:{{.ContainerName}}].env"}, + "Components.{{.ComponentName}}.K8S.HpaSpec": {OutPath: "[HorizontalPodAutoscaler:{{.ResourceName}}].spec"}, + "Components.{{.ComponentName}}.K8S.ImagePullPolicy": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.containers.[name:{{.ContainerName}}].imagePullPolicy"}, + "Components.{{.ComponentName}}.K8S.NodeSelector": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.nodeSelector"}, + "Components.{{.ComponentName}}.K8S.PodDisruptionBudget": {OutPath: "[PodDisruptionBudget:{{.ResourceName}}].spec"}, + "Components.{{.ComponentName}}.K8S.PodAnnotations": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.metadata.annotations"}, + "Components.{{.ComponentName}}.K8S.PriorityClassName": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.priorityClassName."}, + "Components.{{.ComponentName}}.K8S.ReadinessProbe": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.containers.[name:{{.ContainerName}}].readinessProbe"}, + "Components.{{.ComponentName}}.K8S.ReplicaCount": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.replicas"}, + "Components.{{.ComponentName}}.K8S.Resources": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.containers.[name:{{.ContainerName}}].resources"}, + "Components.{{.ComponentName}}.K8S.Strategy": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.strategy"}, + "Components.{{.ComponentName}}.K8S.Tolerations": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.tolerations"}, + "Components.{{.ComponentName}}.K8S.ServiceAnnotations": {OutPath: "[Service:{{.ResourceName}}].metadata.annotations"}, + "Components.{{.ComponentName}}.K8S.Service": {OutPath: "[Service:{{.ResourceName}}].spec"}, + "Components.{{.ComponentName}}.K8S.SecurityContext": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.securityContext"}, + }, + } + return t +} + +// OverlayK8sSettings overlays k8s settings from iop over the manifest objects, based on t's translation mappings. +func (t *Translator) OverlayK8sSettings(yml string, iop *v1alpha1.IstioOperatorSpec, componentName name.ComponentName, + resourceName string, index int) (string, error, +) { + // om is a map of kind:name string to Object ptr. + // This is lazy loaded to avoid parsing when there are no overlays + var om map[string]*object.K8sObject + var objects object.K8sObjects + + for inPath, v := range t.KubernetesMapping { + inPath, err := renderFeatureComponentPathTemplate(inPath, componentName) + if err != nil { + return "", err + } + renderedInPath := strings.Replace(inPath, "gressGateways.", "gressGateways."+fmt.Sprint(index)+".", 1) + scope.Debugf("Checking for path %s in IstioOperatorSpec", renderedInPath) + + m, found, err := tpath.GetFromStructPath(iop, renderedInPath) + if err != nil { + return "", err + } + if !found { + scope.Debugf("path %s not found in IstioOperatorSpec, skip mapping.", renderedInPath) + continue + } + if mstr, ok := m.(string); ok && mstr == "" { + scope.Debugf("path %s is empty string, skip mapping.", renderedInPath) + continue + } + // Zero int values are due to proto3 compiling to scalars rather than ptrs. Skip these because values of 0 are + // the default in destination fields and need not be set explicitly. + if mint, ok := util.ToIntValue(m); ok && mint == 0 { + scope.Debugf("path %s is int 0, skip mapping.", renderedInPath) + continue + } + if componentName == name.IstioBaseComponentName { + return "", fmt.Errorf("base component can only have k8s.overlays, not other K8s settings") + } + inPathParts := strings.Split(inPath, ".") + outPath, err := t.renderResourceComponentPathTemplate(v.OutPath, componentName, resourceName, iop.Revision) + if err != nil { + return "", err + } + scope.Debugf("path has value in IstioOperatorSpec, mapping to output path %s", outPath) + path := util.PathFromString(outPath) + pe := path[0] + // Output path must start with [kind:name], which is used to map to the object to overlay. + if !util.IsKVPathElement(pe) { + return "", fmt.Errorf("path %s has an unexpected first element %s in OverlayK8sSettings", path, pe) + } + + // We need to apply overlay, lazy load om + if om == nil { + objects, err = object.ParseK8sObjectsFromYAMLManifest(yml) + if err != nil { + return "", err + } + if scope.DebugEnabled() { + scope.Debugf("Manifest contains the following objects:") + for _, o := range objects { + scope.Debugf("%s", o.HashNameKind()) + } + } + om = objects.ToNameKindMap() + } + + // After brackets are removed, the remaining "kind:name" is the same format as the keys in om. + pe, _ = util.RemoveBrackets(pe) + oo, ok := om[pe] + if !ok { + // skip to overlay the K8s settings if the corresponding resource doesn't exist. + scope.Infof("resource Kind:name %s doesn't exist in the output manifest, skip overlay.", pe) + continue + } + + // When autoscale is enabled we should not overwrite replica count, consider following scenario: + // 0. Set values.pilot.autoscaleEnabled=true, components.pilot.k8s.replicaCount=1 + // 1. In istio operator it "caches" the generated manifests (with istiod.replicas=1) + // 2. HPA autoscales our pilot replicas to 3 + // 3. Set values.pilot.autoscaleEnabled=false + // 4. The generated manifests (with istiod.replicas=1) is same as istio operator "cache", + // the deployment will not get updated unless istio operator is restarted. + if inPathParts[len(inPathParts)-1] == "ReplicaCount" { + if skipReplicaCountWithAutoscaleEnabled(iop, componentName) { + continue + } + } + + // strategic merge overlay m to the base object oo + mergedObj, err := MergeK8sObject(oo, m, path[1:]) + if err != nil { + return "", err + } + + // Apply the workaround for merging service ports with (port,protocol) composite + // keys instead of just the merging by port. + if inPathParts[len(inPathParts)-1] == "Service" { + if msvc, ok := m.(*v1alpha1.ServiceSpec); ok { + mergedObj, err = t.fixMergedObjectWithCustomServicePortOverlay(oo, msvc, mergedObj) + if err != nil { + return "", err + } + } + } + + // Update the original object in objects slice, since the output should be ordered. + *(om[pe]) = *mergedObj + } + + if objects != nil { + return objects.YAMLManifest() + } + return yml, nil +} + +var componentToAutoScaleEnabledPath = map[name.ComponentName]string{ + name.PilotComponentName: "pilot.autoscaleEnabled", + name.IngressComponentName: "gateways.istio-ingressgateway.autoscaleEnabled", + name.EgressComponentName: "gateways.istio-egressgateway.autoscaleEnabled", +} + +func skipReplicaCountWithAutoscaleEnabled(iop *v1alpha1.IstioOperatorSpec, componentName name.ComponentName) bool { + values := iop.GetValues().AsMap() + path, ok := componentToAutoScaleEnabledPath[componentName] + if !ok { + return false + } + + enabledVal, found, err := tpath.GetFromStructPath(values, path) + if err != nil || !found { + return false + } + + enabled, ok := enabledVal.(bool) + return ok && enabled +} + +func (t *Translator) fixMergedObjectWithCustomServicePortOverlay(oo *object.K8sObject, + msvc *v1alpha1.ServiceSpec, mergedObj *object.K8sObject, +) (*object.K8sObject, error) { + var basePorts []*v1.ServicePort + bps, _, err := unstructured.NestedSlice(oo.Unstructured(), "spec", "ports") + if err != nil { + return nil, err + } + bby, err := json.Marshal(bps) + if err != nil { + return nil, err + } + if err = json.Unmarshal(bby, &basePorts); err != nil { + return nil, err + } + overlayPorts := make([]*v1.ServicePort, 0, len(msvc.GetPorts())) + for _, p := range msvc.GetPorts() { + var pr v1.Protocol + switch strings.ToLower(p.GetProtocol()) { + case "udp": + pr = v1.ProtocolUDP + default: + pr = v1.ProtocolTCP + } + port := &v1.ServicePort{ + Name: p.GetName(), + Protocol: pr, + Port: p.GetPort(), + NodePort: p.GetNodePort(), + } + if p.GetAppProtocol() != "" { + ap := p.AppProtocol + port.AppProtocol = &ap + } + if p.TargetPort != nil { + port.TargetPort = p.TargetPort.ToKubernetes() + } + overlayPorts = append(overlayPorts, port) + } + mergedPorts := strategicMergePorts(basePorts, overlayPorts) + mpby, err := json.Marshal(mergedPorts) + if err != nil { + return nil, err + } + var mergedPortSlice []any + if err = json.Unmarshal(mpby, &mergedPortSlice); err != nil { + return nil, err + } + if err = unstructured.SetNestedSlice(mergedObj.Unstructured(), mergedPortSlice, "spec", "ports"); err != nil { + return nil, err + } + // Now fix the merged object + mjsonby, err := json.Marshal(mergedObj.Unstructured()) + if err != nil { + return nil, err + } + if mergedObj, err = object.ParseJSONToK8sObject(mjsonby); err != nil { + return nil, err + } + return mergedObj, nil +} + +type portWithProtocol struct { + port int32 + protocol v1.Protocol +} + +func portIndexOf(element portWithProtocol, data []portWithProtocol) int { + for k, v := range data { + if element == v { + return k + } + } + return len(data) +} + +// strategicMergePorts merges the base with the given overlay considering both +// port and the protocol as the merge keys. This is a workaround for the strategic +// merge patch in Kubernetes which only uses port number as the key. This causes +// an issue when we have to expose the same port with different protocols. +// See - https://github.com/kubernetes/kubernetes/issues/103544 +// TODO(su225): Remove this once the above issue is addressed in Kubernetes +func strategicMergePorts(base, overlay []*v1.ServicePort) []*v1.ServicePort { + // We want to keep the original port order with base first and then the newly + // added ports through the overlay. This is because there are some cases where + // port order actually matters. For instance, some cloud load balancers use the + // first port for health-checking (in Istio it is 15021). So we must keep maintain + // it in order not to break the users + // See - https://github.com/istio/istio/issues/12503 for more information + // + // Or changing port order might generate weird diffs while upgrading or changing + // IstioOperator spec. It is annoying. So better maintain original order while + // appending newly added ports through overlay. + portPriority := make([]portWithProtocol, 0, len(base)+len(overlay)) + for _, p := range base { + if p.Protocol == "" { + p.Protocol = v1.ProtocolTCP + } + portPriority = append(portPriority, portWithProtocol{port: p.Port, protocol: p.Protocol}) + } + for _, p := range overlay { + if p.Protocol == "" { + p.Protocol = v1.ProtocolTCP + } + portPriority = append(portPriority, portWithProtocol{port: p.Port, protocol: p.Protocol}) + } + sortFn := func(ps []*v1.ServicePort) func(int, int) bool { + return func(i, j int) bool { + pi := portIndexOf(portWithProtocol{port: ps[i].Port, protocol: ps[i].Protocol}, portPriority) + pj := portIndexOf(portWithProtocol{port: ps[j].Port, protocol: ps[j].Protocol}, portPriority) + return pi < pj + } + } + if overlay == nil { + sort.Slice(base, sortFn(base)) + return base + } + if base == nil { + sort.Slice(overlay, sortFn(overlay)) + return overlay + } + // first add the base and then replace appropriate + // keys with the items in the overlay list + merged := make(map[portWithProtocol]*v1.ServicePort) + for _, p := range base { + key := portWithProtocol{port: p.Port, protocol: p.Protocol} + merged[key] = p + } + for _, p := range overlay { + key := portWithProtocol{port: p.Port, protocol: p.Protocol} + merged[key] = p + } + res := make([]*v1.ServicePort, 0, len(merged)) + for _, pv := range merged { + res = append(res, pv) + } + sort.Slice(res, sortFn(res)) + return res +} + +// ProtoToValues traverses the supplied IstioOperatorSpec and returns a values.yaml translation from it. +func (t *Translator) ProtoToValues(ii *v1alpha1.IstioOperatorSpec) (string, error) { + root, err := t.ProtoToHelmValues2(ii) + if err != nil { + return "", err + } + + // Special additional handling not covered by simple translation rules. + if err := t.setComponentProperties(root, ii); err != nil { + return "", err + } + + // Return blank string for empty case. + if len(root) == 0 { + return "", nil + } + + y, err := yaml.Marshal(root) + if err != nil { + return "", err + } + + return string(y), nil +} + +// Fields, beyond 'global', that apply to each chart at the top level of values.yaml +var topLevelFields = sets.New( + "ownerName", + "revision", + "compatibilityVersion", + "profile", +) + +// TranslateHelmValues creates a Helm values.yaml config data tree from iop using the given translator. +func (t *Translator) TranslateHelmValues(iop *v1alpha1.IstioOperatorSpec, componentsSpec any, componentName name.ComponentName) (string, error) { + apiVals := make(map[string]any) + + // First, translate the IstioOperator API to helm Values. + apiValsStr, err := t.ProtoToValues(iop) + if err != nil { + return "", err + } + err = yaml.Unmarshal([]byte(apiValsStr), &apiVals) + if err != nil { + return "", err + } + + scope.Debugf("Values translated from IstioOperator API:\n%s", apiValsStr) + + // Add global overlay from IstioOperatorSpec.Values/UnvalidatedValues. + globalVals := iop.GetValues().AsMap() + globalUnvalidatedVals := iop.GetUnvalidatedValues().AsMap() + + if scope.DebugEnabled() { + scope.Debugf("Values from IstioOperatorSpec.Values:\n%s", util.ToYAML(globalVals)) + scope.Debugf("Values from IstioOperatorSpec.UnvalidatedValues:\n%s", util.ToYAML(globalUnvalidatedVals)) + } + + mergedVals, err := util.OverlayTrees(apiVals, globalVals) + if err != nil { + return "", err + } + mergedVals, err = util.OverlayTrees(mergedVals, globalUnvalidatedVals) + if err != nil { + return "", err + } + c, f := t.ComponentMaps[componentName] + if f && c.FlattenValues { + components, ok := mergedVals[c.ToHelmValuesTreeRoot].(map[string]any) + if !ok { + return "", fmt.Errorf("component value isn't a map") + } + finalVals := map[string]any{} + // These get access to globals + finalVals["global"] = mergedVals["global"] + // strip out anything from the original apiVals which are a map[string]any but populate other top-level fields + for k, v := range apiVals { + _, isMap := v.(map[string]any) + if !isMap { + finalVals[k] = v + } + } + for k := range topLevelFields { + if v, f := mergedVals[k]; f { + finalVals[k] = v + } + } + for k, v := range components { + finalVals[k] = v + } + mergedVals = finalVals + } + + mergedYAML, err := yaml.Marshal(mergedVals) + if err != nil { + return "", err + } + + mergedYAML, err = applyGatewayTranslations(mergedYAML, componentName, componentsSpec) + if err != nil { + return "", err + } + + return string(mergedYAML), err +} + +// applyGatewayTranslations writes gateway name gwName at the appropriate values path in iop and maps k8s.service.ports +// to values. It returns the resulting YAML tree. +func applyGatewayTranslations(iop []byte, componentName name.ComponentName, componentSpec any) ([]byte, error) { + if !componentName.IsGateway() { + return iop, nil + } + iopt := make(map[string]any) + if err := yaml.Unmarshal(iop, &iopt); err != nil { + return nil, err + } + gwSpec := componentSpec.(*v1alpha1.GatewaySpec) + k8s := gwSpec.K8S + switch componentName { + case name.IngressComponentName: + setYAMLNodeByMapPath(iopt, util.PathFromString("gateways.istio-ingressgateway.name"), gwSpec.Name) + if len(gwSpec.Label) != 0 { + setYAMLNodeByMapPath(iopt, util.PathFromString("gateways.istio-ingressgateway.labels"), gwSpec.Label) + } + if k8s != nil && k8s.Service != nil && k8s.Service.Ports != nil { + setYAMLNodeByMapPath(iopt, util.PathFromString("gateways.istio-ingressgateway.ports"), k8s.Service.Ports) + } + case name.EgressComponentName: + setYAMLNodeByMapPath(iopt, util.PathFromString("gateways.istio-egressgateway.name"), gwSpec.Name) + if len(gwSpec.Label) != 0 { + setYAMLNodeByMapPath(iopt, util.PathFromString("gateways.istio-egressgateway.labels"), gwSpec.Label) + } + if k8s != nil && k8s.Service != nil && k8s.Service.Ports != nil { + setYAMLNodeByMapPath(iopt, util.PathFromString("gateways.istio-egressgateway.ports"), k8s.Service.Ports) + } + } + return yaml.Marshal(iopt) +} + +// setYAMLNodeByMapPath sets the value at the given path to val in treeNode. The path cannot traverse lists and +// treeNode must be a YAML tree unmarshaled into a plain map data structure. +func setYAMLNodeByMapPath(treeNode any, path util.Path, val any) { + if len(path) == 0 || treeNode == nil { + return + } + pe := path[0] + switch nt := treeNode.(type) { + case map[any]any: + if len(path) == 1 { + nt[pe] = val + return + } + if nt[pe] == nil { + return + } + setYAMLNodeByMapPath(nt[pe], path[1:], val) + case map[string]any: + if len(path) == 1 { + nt[pe] = val + return + } + if nt[pe] == nil { + return + } + setYAMLNodeByMapPath(nt[pe], path[1:], val) + } +} + +// ComponentMap returns a ComponentMaps struct ptr for the given component name if one exists. +// If the name of the component is lower case, the function will use the capitalized version +// of the name. +func (t *Translator) ComponentMap(cns string) *ComponentMaps { + cn := name.TitleCase(name.ComponentName(cns)) + return t.ComponentMaps[cn] +} + +func (t *Translator) ProtoToHelmValues2(ii *v1alpha1.IstioOperatorSpec) (map[string]any, error) { + by, err := json.Marshal(ii) + if err != nil { + return nil, err + } + res := map[string]any{} + err = json.Unmarshal(by, &res) + if err != nil { + return nil, err + } + r2 := map[string]any{} + errs := t.ProtoToHelmValues(res, r2, nil) + return r2, errs.ToError() +} + +// ProtoToHelmValues function below is used by third party for integrations and has to be public + +// ProtoToHelmValues takes an interface which must be a struct ptr and recursively iterates through all its fields. +// For each leaf, if looks for a mapping from the struct data path to the corresponding YAML path and if one is +// found, it calls the associated mapping function if one is defined to populate the values YAML path. +// If no mapping function is defined, it uses the default mapping function. +func (t *Translator) ProtoToHelmValues(node any, root map[string]any, path util.Path) (errs util.Errors) { + scope.Debugf("ProtoToHelmValues with path %s, %v (%T)", path, node, node) + if util.IsValueNil(node) { + return nil + } + + vv := reflect.ValueOf(node) + vt := reflect.TypeOf(node) + switch vt.Kind() { + case reflect.Ptr: + if !util.IsNilOrInvalidValue(vv.Elem()) { + errs = util.AppendErrs(errs, t.ProtoToHelmValues(vv.Elem().Interface(), root, path)) + } + case reflect.Struct: + scope.Debug("Struct") + for i := 0; i < vv.NumField(); i++ { + fieldName := vv.Type().Field(i).Name + fieldValue := vv.Field(i) + scope.Debugf("Checking field %s", fieldName) + if a, ok := vv.Type().Field(i).Tag.Lookup("json"); ok && a == "-" { + continue + } + if !fieldValue.CanInterface() { + continue + } + errs = util.AppendErrs(errs, t.ProtoToHelmValues(fieldValue.Interface(), root, append(path, fieldName))) + } + case reflect.Map: + scope.Debug("Map") + for _, key := range vv.MapKeys() { + nnp := append(path, key.String()) + errs = util.AppendErrs(errs, t.insertLeaf(root, nnp, vv.MapIndex(key))) + } + case reflect.Slice: + scope.Debug("Slice") + for i := 0; i < vv.Len(); i++ { + errs = util.AppendErrs(errs, t.ProtoToHelmValues(vv.Index(i).Interface(), root, path)) + } + default: + // Must be a leaf + scope.Debugf("field has kind %s", vt.Kind()) + if vv.CanInterface() { + errs = util.AppendErrs(errs, t.insertLeaf(root, path, vv)) + } + } + + return errs +} + +// setComponentProperties translates properties (e.g., enablement and namespace) of each component +// in the baseYAML values tree, based on feature/component inheritance relationship. +func (t *Translator) setComponentProperties(root map[string]any, iop *v1alpha1.IstioOperatorSpec) error { + var keys []string + for k := range t.ComponentMaps { + if k != name.IngressComponentName && k != name.EgressComponentName { + keys = append(keys, string(k)) + } + } + sort.Strings(keys) + l := len(keys) + for i := l - 1; i >= 0; i-- { + cn := name.ComponentName(keys[i]) + c := t.ComponentMaps[cn] + e, err := t.IsComponentEnabled(cn, iop) + if err != nil { + return err + } + + enablementPath := c.ToHelmValuesTreeRoot + // CNI calls itself "cni" in the chart but "istio_cni" for enablement outside of the chart. + if cn == name.CNIComponentName { + enablementPath = "istio_cni" + } + if err := tpath.WriteNode(root, util.PathFromString(enablementPath+"."+HelmValuesEnabledSubpath), e); err != nil { + return err + } + + ns, err := name.Namespace(cn, iop) + if err != nil { + return err + } + if err := tpath.WriteNode(root, util.PathFromString(c.ToHelmValuesTreeRoot+"."+HelmValuesNamespaceSubpath), ns); err != nil { + return err + } + + hub, found, _ := tpath.GetFromStructPath(iop, "Components."+string(cn)+".Hub") + // Unmarshal unfortunately creates struct fields with "" for unset values. Skip these cases to avoid + // overwriting current value with an empty string. + hubStr, ok := hub.(string) + if found && !(ok && hubStr == "") { + if err := tpath.WriteNode(root, util.PathFromString(c.ToHelmValuesTreeRoot+"."+HelmValuesHubSubpath), hub); err != nil { + return err + } + } + + tag, found, _ := tpath.GetFromStructPath(iop, "Components."+string(cn)+".Tag") + tagv, ok := tag.(*structpb.Value) + if found && !(ok && util.ValueString(tagv) == "") { + if err := tpath.WriteNode(root, util.PathFromString(c.ToHelmValuesTreeRoot+"."+HelmValuesTagSubpath), util.ValueString(tagv)); err != nil { + return err + } + } + } + + for cn, gns := range t.GlobalNamespaces { + ns, err := name.Namespace(cn, iop) + if err != nil { + return err + } + if err := tpath.WriteNode(root, util.PathFromString("global."+gns), ns); err != nil { + return err + } + } + + return nil +} + +// IsComponentEnabled reports whether the component with name cn is enabled, according to the translations in t, +// and the contents of ocp. +func (t *Translator) IsComponentEnabled(cn name.ComponentName, iop *v1alpha1.IstioOperatorSpec) (bool, error) { + if t.ComponentMaps[cn] == nil { + return false, nil + } + return IsComponentEnabledInSpec(cn, iop) +} + +// insertLeaf inserts a leaf with value into root at path, which is first mapped using t.APIMapping. +func (t *Translator) insertLeaf(root map[string]any, path util.Path, value reflect.Value) (errs util.Errors) { + // Must be a scalar leaf. See if we have a mapping. + valuesPath, m := getValuesPathMapping(t.APIMapping, path) + var v any + if value.Kind() == reflect.Ptr { + v = value.Elem().Interface() + } else { + v = value.Interface() + } + switch { + case m == nil: + break + case m.translationFunc == nil: + // Use default translation which just maps to a different part of the tree. + errs = util.AppendErr(errs, defaultTranslationFunc(m, root, valuesPath, v)) + default: + // Use a custom translation function. + errs = util.AppendErr(errs, m.translationFunc(m, root, valuesPath, v)) + } + return errs +} + +// getValuesPathMapping tries to map path against the passed in mappings with a longest prefix match. If a matching prefix +// is found, it returns the translated YAML path and the corresponding translation. +// e.g. for mapping "a.b" -> "1.2", the input path "a.b.c.d" would yield "1.2.c.d". +func getValuesPathMapping(mappings map[string]*Translation, path util.Path) (string, *Translation) { + p := path + var m *Translation + for ; len(p) > 0; p = p[0 : len(p)-1] { + m = mappings[p.String()] + if m != nil { + break + } + } + if m == nil { + return "", nil + } + + if m.OutPath == "" { + return "", m + } + + out := m.OutPath + "." + path[len(p):].String() + scope.Debugf("translating %s to %s", path, out) + return out, m +} + +// renderFeatureComponentPathTemplate renders a template of the form {{.ComponentName}} with +// the supplied parameters. +func renderFeatureComponentPathTemplate(tmpl string, componentName name.ComponentName) (string, error) { + type Temp struct { + ComponentName name.ComponentName + } + ts := Temp{ + ComponentName: componentName, + } + return util.RenderTemplate(tmpl, ts) +} + +// renderResourceComponentPathTemplate renders a template of the form {{.ResourceName}}{{.ContainerName}} with +// the supplied parameters. +func (t *Translator) renderResourceComponentPathTemplate(tmpl string, componentName name.ComponentName, + resourceName, revision string, +) (string, error) { + cn := string(componentName) + cmp := t.ComponentMap(cn) + if cmp == nil { + return "", fmt.Errorf("component: %s does not exist in the componentMap", cn) + } + if resourceName == "" { + resourceName = cmp.ResourceName + } + // The istiod resource will be istiod-, so we need to append the revision suffix + if revision != "" && resourceName == "istiod" { + resourceName += "-" + revision + } + ts := struct { + ResourceType string + ResourceName string + ContainerName string + }{ + ResourceType: cmp.ResourceType, + ResourceName: resourceName, + ContainerName: cmp.ContainerName, + } + return util.RenderTemplate(tmpl, ts) +} + +// defaultTranslationFunc is the default translation to values. It maps a Go data path into a YAML path. +func defaultTranslationFunc(m *Translation, root map[string]any, valuesPath string, value any) error { + var path []string + + if util.IsEmptyString(value) { + scope.Debugf("Skip empty string value for path %s", m.OutPath) + return nil + } + if valuesPath == "" { + scope.Debugf("Not mapping to values, resources path is %s", m.OutPath) + return nil + } + + for _, p := range util.PathFromString(valuesPath) { + path = append(path, firstCharToLower(p)) + } + + return tpath.WriteNode(root, path, value) +} + +func firstCharToLower(s string) string { + return strings.ToLower(s[0:1]) + s[1:] +} + +// MergeK8sObject function below is used by third party for integrations and has to be public + +// MergeK8sObject does strategic merge for overlayNode on the base object. +func MergeK8sObject(base *object.K8sObject, overlayNode any, path util.Path) (*object.K8sObject, error) { + overlay, err := createPatchObjectFromPath(overlayNode, path) + if err != nil { + return nil, err + } + overlayYAML, err := yaml.Marshal(overlay) + if err != nil { + return nil, err + } + overlayJSON, err := yaml.YAMLToJSON(overlayYAML) + if err != nil { + return nil, fmt.Errorf("yamlToJSON error in overlayYAML: %s\n%s", err, overlayYAML) + } + baseJSON, err := base.JSON() + if err != nil { + return nil, err + } + + // get a versioned object from the scheme, we can use the strategic patching mechanism + // (i.e. take advantage of patchStrategy in the type) + versionedObject, err := scheme.Scheme.New(base.GroupVersionKind()) + if err != nil { + return nil, err + } + // strategic merge patch + newBytes, err := strategicpatch.StrategicMergePatch(baseJSON, overlayJSON, versionedObject) + if err != nil { + return nil, fmt.Errorf("get error: %s to merge patch:\n%s for base:\n%s", err, overlayJSON, baseJSON) + } + + newObj, err := object.ParseJSONToK8sObject(newBytes) + if err != nil { + return nil, err + } + + return newObj.ResolveK8sConflict(), nil +} + +// createPatchObjectFromPath constructs patch object for node with path, returns nil object and error if the path is invalid. +// e.g. node: +// - name: NEW_VAR +// value: new_value +// +// and path: +// +// spec.template.spec.containers.[name:discovery].env +// will construct the following patch object: +// spec: +// template: +// spec: +// containers: +// - name: discovery +// env: +// - name: NEW_VAR +// value: new_value +func createPatchObjectFromPath(node any, path util.Path) (map[string]any, error) { + if len(path) == 0 { + return nil, fmt.Errorf("empty path %s", path) + } + if util.IsKVPathElement(path[0]) { + return nil, fmt.Errorf("path %s has an unexpected first element %s", path, path[0]) + } + length := len(path) + if util.IsKVPathElement(path[length-1]) { + return nil, fmt.Errorf("path %s has an unexpected last element %s", path, path[length-1]) + } + + patchObj := make(map[string]any) + var currentNode, nextNode any + nextNode = patchObj + for i, pe := range path { + currentNode = nextNode + // last path element + if i == length-1 { + currentNode, ok := currentNode.(map[string]any) + if !ok { + return nil, fmt.Errorf("path %s has an unexpected non KV element %s", path, pe) + } + currentNode[pe] = node + break + } + + if util.IsKVPathElement(pe) { + currentNode, ok := currentNode.([]any) + if !ok { + return nil, fmt.Errorf("path %s has an unexpected KV element %s", path, pe) + } + k, v, err := util.PathKV(pe) + if err != nil { + return nil, err + } + if k == "" || v == "" { + return nil, fmt.Errorf("path %s has an invalid KV element %s", path, pe) + } + currentNode[0] = map[string]any{k: v} + nextNode = currentNode[0] + continue + } + + currentNode, ok := currentNode.(map[string]any) + if !ok { + return nil, fmt.Errorf("path %s has an unexpected non KV element %s", path, pe) + } + // next path element determines the next node type + if util.IsKVPathElement(path[i+1]) { + currentNode[pe] = make([]any, 1) + } else { + currentNode[pe] = make(map[string]any) + } + nextNode = currentNode[pe] + } + return patchObj, nil +} + +// IOPStoIOP takes an IstioOperatorSpec and returns a corresponding IstioOperator with the given name and namespace. +func IOPStoIOP(iops proto.Message, name, namespace string) (*iopv1alpha1.IstioOperator, error) { + iopStr, err := IOPStoIOPstr(iops, name, namespace) + if err != nil { + return nil, err + } + iop, err := istio.UnmarshalIstioOperator(iopStr, false) + if err != nil { + return nil, err + } + return iop, nil +} + +// IOPStoIOPstr takes an IstioOperatorSpec and returns a corresponding IstioOperator string with the given name and namespace. +func IOPStoIOPstr(iops proto.Message, name, namespace string) (string, error) { + iopsStr, err := util.MarshalWithJSONPB(iops) + if err != nil { + return "", err + } + spec, err := tpath.AddSpecRoot(iopsStr) + if err != nil { + return "", err + } + + tmpl := ` +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +metadata: + namespace: {{ .Namespace }} + name: {{ .Name }} +` + // Passing into template causes reformatting, use simple concatenation instead. + tmpl += spec + + type Temp struct { + Namespace string + Name string + } + ts := Temp{ + Namespace: namespace, + Name: name, + } + return util.RenderTemplate(tmpl, ts) +} diff --git a/operator/pkg/translate/translate_common.go b/operator/pkg/translate/translate_common.go new file mode 100644 index 0000000..2f9ffe2 --- /dev/null +++ b/operator/pkg/translate/translate_common.go @@ -0,0 +1,121 @@ +// Copyright Istio 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 translate + +import ( + "fmt" + + "google.golang.org/protobuf/types/known/wrapperspb" + + "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" +) + +// IsComponentEnabledInSpec reports whether the given component is enabled in the given spec. +// IsComponentEnabledInSpec assumes that controlPlaneSpec has been validated. +// TODO: remove extra validations when comfort level is high enough. +func IsComponentEnabledInSpec(componentName name.ComponentName, controlPlaneSpec *v1alpha1.IstioOperatorSpec) (bool, error) { + componentNodeI, found, err := tpath.GetFromStructPath(controlPlaneSpec, "Components."+string(componentName)+".Enabled") + if err != nil { + return false, fmt.Errorf("error in IsComponentEnabledInSpec GetFromStructPath componentEnabled for component=%s: %s", + componentName, err) + } + if !found || componentNodeI == nil { + return false, nil + } + componentNode, ok := componentNodeI.(*wrapperspb.BoolValue) + if !ok { + return false, fmt.Errorf("component %s enabled has bad type %T, expect *v1alpha1.BoolValueForPB", componentName, componentNodeI) + } + if componentNode == nil { + return false, nil + } + return componentNode.Value, nil +} + +// IsComponentEnabledFromValue get whether component is enabled in helm value.yaml tree. +// valuePath points to component path in the values tree. +func IsComponentEnabledFromValue(cn name.ComponentName, valueSpec map[string]any) (enabled bool, pathExist bool, err error) { + t := NewTranslator() + cnMap, ok := t.ComponentMaps[cn] + if !ok { + return false, false, nil + } + valuePath := cnMap.ToHelmValuesTreeRoot + enabledPath := valuePath + ".enabled" + enableNodeI, found, err := tpath.Find(valueSpec, util.ToYAMLPath(enabledPath)) + if err != nil { + return false, false, fmt.Errorf("error finding component enablement path: %s in helm value.yaml tree", enabledPath) + } + if !found { + // Some components do not specify enablement should be treated as enabled if the root node in the component subtree exists. + _, found, err := tpath.Find(valueSpec, util.ToYAMLPath(valuePath)) + if err != nil { + return false, false, err + } + if found { + return true, false, nil + } + return false, false, nil + } + enableNode, ok := enableNodeI.(bool) + if !ok { + return false, true, fmt.Errorf("node at valuePath %s has bad type %T, expect bool", enabledPath, enableNodeI) + } + return enableNode, true, nil +} + +// OverlayValuesEnablement overlays any enablement in values path from the user file overlay or set flag overlay. +// The overlay is translated from values to the corresponding addonComponents enablement paths. +func OverlayValuesEnablement(baseYAML, fileOverlayYAML, setOverlayYAML string) (string, error) { + overlayYAML, err := util.OverlayYAML(fileOverlayYAML, setOverlayYAML) + if err != nil { + return "", fmt.Errorf("could not overlay user config over base: %s", err) + } + + return YAMLTree(overlayYAML, baseYAML, name.ValuesEnablementPathMap) +} + +// GetEnabledComponents get all the enabled components from the given istio operator spec +func GetEnabledComponents(iopSpec *v1alpha1.IstioOperatorSpec) ([]string, error) { + var enabledComponents []string + if iopSpec.Components != nil { + for _, c := range name.AllCoreComponentNames { + enabled, err := IsComponentEnabledInSpec(c, iopSpec) + if err != nil { + return nil, fmt.Errorf("failed to check if component: %s is enabled or not: %v", string(c), err) + } + if enabled { + enabledComponents = append(enabledComponents, string(c)) + } + } + for _, c := range iopSpec.Components.IngressGateways { + if c != nil && c.Enabled.GetValue() { + enabledComponents = append(enabledComponents, string(name.IngressComponentName)) + break + } + } + for _, c := range iopSpec.Components.EgressGateways { + if c != nil && c.Enabled.GetValue() { + enabledComponents = append(enabledComponents, string(name.EgressComponentName)) + break + } + } + } + + return enabledComponents, nil +} diff --git a/operator/pkg/translate/translate_common_test.go b/operator/pkg/translate/translate_common_test.go new file mode 100644 index 0000000..738720b --- /dev/null +++ b/operator/pkg/translate/translate_common_test.go @@ -0,0 +1,114 @@ +// Copyright Istio 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 translate + +import ( + "testing" + + "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/util" +) + +func TestGetEnabledComponents(t *testing.T) { + tests := []struct { + desc string + yamlStr string + want []string + wantErrs bool + }{ + { + desc: "nil success", + yamlStr: "", + want: nil, + }, + { + desc: "all components disabled", + yamlStr: ` +components: + pilot: + enabled: false + ingressGateways: + - enabled: false`, + want: nil, + }, + { + desc: "only pilot component enabled", + yamlStr: ` +components: + pilot: + enabled: true + ingressGateways: + - enabled: false`, + want: []string{string(name.PilotComponentName)}, + }, + { + desc: "only gateway component enabled", + yamlStr: ` +components: + pilot: + enabled: false + ingressGateways: + - enabled: true`, + want: []string{string(name.IngressComponentName)}, + }, + { + desc: "all components enabled", + yamlStr: ` +components: + pilot: + enabled: true + ingressGateways: + - enabled: true`, + want: []string{string(name.PilotComponentName), string(name.IngressComponentName)}, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + iopSpec := &v1alpha1.IstioOperatorSpec{} + err := util.UnmarshalWithJSONPB(tt.yamlStr, iopSpec, false) + if err != nil { + t.Fatalf("unmarshalWithJSONPB(%s): got error %s", tt.desc, err) + } + + enabledComponents, err := GetEnabledComponents(iopSpec) + if err != nil { + t.Errorf("GetEnabledComponents(%s)(%v): got error: %v", tt.desc, tt.yamlStr, err) + } + if !testEquality(enabledComponents, tt.want) { + t.Errorf("GetEnabledComponents(%s)(%v): got: %v, want: %v", tt.desc, tt.yamlStr, enabledComponents, tt.want) + } + }) + } +} + +func testEquality(a, b []string) bool { + if (a == nil) != (b == nil) { + return false + } + + if len(a) != len(b) { + return false + } + + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} diff --git a/operator/pkg/translate/translate_test.go b/operator/pkg/translate/translate_test.go new file mode 100644 index 0000000..a5c999c --- /dev/null +++ b/operator/pkg/translate/translate_test.go @@ -0,0 +1,196 @@ +// Copyright Istio 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 translate + +import ( + "fmt" + "testing" + + "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/object" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" + "istio.io/istio/pkg/test/util/assert" +) + +func Test_skipReplicaCountWithAutoscaleEnabled(t *testing.T) { + const valuesWithHPAndReplicaCountFormat = ` +values: + pilot: + autoscaleEnabled: %t + gateways: + istio-ingressgateway: + autoscaleEnabled: %t + istio-egressgateway: + autoscaleEnabled: %t +components: + pilot: + k8s: + replicaCount: 2 + ingressGateways: + - name: istio-ingressgateway + enabled: true + k8s: + replicaCount: 2 + egressGateways: + - name: istio-egressgateway + enabled: true + k8s: + replicaCount: 2 +` + + cases := []struct { + name string + component name.ComponentName + values string + expectSkip bool + }{ + { + name: "hpa enabled for pilot without replicas", + component: name.PilotComponentName, + values: fmt.Sprintf(valuesWithHPAndReplicaCountFormat, false, false, false), + expectSkip: false, + }, + { + name: "hpa enabled for ingressgateway without replica", + component: name.IngressComponentName, + values: fmt.Sprintf(valuesWithHPAndReplicaCountFormat, false, false, false), + expectSkip: false, + }, + { + name: "hpa enabled for pilot without replicas", + component: name.EgressComponentName, + values: fmt.Sprintf(valuesWithHPAndReplicaCountFormat, false, false, false), + expectSkip: false, + }, + { + name: "hpa enabled for pilot with replicas", + component: name.PilotComponentName, + values: fmt.Sprintf(valuesWithHPAndReplicaCountFormat, true, false, false), + expectSkip: true, + }, + { + name: "hpa enabled for ingressgateway with replicas", + component: name.IngressComponentName, + values: fmt.Sprintf(valuesWithHPAndReplicaCountFormat, false, true, false), + expectSkip: true, + }, + { + name: "hpa enabled for egressgateway with replicas", + component: name.EgressComponentName, + values: fmt.Sprintf(valuesWithHPAndReplicaCountFormat, true, false, true), + expectSkip: true, + }, + } + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + var iop *v1alpha1.IstioOperatorSpec + if tt.values != "" { + iop = &v1alpha1.IstioOperatorSpec{} + if err := util.UnmarshalWithJSONPB(tt.values, iop, true); err != nil { + t.Fatal(err) + } + } + + got := skipReplicaCountWithAutoscaleEnabled(iop, tt.component) + assert.Equal(t, tt.expectSkip, got) + }) + } +} + +func Test_fixMergedObjectWithCustomServicePortOverlay_withAppProtocol(t *testing.T) { + const serviceString = ` +apiVersion: v1 +kind: Service +metadata: + name: istio-ingressgateway + namespace: istio-system +spec: + type: LoadBalancer +` + const iopString = ` +components: + ingressGateways: + - name: istio-ingressgateway + enabled: true + k8s: + service: + ports: + - name: http2 + port: 80 + targetPort: 8080 +` + const iopString2 = ` +components: + ingressGateways: + - name: istio-ingressgateway + enabled: true + k8s: + service: + ports: + - name: http2 + port: 80 + targetPort: 8080 + appProtocol: HTTP +` + cases := []struct { + name string + iopString string + expectValue string + expectExist bool + }{ + { + name: "without appProtocol", + iopString: iopString, + expectExist: false, + }, + { + name: "with appProtocol", + iopString: iopString2, + expectExist: true, + expectValue: "HTTP", + }, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + var iop *v1alpha1.IstioOperatorSpec + if tt.iopString != "" { + iop = &v1alpha1.IstioOperatorSpec{} + if err := util.UnmarshalWithJSONPB(tt.iopString, iop, true); err != nil { + t.Fatal(err) + } + } + translator := NewTranslator() + serviceObj, err := object.ParseYAMLToK8sObject([]byte(serviceString)) + assert.NoError(t, err) + obj, err := translator.fixMergedObjectWithCustomServicePortOverlay(serviceObj, + iop.Components.IngressGateways[0].GetK8S().GetService(), serviceObj) + assert.NoError(t, err) + val := obj.UnstructuredObject().Object["spec"].(map[string]interface{})["ports"].([]interface{})[0] + apVal, found, _ := tpath.GetFromStructPath(val, "appProtocol") + if !tt.expectExist { + assert.Equal(t, found, false) + } else { + if apVal != nil { + assert.Equal(t, apVal.(string), tt.expectValue) + } else { + assert.Equal(t, "", tt.expectValue) + } + } + }) + } +} diff --git a/operator/pkg/translate/translate_value.go b/operator/pkg/translate/translate_value.go new file mode 100644 index 0000000..355485a --- /dev/null +++ b/operator/pkg/translate/translate_value.go @@ -0,0 +1,609 @@ +// Copyright Istio 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 translate + +import ( + "fmt" + "sort" + "strings" + + "sigs.k8s.io/yaml" + + "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/metrics" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/version" + oversion "github.com/jehawley/istio/operator/version" +) + +// ReverseTranslator is a set of mappings to translate between values.yaml and API paths, charts, k8s paths. +type ReverseTranslator struct { + Version version.MinorVersion + // APIMapping is Values.yaml path to API path mapping using longest prefix match. If the path is a non-leaf node, + // the output path is the matching portion of the path, plus any remaining output path. + APIMapping map[string]*Translation `yaml:"apiMapping,omitempty"` + // KubernetesPatternMapping defines mapping patterns from k8s resource paths to IstioOperator API paths. + KubernetesPatternMapping map[string]string `yaml:"kubernetesPatternMapping,omitempty"` + // KubernetesMapping defines actual k8s mappings generated from KubernetesPatternMapping before each translation. + KubernetesMapping map[string]*Translation `yaml:"kubernetesMapping,omitempty"` + // GatewayKubernetesMapping defines actual k8s mappings for gateway components generated from KubernetesPatternMapping before each translation. + GatewayKubernetesMapping gatewayKubernetesMapping `yaml:"GatewayKubernetesMapping,omitempty"` + // ValuesToComponentName defines mapping from value path to component name in API paths. + ValuesToComponentName map[string]name.ComponentName `yaml:"valuesToComponentName,omitempty"` +} + +type gatewayKubernetesMapping struct { + IngressMapping map[string]*Translation + EgressMapping map[string]*Translation +} + +var ( + // Component enablement mapping. Ex "{{.ValueComponent}}.enabled": Components.{{.ComponentName}}.enabled}", nil}, + componentEnablementPattern = "Components.{{.ComponentName}}.Enabled" + // specialComponentPath lists cases of component path of values.yaml we need to have special treatment. + specialComponentPath = map[string]bool{ + "gateways": true, + "gateways.istio-ingressgateway": true, + "gateways.istio-egressgateway": true, + } + + skipTranslate = map[name.ComponentName]bool{ + name.IstioBaseComponentName: true, + name.IstioOperatorComponentName: true, + name.IstioOperatorCustomResourceName: true, + name.CNIComponentName: true, + name.IstiodRemoteComponentName: true, + name.ZtunnelComponentName: true, + } + + gatewayPathMapping = map[string]name.ComponentName{ + "gateways.istio-ingressgateway": name.IngressComponentName, + "gateways.istio-egressgateway": name.EgressComponentName, + } +) + +// initAPIMapping generate the reverse mapping from original translator apiMapping. +func (t *ReverseTranslator) initAPIAndComponentMapping() { + ts := NewTranslator() + t.APIMapping = make(map[string]*Translation) + t.KubernetesMapping = make(map[string]*Translation) + t.ValuesToComponentName = make(map[string]name.ComponentName) + for valKey, outVal := range ts.APIMapping { + t.APIMapping[outVal.OutPath] = &Translation{valKey, nil} + } + for cn, cm := range ts.ComponentMaps { + // we use dedicated translateGateway for gateway instead + if !skipTranslate[cn] && !cm.SkipReverseTranslate && !cn.IsGateway() { + t.ValuesToComponentName[cm.ToHelmValuesTreeRoot] = cn + } + } +} + +// initK8SMapping generates the k8s settings mapping for components that are enabled based on templates. +func (t *ReverseTranslator) initK8SMapping() error { + outputMapping := make(map[string]*Translation) + for valKey, componentName := range t.ValuesToComponentName { + for K8SValKey, outPathTmpl := range t.KubernetesPatternMapping { + newKey, err := renderComponentName(K8SValKey, valKey) + if err != nil { + return err + } + newVal, err := renderFeatureComponentPathTemplate(outPathTmpl, componentName) + if err != nil { + return err + } + outputMapping[newKey] = &Translation{newVal, nil} + } + } + + t.KubernetesMapping = outputMapping + + igwOutputMapping := make(map[string]*Translation) + egwOutputMapping := make(map[string]*Translation) + for valKey, componentName := range gatewayPathMapping { + mapping := igwOutputMapping + if componentName == name.EgressComponentName { + mapping = egwOutputMapping + } + for K8SValKey, outPathTmpl := range t.KubernetesPatternMapping { + newKey, err := renderComponentName(K8SValKey, valKey) + if err != nil { + return err + } + newP := util.PathFromString(outPathTmpl) + mapping[newKey] = &Translation{newP[len(newP)-2:].String(), nil} + } + } + t.GatewayKubernetesMapping = gatewayKubernetesMapping{IngressMapping: igwOutputMapping, EgressMapping: egwOutputMapping} + return nil +} + +// NewReverseTranslator creates a new ReverseTranslator for minorVersion and returns a ptr to it. +func NewReverseTranslator() *ReverseTranslator { + rt := &ReverseTranslator{ + KubernetesPatternMapping: map[string]string{ + "{{.ValueComponentName}}.env": "Components.{{.ComponentName}}.K8s.Env", + "{{.ValueComponentName}}.autoscaleEnabled": "Components.{{.ComponentName}}.K8s.HpaSpec", + "{{.ValueComponentName}}.imagePullPolicy": "Components.{{.ComponentName}}.K8s.ImagePullPolicy", + "{{.ValueComponentName}}.nodeSelector": "Components.{{.ComponentName}}.K8s.NodeSelector", + "{{.ValueComponentName}}.tolerations": "Components.{{.ComponentName}}.K8s.Tolerations", + "{{.ValueComponentName}}.podDisruptionBudget": "Components.{{.ComponentName}}.K8s.PodDisruptionBudget", + "{{.ValueComponentName}}.podAnnotations": "Components.{{.ComponentName}}.K8s.PodAnnotations", + "{{.ValueComponentName}}.priorityClassName": "Components.{{.ComponentName}}.K8s.PriorityClassName", + "{{.ValueComponentName}}.readinessProbe": "Components.{{.ComponentName}}.K8s.ReadinessProbe", + "{{.ValueComponentName}}.replicaCount": "Components.{{.ComponentName}}.K8s.ReplicaCount", + "{{.ValueComponentName}}.resources": "Components.{{.ComponentName}}.K8s.Resources", + "{{.ValueComponentName}}.rollingMaxSurge": "Components.{{.ComponentName}}.K8s.Strategy", + "{{.ValueComponentName}}.rollingMaxUnavailable": "Components.{{.ComponentName}}.K8s.Strategy", + "{{.ValueComponentName}}.serviceAnnotations": "Components.{{.ComponentName}}.K8s.ServiceAnnotations", + }, + } + rt.initAPIAndComponentMapping() + rt.Version = oversion.OperatorBinaryVersion.MinorVersion + return rt +} + +// TranslateFromValueToSpec translates from values.yaml value to IstioOperatorSpec. +func (t *ReverseTranslator) TranslateFromValueToSpec(values []byte, force bool) (controlPlaneSpec *v1alpha1.IstioOperatorSpec, err error) { + yamlTree := make(map[string]any) + err = yaml.Unmarshal(values, &yamlTree) + if err != nil { + return nil, fmt.Errorf("error when unmarshalling into untype tree %v", err) + } + + outputTree := make(map[string]any) + err = t.TranslateTree(yamlTree, outputTree, nil) + if err != nil { + return nil, err + } + outputVal, err := yaml.Marshal(outputTree) + if err != nil { + return nil, err + } + + cpSpec := &v1alpha1.IstioOperatorSpec{} + err = util.UnmarshalWithJSONPB(string(outputVal), cpSpec, force) + if err != nil { + return nil, fmt.Errorf("error when unmarshalling into control plane spec %v, \nyaml:\n %s", err, outputVal) + } + + return cpSpec, nil +} + +// TranslateTree translates input value.yaml Tree to ControlPlaneSpec Tree. +func (t *ReverseTranslator) TranslateTree(valueTree map[string]any, cpSpecTree map[string]any, path util.Path) error { + // translate enablement and namespace + err := t.setEnablementFromValue(valueTree, cpSpecTree) + if err != nil { + return fmt.Errorf("error when translating enablement and namespace from value.yaml tree: %v", err) + } + // translate with api mapping + err = t.translateAPI(valueTree, cpSpecTree) + if err != nil { + return fmt.Errorf("error when translating value.yaml tree with global mapping: %v", err) + } + + // translate with k8s mapping + if err := t.TranslateK8S(valueTree, cpSpecTree); err != nil { + return err + } + + if err := t.translateGateway(valueTree, cpSpecTree); err != nil { + return fmt.Errorf("error when translating gateway with kubernetes mapping: %v", err.Error()) + } + // translate remaining untranslated paths into component values + err = t.translateRemainingPaths(valueTree, cpSpecTree, nil) + if err != nil { + return fmt.Errorf("error when translating remaining path: %v", err) + } + return nil +} + +// TranslateK8S is a helper function to translate k8s settings from values.yaml to IstioOperator, except for gateways. +func (t *ReverseTranslator) TranslateK8S(valueTree map[string]any, cpSpecTree map[string]any) error { + // translate with k8s mapping + if err := t.initK8SMapping(); err != nil { + return fmt.Errorf("error when initiating k8s mapping: %v", err) + } + if err := t.translateK8sTree(valueTree, cpSpecTree, t.KubernetesMapping); err != nil { + return fmt.Errorf("error when translating value.yaml tree with kubernetes mapping: %v", err) + } + return nil +} + +// setEnablementFromValue translates the enablement value of components in the values.yaml +// tree, based on feature/component inheritance relationship. +func (t *ReverseTranslator) setEnablementFromValue(valueSpec map[string]any, root map[string]any) error { + for _, cni := range t.ValuesToComponentName { + enabled, pathExist, err := IsComponentEnabledFromValue(cni, valueSpec) + if err != nil { + return err + } + if !pathExist { + continue + } + tmpl := componentEnablementPattern + ceVal, err := renderFeatureComponentPathTemplate(tmpl, cni) + if err != nil { + return err + } + outCP := util.ToYAMLPath(ceVal) + // set component enablement + if err := tpath.WriteNode(root, outCP, enabled); err != nil { + return err + } + } + + return nil +} + +// WarningForGatewayK8SSettings creates deprecated warning messages +// when user try to set kubernetes settings for gateways via values api. +func (t *ReverseTranslator) WarningForGatewayK8SSettings(valuesOverlay string) (string, error) { + gwOverlay, err := tpath.GetConfigSubtree(valuesOverlay, "gateways") + if err != nil { + return "", fmt.Errorf("error getting gateways overlay from valuesOverlayYaml %v", err) + } + if gwOverlay == "" { + return "", nil + } + var deprecatedFields []string + for inPath := range t.GatewayKubernetesMapping.IngressMapping { + _, found, err := tpath.GetPathContext(valuesOverlay, util.ToYAMLPath(inPath), false) + if err != nil { + scope.Debug(err.Error()) + continue + } + if found { + deprecatedFields = append(deprecatedFields, inPath) + } + } + for inPath := range t.GatewayKubernetesMapping.EgressMapping { + _, found, err := tpath.GetPathContext(valuesOverlay, util.ToYAMLPath(inPath), false) + if err != nil { + scope.Debug(err.Error()) + continue + } + if found { + deprecatedFields = append(deprecatedFields, inPath) + } + } + if len(deprecatedFields) == 0 { + return "", nil + } + warningMessage := fmt.Sprintf("using deprecated values api paths: %s.\n"+ + " please use k8s spec of gateway components instead\n", strings.Join(deprecatedFields, ",")) + return warningMessage, nil +} + +// translateGateway handles translation for gateways specific configuration +func (t *ReverseTranslator) translateGateway(valueSpec map[string]any, root map[string]any) error { + for inPath, outPath := range gatewayPathMapping { + enabled, pathExist, err := IsComponentEnabledFromValue(outPath, valueSpec) + if err != nil { + return err + } + if !pathExist && !enabled { + continue + } + gwSpecs := make([]map[string]any, 1) + gwSpec := make(map[string]any) + gwSpecs[0] = gwSpec + gwSpec["enabled"] = enabled + gwSpec["name"] = util.ToYAMLPath(inPath)[1] + outCP := util.ToYAMLPath("Components." + string(outPath)) + + if enabled { + mapping := t.GatewayKubernetesMapping.IngressMapping + if outPath == name.EgressComponentName { + mapping = t.GatewayKubernetesMapping.EgressMapping + } + err = t.translateK8sTree(valueSpec, gwSpec, mapping) + if err != nil { + return err + } + } + err = tpath.WriteNode(root, outCP, gwSpecs) + if err != nil { + return err + } + } + return nil +} + +// TranslateK8SfromValueToIOP use reverse translation to convert k8s settings defined in values API to IOP API. +// this ensures that user overlays that set k8s through spec.values +// are not overridden by spec.components.X.k8s settings in the base profiles +func (t *ReverseTranslator) TranslateK8SfromValueToIOP(userOverlayYaml string) (string, error) { + valuesOverlay, err := tpath.GetConfigSubtree(userOverlayYaml, "spec.values") + if err != nil { + scope.Debugf("no spec.values section from userOverlayYaml %v", err) + return "", nil + } + valuesOverlayTree := make(map[string]any) + err = yaml.Unmarshal([]byte(valuesOverlay), &valuesOverlayTree) + if err != nil { + return "", fmt.Errorf("error unmarshalling values overlay yaml into untype tree %v", err) + } + iopSpecTree := make(map[string]any) + iopSpecOverlay, err := tpath.GetConfigSubtree(userOverlayYaml, "spec") + if err != nil { + return "", fmt.Errorf("error getting iop spec subtree from overlay yaml %v", err) + } + err = yaml.Unmarshal([]byte(iopSpecOverlay), &iopSpecTree) + if err != nil { + return "", fmt.Errorf("error unmarshalling spec overlay yaml into tree %v", err) + } + if err = t.TranslateK8S(valuesOverlayTree, iopSpecTree); err != nil { + return "", err + } + warning, err := t.WarningForGatewayK8SSettings(valuesOverlay) + if err != nil { + return "", fmt.Errorf("error handling values gateway k8s settings: %v", err) + } + if warning != "" { + return "", fmt.Errorf(warning) + } + iopSpecTreeYAML, err := yaml.Marshal(iopSpecTree) + if err != nil { + return "", fmt.Errorf("error marshaling reverse translated tree %v", err) + } + iopTreeYAML, err := tpath.AddSpecRoot(string(iopSpecTreeYAML)) + if err != nil { + return "", fmt.Errorf("error adding spec root: %v", err) + } + // overlay the reverse translated iopTreeYAML back to userOverlayYaml + finalYAML, err := util.OverlayYAML(userOverlayYaml, iopTreeYAML) + if err != nil { + return "", fmt.Errorf("failed to overlay the reverse translated iopTreeYAML: %v", err) + } + return finalYAML, err +} + +// translateStrategy translates Deployment Strategy related configurations from helm values.yaml tree. +func translateStrategy(fieldName string, outPath string, value any, cpSpecTree map[string]any) error { + fieldMap := map[string]string{ + "rollingMaxSurge": "maxSurge", + "rollingMaxUnavailable": "maxUnavailable", + } + newFieldName, ok := fieldMap[fieldName] + if !ok { + return fmt.Errorf("expected field name found in values.yaml: %s", fieldName) + } + outPath += ".rollingUpdate." + newFieldName + + scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", outPath) + if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath(outPath), value); err != nil { + return err + } + return nil +} + +// translateEnv translates env value from helm values.yaml tree. +func translateEnv(outPath string, value any, cpSpecTree map[string]any) error { + envMap, ok := value.(map[string]any) + if !ok { + return fmt.Errorf("expect env node type to be map[string]interface{} but got: %T", value) + } + if len(envMap) == 0 { + return nil + } + scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", outPath) + nc, found, _ := tpath.GetPathContext(cpSpecTree, util.ToYAMLPath(outPath), false) + var envValStr []byte + if nc != nil { + envValStr, _ = yaml.Marshal(nc.Node) + } + if !found || strings.TrimSpace(string(envValStr)) == "{}" { + scope.Debugf("path doesn't have value in k8s setting with output path %s, override with helm Value.yaml tree", outPath) + outEnv := make([]map[string]any, len(envMap)) + keys := make([]string, 0, len(envMap)) + for k := range envMap { + keys = append(keys, k) + } + sort.Strings(keys) + for i, k := range keys { + outEnv[i] = make(map[string]any) + outEnv[i]["name"] = k + outEnv[i]["value"] = fmt.Sprintf("%v", envMap[k]) + } + if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath(outPath), outEnv); err != nil { + return err + } + } else { + scope.Debugf("path has value in k8s setting with output path %s, merge it with helm Value.yaml tree", outPath) + keys := make([]string, 0, len(envMap)) + for k := range envMap { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + outEnv := make(map[string]any) + outEnv["name"] = k + outEnv["value"] = fmt.Sprintf("%v", envMap[k]) + if err := tpath.MergeNode(cpSpecTree, util.ToYAMLPath(outPath), outEnv); err != nil { + return err + } + } + } + return nil +} + +// translateK8sTree is internal method for translating K8s configurations from value.yaml tree. +func (t *ReverseTranslator) translateK8sTree(valueTree map[string]any, + cpSpecTree map[string]any, mapping map[string]*Translation, +) error { + for inPath, v := range mapping { + scope.Debugf("Checking for k8s path %s in helm Value.yaml tree", inPath) + path := util.PathFromString(inPath) + k8sSettingName := "" + if len(path) != 0 { + k8sSettingName = path[len(path)-1] + } + if k8sSettingName == "autoscaleEnabled" { + continue + } + m, found, err := tpath.Find(valueTree, util.ToYAMLPath(inPath)) + if err != nil { + return err + } + if !found { + scope.Debugf("path %s not found in helm Value.yaml tree, skip mapping.", inPath) + continue + } + + if mstr, ok := m.(string); ok && mstr == "" { + scope.Debugf("path %s is empty string, skip mapping.", inPath) + continue + } + // Zero int values are due to proto3 compiling to scalars rather than ptrs. Skip these because values of 0 are + // the default in destination fields and need not be set explicitly. + if mint, ok := util.ToIntValue(m); ok && mint == 0 { + scope.Debugf("path %s is int 0, skip mapping.", inPath) + continue + } + + switch k8sSettingName { + case "env": + err := translateEnv(v.OutPath, m, cpSpecTree) + if err != nil { + return fmt.Errorf("error in translating k8s Env: %s", err) + } + + case "rollingMaxSurge", "rollingMaxUnavailable": + err := translateStrategy(k8sSettingName, v.OutPath, m, cpSpecTree) + if err != nil { + return fmt.Errorf("error in translating k8s Strategy: %s", err) + } + + default: + if util.IsValueNilOrDefault(m) { + continue + } + output := util.ToYAMLPath(v.OutPath) + scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", output) + + if err := tpath.WriteNode(cpSpecTree, output, m); err != nil { + return err + } + } + metrics.LegacyPathTranslationTotal.Increment() + + if _, err := tpath.Delete(valueTree, util.ToYAMLPath(inPath)); err != nil { + return err + } + } + return nil +} + +// translateRemainingPaths translates remaining paths that are not available in existing mappings. +func (t *ReverseTranslator) translateRemainingPaths(valueTree map[string]any, + cpSpecTree map[string]any, path util.Path, +) error { + for key, val := range valueTree { + newPath := append(path, key) + // value set to nil means no translation needed or being translated already. + if val == nil { + continue + } + switch node := val.(type) { + case map[string]any: + err := t.translateRemainingPaths(node, cpSpecTree, newPath) + if err != nil { + return err + } + case []any: + if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath("Values."+newPath.String()), node); err != nil { + return err + } + // remaining leaf need to be put into root.values + default: + if t.isEnablementPath(newPath) { + continue + } + if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath("Values."+newPath.String()), val); err != nil { + return err + } + } + } + return nil +} + +// translateAPI is internal method for translating value.yaml tree based on API mapping. +func (t *ReverseTranslator) translateAPI(valueTree map[string]any, + cpSpecTree map[string]any, +) error { + for inPath, v := range t.APIMapping { + scope.Debugf("Checking for path %s in helm Value.yaml tree", inPath) + m, found, err := tpath.Find(valueTree, util.ToYAMLPath(inPath)) + if err != nil { + return err + } + if !found { + scope.Debugf("path %s not found in helm Value.yaml tree, skip mapping.", inPath) + continue + } + if mstr, ok := m.(string); ok && mstr == "" { + scope.Debugf("path %s is empty string, skip mapping.", inPath) + continue + } + // Zero int values are due to proto3 compiling to scalars rather than ptrs. Skip these because values of 0 are + // the default in destination fields and need not be set explicitly. + if mint, ok := util.ToIntValue(m); ok && mint == 0 { + scope.Debugf("path %s is int 0, skip mapping.", inPath) + continue + } + + path := util.ToYAMLPath(v.OutPath) + scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", path) + metrics.LegacyPathTranslationTotal. + With(metrics.ResourceKindLabel.Value(inPath)).Increment() + + if err := tpath.WriteNode(cpSpecTree, path, m); err != nil { + return err + } + + if _, err := tpath.Delete(valueTree, util.ToYAMLPath(inPath)); err != nil { + return err + } + } + return nil +} + +// isEnablementPath is helper function to check whether paths represent enablement of components in values.yaml +func (t *ReverseTranslator) isEnablementPath(path util.Path) bool { + if len(path) < 2 || path[len(path)-1] != "enabled" { + return false + } + + pf := path[:len(path)-1].String() + if specialComponentPath[pf] { + return true + } + + _, exist := t.ValuesToComponentName[pf] + return exist +} + +// renderComponentName renders a template of the form {{.ComponentName}} with +// the supplied parameters. +func renderComponentName(tmpl string, componentName string) (string, error) { + type temp struct { + ValueComponentName string + } + return util.RenderTemplate(tmpl, temp{componentName}) +} diff --git a/operator/pkg/translate/translate_value_test.go b/operator/pkg/translate/translate_value_test.go new file mode 100644 index 0000000..fea97c9 --- /dev/null +++ b/operator/pkg/translate/translate_value_test.go @@ -0,0 +1,907 @@ +// Copyright Istio 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 translate + +import ( + "testing" + + "sigs.k8s.io/yaml" + + "github.com/jehawley/istio/operator/pkg/apis/istio" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/util" + "istio.io/istio/pkg/util/protomarshal" +) + +func TestValueToProto(t *testing.T) { + tests := []struct { + desc string + valueYAML string + want string + wantErr string + }{ + { + desc: "K8s resources translation", + valueYAML: ` +pilot: + enabled: true + rollingMaxSurge: 100% + rollingMaxUnavailable: 25% + resources: + requests: + cpu: 1000m + memory: 1G + replicaCount: 1 + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: dedicated + operator: Exists + effect: NoSchedule + - key: CriticalAddonsOnly + operator: Exists + autoscaleEnabled: true + autoscaleMax: 3 + autoscaleMin: 1 + cpu: + targetAverageUtilization: 80 + traceSampling: 1.0 + image: pilot + env: + GODEBUG: gctrace=1 +global: + hub: docker.io/istio + istioNamespace: istio-system + tag: 1.2.3 + proxy: + readinessInitialDelaySeconds: 2 +`, + want: ` +hub: docker.io/istio +tag: 1.2.3 +components: + pilot: + enabled: true + k8s: + replicaCount: 1 + env: + - name: GODEBUG + value: gctrace=1 + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: dedicated + operator: Exists + effect: NoSchedule + - key: CriticalAddonsOnly + operator: Exists + resources: + requests: + cpu: 1000m + memory: 1G + strategy: + rollingUpdate: + maxSurge: 100% + maxUnavailable: 25% +values: + global: + istioNamespace: istio-system + proxy: + readinessInitialDelaySeconds: 2 + pilot: + image: pilot + autoscaleEnabled: true + autoscaleMax: 3 + autoscaleMin: 1 + cpu: + targetAverageUtilization: 80 + traceSampling: 1 +`, + }, + { + desc: "All Enabled", + valueYAML: ` +global: + hub: docker.io/istio + istioNamespace: istio-system + tag: 1.2.3 +pilot: + enabled: true +gateways: + enabled: true + istio-ingressgateway: + rollingMaxSurge: 4 + rollingMaxUnavailable: 1 + resources: + requests: + cpu: 1000m + memory: 1G + enabled: true +`, + want: ` +hub: docker.io/istio +tag: 1.2.3 +components: + pilot: + enabled: true + ingressGateways: + - name: istio-ingressgateway + enabled: true + k8s: + resources: + requests: + cpu: 1000m + memory: 1G + strategy: + rollingUpdate: + maxSurge: 4 + maxUnavailable: 1 +values: + global: + istioNamespace: istio-system +`, + }, + { + desc: "Some components Disabled", + valueYAML: ` +pilot: + enabled: true +global: + hub: docker.io/istio + istioNamespace: istio-system + tag: 1.2.3 +`, + want: ` +hub: docker.io/istio +tag: 1.2.3 +components: + pilot: + enabled: true +values: + global: + istioNamespace: istio-system +`, + }, + } + tr := NewReverseTranslator() + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + valueStruct := v1alpha1.Values{} + err := util.UnmarshalWithJSONPB(tt.valueYAML, &valueStruct, false) + if err != nil { + t.Fatalf("unmarshal(%s): got error %s", tt.desc, err) + } + gotSpec, err := tr.TranslateFromValueToSpec([]byte(tt.valueYAML), false) + if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { + t.Errorf("ValuesToProto(%s)(%v): gotErr:%s, wantErr:%s", tt.desc, tt.valueYAML, gotErr, wantErr) + } + if tt.wantErr == "" { + byteArray, err := protomarshal.Marshal(gotSpec) + if err != nil { + t.Errorf("failed to marshal translated IstioOperatorSpec: %s", err) + } + cpYaml, _ := yaml.JSONToYAML(byteArray) + if want := tt.want; !util.IsYAMLEqual(string(byteArray), want) { + t.Errorf("ValuesToProto(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, string(cpYaml), want, util.YAMLDiff(string(byteArray), want)) + } + + } + }) + } +} + +func TestValueToK8s(t *testing.T) { + tests := []struct { + desc string + inIOPSpec string + want string + wantErr string + }{ + { + desc: "pilot env k8s setting with values", + inIOPSpec: ` +spec: + components: + pilot: + k8s: + nodeSelector: + master: "true" + env: + - name: EXTERNAL_CA + value: ISTIOD_RA_KUBERNETES_API + - name: K8S_SIGNER + value: kubernetes.io/legacy-unknown + values: + pilot: + enabled: true + rollingMaxSurge: 100% + rollingMaxUnavailable: 25% + resources: + requests: + cpu: 1000m + memory: 1G + replicaCount: 1 + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: dedicated + operator: Exists + effect: NoSchedule + - key: CriticalAddonsOnly + operator: Exists + autoscaleEnabled: true + autoscaleMax: 3 + autoscaleMin: 1 + cpu: + targetAverageUtilization: 80 + memory: + targetAverageUtilization: 80 + traceSampling: 1.0 + image: pilot + env: + GODEBUG: gctrace=1 + global: + hub: docker.io/istio + tag: 1.2.3 + istioNamespace: istio-system + proxy: + readinessInitialDelaySeconds: 2 +`, + want: ` +spec: + components: + pilot: + k8s: + env: + - name: EXTERNAL_CA + value: ISTIOD_RA_KUBERNETES_API + - name: K8S_SIGNER + value: kubernetes.io/legacy-unknown + - name: GODEBUG + value: gctrace=1 + nodeSelector: + master: "true" + kubernetes.io/os: linux + replicaCount: 1 + resources: + requests: + cpu: 1000m + memory: 1G + strategy: + rollingUpdate: + maxSurge: 100% + maxUnavailable: 25% + tolerations: + - effect: NoSchedule + key: dedicated + operator: Exists + - key: CriticalAddonsOnly + operator: Exists + values: + global: + hub: docker.io/istio + tag: 1.2.3 + istioNamespace: istio-system + proxy: + readinessInitialDelaySeconds: 2 + pilot: + enabled: true + rollingMaxSurge: 100% + rollingMaxUnavailable: 25% + resources: + requests: + cpu: 1000m + memory: 1G + replicaCount: 1 + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: dedicated + operator: Exists + effect: NoSchedule + - key: CriticalAddonsOnly + operator: Exists + autoscaleEnabled: true + autoscaleMax: 3 + autoscaleMin: 1 + cpu: + targetAverageUtilization: 80 + memory: + targetAverageUtilization: 80 + traceSampling: 1.0 + image: pilot + env: + GODEBUG: gctrace=1 +`, + }, + { + desc: "pilot no env k8s setting with values", + inIOPSpec: ` +spec: + components: + pilot: + k8s: + nodeSelector: + master: "true" + values: + pilot: + enabled: true + rollingMaxSurge: 100% + rollingMaxUnavailable: 25% + resources: + requests: + cpu: 1000m + memory: 1G + replicaCount: 1 + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: dedicated + operator: Exists + effect: NoSchedule + - key: CriticalAddonsOnly + operator: Exists + autoscaleEnabled: true + autoscaleMax: 3 + autoscaleMin: 1 + cpu: + targetAverageUtilization: 80 + memory: + targetAverageUtilization: 80 + traceSampling: 1.0 + image: pilot + env: + GODEBUG: gctrace=1 + global: + hub: docker.io/istio + tag: 1.2.3 + istioNamespace: istio-system + proxy: + readinessInitialDelaySeconds: 2 +`, + want: ` +spec: + components: + pilot: + k8s: + env: + - name: GODEBUG + value: gctrace=1 + nodeSelector: + master: "true" + kubernetes.io/os: linux + replicaCount: 1 + resources: + requests: + cpu: 1000m + memory: 1G + strategy: + rollingUpdate: + maxSurge: 100% + maxUnavailable: 25% + tolerations: + - effect: NoSchedule + key: dedicated + operator: Exists + - key: CriticalAddonsOnly + operator: Exists + values: + global: + hub: docker.io/istio + tag: 1.2.3 + istioNamespace: istio-system + proxy: + readinessInitialDelaySeconds: 2 + pilot: + enabled: true + rollingMaxSurge: 100% + rollingMaxUnavailable: 25% + resources: + requests: + cpu: 1000m + memory: 1G + replicaCount: 1 + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: dedicated + operator: Exists + effect: NoSchedule + - key: CriticalAddonsOnly + operator: Exists + autoscaleEnabled: true + autoscaleMax: 3 + autoscaleMin: 1 + cpu: + targetAverageUtilization: 80 + memory: + targetAverageUtilization: 80 + traceSampling: 1.0 + image: pilot + env: + GODEBUG: gctrace=1 +`, + }, + { + desc: "pilot k8s setting with empty env in values", + inIOPSpec: ` +spec: + components: + pilot: + k8s: + nodeSelector: + master: "true" + env: + - name: SPIFFE_BUNDLE_ENDPOINTS + value: "SPIFFE_BUNDLE_ENDPOINT" + values: + pilot: + enabled: true + rollingMaxSurge: 100% + rollingMaxUnavailable: 25% + resources: + requests: + cpu: 1000m + memory: 1G + replicaCount: 1 + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: dedicated + operator: Exists + effect: NoSchedule + - key: CriticalAddonsOnly + operator: Exists + autoscaleEnabled: true + autoscaleMax: 3 + autoscaleMin: 1 + cpu: + targetAverageUtilization: 80 + memory: + targetAverageUtilization: 80 + traceSampling: 1.0 + image: pilot + env: {} + global: + hub: docker.io/istio + tag: 1.2.3 + istioNamespace: istio-system + proxy: + readinessInitialDelaySeconds: 2 +`, + want: ` +spec: + components: + pilot: + k8s: + env: + - name: SPIFFE_BUNDLE_ENDPOINTS + value: "SPIFFE_BUNDLE_ENDPOINT" + nodeSelector: + master: "true" + kubernetes.io/os: linux + replicaCount: 1 + resources: + requests: + cpu: 1000m + memory: 1G + strategy: + rollingUpdate: + maxSurge: 100% + maxUnavailable: 25% + tolerations: + - effect: NoSchedule + key: dedicated + operator: Exists + - key: CriticalAddonsOnly + operator: Exists + values: + global: + hub: docker.io/istio + tag: 1.2.3 + istioNamespace: istio-system + proxy: + readinessInitialDelaySeconds: 2 + pilot: + enabled: true + rollingMaxSurge: 100% + rollingMaxUnavailable: 25% + resources: + requests: + cpu: 1000m + memory: 1G + replicaCount: 1 + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: dedicated + operator: Exists + effect: NoSchedule + - key: CriticalAddonsOnly + operator: Exists + autoscaleEnabled: true + autoscaleMax: 3 + autoscaleMin: 1 + cpu: + targetAverageUtilization: 80 + memory: + targetAverageUtilization: 80 + traceSampling: 1.0 + image: pilot + env: {} +`, + }, + { + desc: "pilot no env k8s setting with multiple env variables in values", + inIOPSpec: ` +spec: + components: + pilot: + k8s: + nodeSelector: + master: "true" + values: + pilot: + enabled: true + rollingMaxSurge: 100% + rollingMaxUnavailable: 25% + resources: + requests: + cpu: 1000m + memory: 1G + replicaCount: 1 + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: dedicated + operator: Exists + effect: NoSchedule + - key: CriticalAddonsOnly + operator: Exists + autoscaleEnabled: true + autoscaleMax: 3 + autoscaleMin: 1 + cpu: + targetAverageUtilization: 80 + memory: + targetAverageUtilization: 80 + traceSampling: 1.0 + image: pilot + env: + PILOT_ENABLE_ISTIO_TAGS: false + PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME: false + PROXY_XDS_DEBUG_VIA_AGENT: false + global: + hub: docker.io/istio + tag: 1.2.3 + istioNamespace: istio-system + proxy: + readinessInitialDelaySeconds: 2 +`, + want: ` +spec: + components: + pilot: + k8s: + env: + - name: PILOT_ENABLE_ISTIO_TAGS + value: "false" + - name: PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME + value: "false" + - name: PROXY_XDS_DEBUG_VIA_AGENT + value: "false" + nodeSelector: + master: "true" + kubernetes.io/os: linux + replicaCount: 1 + resources: + requests: + cpu: 1000m + memory: 1G + strategy: + rollingUpdate: + maxSurge: 100% + maxUnavailable: 25% + tolerations: + - effect: NoSchedule + key: dedicated + operator: Exists + - key: CriticalAddonsOnly + operator: Exists + values: + global: + hub: docker.io/istio + tag: 1.2.3 + istioNamespace: istio-system + proxy: + readinessInitialDelaySeconds: 2 + pilot: + enabled: true + rollingMaxSurge: 100% + rollingMaxUnavailable: 25% + resources: + requests: + cpu: 1000m + memory: 1G + replicaCount: 1 + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: dedicated + operator: Exists + effect: NoSchedule + - key: CriticalAddonsOnly + operator: Exists + autoscaleEnabled: true + autoscaleMax: 3 + autoscaleMin: 1 + cpu: + targetAverageUtilization: 80 + memory: + targetAverageUtilization: 80 + traceSampling: 1.0 + image: pilot + env: + PILOT_ENABLE_ISTIO_TAGS: false + PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME: false + PROXY_XDS_DEBUG_VIA_AGENT: false +`, + }, + { + desc: "pilot k8s setting with multiple env variables in values", + inIOPSpec: ` +spec: + components: + pilot: + k8s: + nodeSelector: + master: "true" + env: + - name: SPIFFE_BUNDLE_ENDPOINTS + value: "SPIFFE_BUNDLE_ENDPOINT" + values: + pilot: + enabled: true + rollingMaxSurge: 100% + rollingMaxUnavailable: 25% + resources: + requests: + cpu: 1000m + memory: 1G + replicaCount: 1 + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: dedicated + operator: Exists + effect: NoSchedule + - key: CriticalAddonsOnly + operator: Exists + autoscaleEnabled: true + autoscaleMax: 3 + autoscaleMin: 1 + cpu: + targetAverageUtilization: 80 + memory: + targetAverageUtilization: 80 + traceSampling: 1.0 + image: pilot + env: + PILOT_ENABLE_ISTIO_TAGS: false + PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME: false + PROXY_XDS_DEBUG_VIA_AGENT: false + global: + hub: docker.io/istio + tag: 1.2.3 + istioNamespace: istio-system + proxy: + readinessInitialDelaySeconds: 2 +`, + want: ` +spec: + components: + pilot: + k8s: + env: + - name: SPIFFE_BUNDLE_ENDPOINTS + value: "SPIFFE_BUNDLE_ENDPOINT" + - name: PILOT_ENABLE_ISTIO_TAGS + value: "false" + - name: PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME + value: "false" + - name: PROXY_XDS_DEBUG_VIA_AGENT + value: "false" + nodeSelector: + master: "true" + kubernetes.io/os: linux + replicaCount: 1 + resources: + requests: + cpu: 1000m + memory: 1G + strategy: + rollingUpdate: + maxSurge: 100% + maxUnavailable: 25% + tolerations: + - effect: NoSchedule + key: dedicated + operator: Exists + - key: CriticalAddonsOnly + operator: Exists + values: + global: + hub: docker.io/istio + tag: 1.2.3 + istioNamespace: istio-system + proxy: + readinessInitialDelaySeconds: 2 + pilot: + enabled: true + rollingMaxSurge: 100% + rollingMaxUnavailable: 25% + resources: + requests: + cpu: 1000m + memory: 1G + replicaCount: 1 + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: dedicated + operator: Exists + effect: NoSchedule + - key: CriticalAddonsOnly + operator: Exists + autoscaleEnabled: true + autoscaleMax: 3 + autoscaleMin: 1 + cpu: + targetAverageUtilization: 80 + memory: + targetAverageUtilization: 80 + traceSampling: 1.0 + image: pilot + env: + PILOT_ENABLE_ISTIO_TAGS: false + PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME: false + PROXY_XDS_DEBUG_VIA_AGENT: false +`, + }, + { + desc: "ingressgateway k8s setting with values", + inIOPSpec: ` +spec: + components: + pilot: + enabled: false + ingressGateways: + - namespace: istio-system + name: istio-ingressgateway + enabled: true + k8s: + service: + externalTrafficPolicy: Local + serviceAnnotations: + manifest-generate: "testserviceAnnotation" + securityContext: + sysctls: + - name: "net.ipv4.ip_local_port_range" + value: "80 65535" + values: + gateways: + istio-ingressgateway: + serviceAnnotations: {} + nodeSelector: {} + tolerations: [] + global: + hub: docker.io/istio + tag: 1.2.3 + istioNamespace: istio-system +`, + want: ` +spec: + components: + ingressGateways: + - name: istio-ingressgateway + namespace: istio-system + enabled: true + k8s: + securityContext: + sysctls: + - name: net.ipv4.ip_local_port_range + value: "80 65535" + service: + externalTrafficPolicy: Local + serviceAnnotations: + manifest-generate: testserviceAnnotation + pilot: + enabled: false + values: + gateways: + istio-ingressgateway: + serviceAnnotations: {} + nodeSelector: {} + tolerations: [] + global: + hub: docker.io/istio + tag: 1.2.3 + istioNamespace: istio-system +`, + }, + { + desc: "pilot env k8s setting with non-empty hpa values", + inIOPSpec: ` +spec: + revision: canary + components: + pilot: + enabled: true + values: + pilot: + autoscaleMin: 1 + autoscaleMax: 3 + cpu: + targetAverageUtilization: 80 + memory: + targetAverageUtilization: 80 +`, + want: ` +spec: + revision: canary + components: + pilot: + enabled: true + values: + pilot: + autoscaleMin: 1 + autoscaleMax: 3 + cpu: + targetAverageUtilization: 80 + memory: + targetAverageUtilization: 80 +`, + }, + } + tr := NewReverseTranslator() + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + _, err := istio.UnmarshalIstioOperator(tt.inIOPSpec, false) + if err != nil { + t.Fatalf("unmarshal(%s): got error %s", tt.desc, err) + } + gotSpec, err := tr.TranslateK8SfromValueToIOP(tt.inIOPSpec) + if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { + t.Errorf("ValuesToK8s(%s)(%v): gotErr:%s, wantErr:%s", tt.desc, tt.inIOPSpec, gotErr, wantErr) + } + if tt.wantErr == "" { + if want := tt.want; !util.IsYAMLEqual(gotSpec, want) { + t.Errorf("ValuesToK8s(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, gotSpec, want, util.YAMLDiff(gotSpec, want)) + } + } + }) + } +} + +// errToString returns the string representation of err and the empty string if +// err is nil. +func errToString(err error) string { + if err == nil { + return "" + } + return err.Error() +} diff --git a/operator/pkg/translate/yaml_tree.go b/operator/pkg/translate/yaml_tree.go new file mode 100644 index 0000000..566a7ea --- /dev/null +++ b/operator/pkg/translate/yaml_tree.go @@ -0,0 +1,57 @@ +// Copyright Istio 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 translate + +import ( + "gopkg.in/yaml.v2" + + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" +) + +// YAMLTree takes an input tree inTreeStr, a partially constructed output tree outTreeStr, and a map of +// translations of source-path:dest-path in pkg/tpath format. It returns an output tree with paths from the input +// tree, translated and overlaid on the output tree. +func YAMLTree(inTreeStr, outTreeStr string, translations map[string]string) (string, error) { + inTree := make(map[string]any) + if err := yaml.Unmarshal([]byte(inTreeStr), &inTree); err != nil { + return "", err + } + outTree := make(map[string]any) + if err := yaml.Unmarshal([]byte(outTreeStr), &outTree); err != nil { + return "", err + } + + for inPath, translation := range translations { + path := util.PathFromString(inPath) + node, found, err := tpath.Find(inTree, path) + if err != nil { + return "", err + } + if !found { + continue + } + + if err := tpath.MergeNode(outTree, util.PathFromString(translation), node); err != nil { + return "", err + } + } + + outYAML, err := yaml.Marshal(outTree) + if err != nil { + return "", err + } + return string(outYAML), nil +} diff --git a/operator/pkg/util/clog/clog.go b/operator/pkg/util/clog/clog.go new file mode 100644 index 0000000..a79626f --- /dev/null +++ b/operator/pkg/util/clog/clog.go @@ -0,0 +1,109 @@ +// Copyright Istio 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 clog + +import ( + "fmt" + "io" + "os" + + "istio.io/istio/pkg/log" +) + +// Logger provides optional log taps for console and test buffer outputs. +type Logger interface { + LogAndPrint(v ...any) + LogAndError(v ...any) + LogAndFatal(a ...any) + LogAndPrintf(format string, a ...any) + LogAndErrorf(format string, a ...any) + LogAndFatalf(format string, a ...any) + Print(s string) + PrintErr(s string) +} + +// ConsoleLogger is the struct used for mesh command +type ConsoleLogger struct { + stdOut io.Writer + stdErr io.Writer + scope *log.Scope +} + +// NewConsoleLogger creates a new logger and returns a pointer to it. +// stdOut and stdErr can be used to capture output for testing. If scope is nil, the default scope is used. +func NewConsoleLogger(stdOut, stdErr io.Writer, scope *log.Scope) *ConsoleLogger { + s := scope + if s == nil { + s = log.RegisterScope(log.DefaultScopeName, log.DefaultScopeName) + } + return &ConsoleLogger{ + stdOut: stdOut, + stdErr: stdErr, + scope: s, + } +} + +// NewDefaultLogger creates a new logger that outputs to stdout/stderr at default scope. +func NewDefaultLogger() *ConsoleLogger { + return NewConsoleLogger(os.Stdout, os.Stderr, nil) +} + +func (l *ConsoleLogger) LogAndPrint(v ...any) { + if len(v) == 0 { + return + } + s := fmt.Sprint(v...) + l.Print(s + "\n") + l.scope.Infof(s) +} + +func (l *ConsoleLogger) LogAndError(v ...any) { + if len(v) == 0 { + return + } + s := fmt.Sprint(v...) + l.PrintErr(s + "\n") + l.scope.Infof(s) +} + +func (l *ConsoleLogger) LogAndFatal(a ...any) { + l.LogAndError(a...) + os.Exit(-1) +} + +func (l *ConsoleLogger) LogAndPrintf(format string, a ...any) { + s := fmt.Sprintf(format, a...) + l.Print(s + "\n") + l.scope.Infof(s) +} + +func (l *ConsoleLogger) LogAndErrorf(format string, a ...any) { + s := fmt.Sprintf(format, a...) + l.PrintErr(s + "\n") + l.scope.Infof(s) +} + +func (l *ConsoleLogger) LogAndFatalf(format string, a ...any) { + l.LogAndErrorf(format, a...) + os.Exit(-1) +} + +func (l *ConsoleLogger) Print(s string) { + _, _ = l.stdOut.Write([]byte(s)) +} + +func (l *ConsoleLogger) PrintErr(s string) { + _, _ = l.stdErr.Write([]byte(s)) +} diff --git a/operator/pkg/util/common.go b/operator/pkg/util/common.go new file mode 100644 index 0000000..7709ebe --- /dev/null +++ b/operator/pkg/util/common.go @@ -0,0 +1,40 @@ +// Copyright Istio 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 util + +import ( + "fmt" + "net/url" + "strings" + + "istio.io/istio/pkg/log" +) + +var scope = log.RegisterScope("util", "util") + +// IsFilePath reports whether the given URL is a local file path. +func IsFilePath(path string) bool { + return strings.Contains(path, "/") || strings.Contains(path, ".") +} + +// IsHTTPURL checks whether the given URL is a HTTP URL. +func IsHTTPURL(path string) (bool, error) { + u, err := url.Parse(path) + valid := err == nil && u.Host != "" && (u.Scheme == "http" || u.Scheme == "https") + if strings.HasPrefix(path, "http") && !valid { + return false, fmt.Errorf("%s starts with http but is not a valid URL: %s", path, err) + } + return valid, nil +} diff --git a/operator/pkg/util/common_test.go b/operator/pkg/util/common_test.go new file mode 100644 index 0000000..079f8b1 --- /dev/null +++ b/operator/pkg/util/common_test.go @@ -0,0 +1,110 @@ +// Copyright Istio 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 util + +import ( + "errors" + "testing" +) + +func TestIsFilePath(t *testing.T) { + tests := []struct { + desc string + in string + want bool + }{ + { + desc: "empty", + in: "", + want: false, + }, + { + desc: "no-markers", + in: "foobar", + want: false, + }, + { + desc: "with-slash", + in: "/home/bobby/go_rocks/main", + want: true, + }, + { + desc: "with-period", + in: "istio.go", + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got, want := IsFilePath(tt.in), tt.want; !(got == want) { + t.Errorf("%s: got %v, want: %v", tt.desc, got, want) + } + }) + } +} + +func TestIsHTTPURL(t *testing.T) { + tests := []struct { + desc string + in string + want bool + err error + }{ + { + desc: "empty", + in: "", + want: false, + err: nil, + }, + { + desc: "http-standard-url", + in: "http://localhost:3000", + want: true, + err: nil, + }, + { + desc: "https-standard-url", + in: "https://golang.org/", + want: true, + err: nil, + }, + { + desc: "ftp-url", + in: "ftp://gopher:gopwd@localhost:3000/go", + want: false, + err: nil, + }, + { + desc: "tcp-discovery-url", + in: "tcp://127.0.0.1:80", + want: false, + err: nil, + }, + { + desc: "empty-http", + in: "http://", + want: false, + err: errors.New(""), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + got, err := IsHTTPURL(tt.in) + if want, wantErr := tt.want, tt.err; !(got == want) || ((err == nil && wantErr != nil) || (err != nil && wantErr == nil)) { + t.Errorf("%s: got :%v, wanted output: %v, got error: %v, wanted error: %v", tt.desc, got, want, err, wantErr) + } + }) + } +} diff --git a/operator/pkg/util/errs.go b/operator/pkg/util/errs.go new file mode 100644 index 0000000..4d72683 --- /dev/null +++ b/operator/pkg/util/errs.go @@ -0,0 +1,146 @@ +// Copyright Istio 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 util + +import ( + "fmt" +) + +const ( + defaultSeparator = ", " +) + +// Errors is a slice of error. +type Errors []error + +// Error implements the error#Error method. +func (e Errors) Error() string { + return ToString(e, defaultSeparator) +} + +// String implements the stringer#String method. +func (e Errors) String() string { + return e.Error() +} + +// ToError returns an error from Errors. +func (e Errors) ToError() error { + if len(e) == 0 { + return nil + } + return fmt.Errorf("%s", e) +} + +// Dedup removes any duplicated errors. +func (e Errors) Dedup() Errors { + logCountMap := make(map[string]int) + for _, ee := range e { + if ee == nil { + continue + } + item := ee.Error() + _, exist := logCountMap[item] + if exist { + logCountMap[item]++ + } else { + logCountMap[item] = 1 + } + } + var out Errors + for _, ee := range e { + item := ee.Error() + count := logCountMap[item] + if count == 0 { + continue + } + times := "" + if count > 1 { + times = fmt.Sprintf(" (repeated %d times)", count) + } + out = AppendErr(out, fmt.Errorf("%s%s", ee, times)) + // reset seen log count + logCountMap[item] = 0 + } + return out +} + +// NewErrs returns a slice of error with a single element err. +// If err is nil, returns nil. +func NewErrs(err error) Errors { + if err == nil { + return nil + } + return []error{err} +} + +// AppendErr appends err to errors if it is not nil and returns the result. +// If err is nil, it is not appended. +func AppendErr(errors []error, err error) Errors { + if err == nil { + if len(errors) == 0 { + return nil + } + return errors + } + return append(errors, err) +} + +// AppendErrs appends newErrs to errors and returns the result. +// If newErrs is empty, nothing is appended. +func AppendErrs(errors []error, newErrs []error) Errors { + if len(newErrs) == 0 { + return errors + } + for _, e := range newErrs { + errors = AppendErr(errors, e) + } + if len(errors) == 0 { + return nil + } + return errors +} + +// ToString returns a string representation of errors, with elements separated by separator string. Any nil errors in the +// slice are skipped. +func ToString(errors []error, separator string) string { + var out string + for i, e := range errors { + if e == nil { + continue + } + if i != 0 { + out += separator + } + out += e.Error() + } + return out +} + +// EqualErrors reports whether a and b are equal, regardless of ordering. +func EqualErrors(a, b Errors) bool { + if len(a) != len(b) { + return false + } + m := make(map[string]bool) + for _, e := range b { + m[e.Error()] = true + } + for _, ea := range a { + if !m[ea.Error()] { + return false + } + } + return true +} diff --git a/operator/pkg/util/errs_test.go b/operator/pkg/util/errs_test.go new file mode 100644 index 0000000..80d17cf --- /dev/null +++ b/operator/pkg/util/errs_test.go @@ -0,0 +1,121 @@ +// Copyright Istio 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 util + +import ( + "fmt" + "testing" +) + +var ( + testErrs = Errors{fmt.Errorf("err1"), fmt.Errorf("err2")} + wantStr = "err1, err2" +) + +func TestError(t *testing.T) { + if got, want := testErrs.Error(), wantStr; got != want { + t.Errorf("got: %s, want: %s", got, want) + } +} + +func TestString(t *testing.T) { + if got, want := testErrs.String(), wantStr; got != want { + t.Errorf("got: %s, want: %s", got, want) + } +} + +func TestToString(t *testing.T) { + if got, want := ToString(testErrs, defaultSeparator), wantStr; got != want { + t.Errorf("got: %s, want: %s", got, want) + } +} + +func TestNewErrs(t *testing.T) { + errs := NewErrs(nil) + if errs != nil { + t.Errorf("got: %s, want: nil", errs) + } + + errs = NewErrs(fmt.Errorf("err1")) + if got, want := errs.String(), "err1"; got != want { + t.Errorf("got: %s, want: %s", got, want) + } +} + +func TestAppendErr(t *testing.T) { + var errs Errors + if got, want := errs.String(), ""; got != want { + t.Errorf("got: %s, want: %s", got, want) + } + + errs = AppendErr(errs, nil) + if got, want := errs.String(), ""; got != want { + t.Errorf("got: %s, want: %s", got, want) + } + + errs = AppendErr(errs, fmt.Errorf("err1")) + if got, want := errs.String(), "err1"; got != want { + t.Errorf("got: %s, want: %s", got, want) + } + + errs = AppendErr(errs, nil) + errs = AppendErr(errs, fmt.Errorf("err2")) + if got, want := errs.String(), "err1, err2"; got != want { + t.Errorf("got: %s, want: %s", got, want) + } +} + +func TestAppendErrs(t *testing.T) { + var errs Errors + + errs = AppendErrs(errs, []error{nil}) + if got, want := errs.String(), ""; got != want { + t.Errorf("got: %s, want: %s", got, want) + } + + errs = AppendErrs(errs, testErrs) + errs = AppendErrs(errs, []error{nil}) + if got, want := errs.String(), wantStr; got != want { + t.Errorf("got: %s, want: %s", got, want) + } +} + +func TestAppendErrsInFunction(t *testing.T) { + myAppendErrFunc := func() (errs Errors) { + errs = AppendErr(errs, fmt.Errorf("err1")) + errs = AppendErr(errs, fmt.Errorf("err2")) + return + } + if got, want := myAppendErrFunc().String(), wantStr; got != want { + t.Errorf("got: %s, want: %s", got, want) + } + + myAppendErrsFunc := func() (errs Errors) { + errs = AppendErrs(errs, testErrs) + return + } + if got, want := myAppendErrsFunc().String(), wantStr; got != want { + t.Errorf("got: %s, want: %s", got, want) + } + + myErrorSliceFunc := func() (errs []error) { + errs = AppendErrs(errs, testErrs) + return + } + + if got, want := Errors(myErrorSliceFunc()).String(), wantStr; got != want { + t.Errorf("got: %s, want: %s", got, want) + } +} diff --git a/operator/pkg/util/k8s.go b/operator/pkg/util/k8s.go new file mode 100644 index 0000000..2d6db27 --- /dev/null +++ b/operator/pkg/util/k8s.go @@ -0,0 +1,119 @@ +// Copyright Istio 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 util + +import ( + "context" + "fmt" + "strconv" + + "github.com/prometheus/prometheus/util/strutil" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes" + + "istio.io/api/label" + iopv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "istio.io/istio/pkg/config/constants" + "istio.io/istio/pkg/kube" +) + +// GKString differs from default representation of GroupKind +func GKString(gvk schema.GroupKind) string { + return fmt.Sprintf("%s/%s", gvk.Group, gvk.Kind) +} + +// ValidateIOPCAConfig validates if the IstioOperator CA configs are applicable to the K8s cluster +func ValidateIOPCAConfig(client kube.Client, iop *iopv1alpha1.IstioOperator) error { + globalI := iop.Spec.Values.AsMap()["global"] + global, ok := globalI.(map[string]any) + if !ok { + // This means no explicit global configuration. Still okay + return nil + } + ca, ok := global["pilotCertProvider"].(string) + if !ok { + // This means the default pilotCertProvider is being used + return nil + } + if ca == "kubernetes" { + ver, err := client.GetKubernetesVersion() + if err != nil { + return fmt.Errorf("failed to determine support for K8s legacy signer. Use the --force flag to ignore this: %v", err) + } + + if kube.IsAtLeastVersion(client, 22) { + return fmt.Errorf("configuration PILOT_CERT_PROVIDER=%s not supported in Kubernetes %v."+ + "Please pick another value for PILOT_CERT_PROVIDER", ca, ver.String()) + } + } + return nil +} + +// CreateNamespace creates a namespace using the given k8s interface. +func CreateNamespace(cs kubernetes.Interface, namespace string, network string, dryRun bool) error { + if dryRun { + scope.Infof("Not applying Namespace %s because of dry run.", namespace) + return nil + } + if namespace == "" { + // Setup default namespace + namespace = constants.IstioSystemNamespace + } + // check if the namespace already exists. If yes, do nothing. If no, create a new one. + if _, err := cs.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{}); err != nil { + if errors.IsNotFound(err) { + ns := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{ + Name: namespace, + Labels: map[string]string{}, + }} + if network != "" { + ns.Labels[label.TopologyNetwork.Name] = network + } + _, err := cs.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{}) + if err != nil { + return fmt.Errorf("failed to create namespace %v: %v", namespace, err) + } + + return nil + } + + return fmt.Errorf("failed to check if namespace %v exists: %v", namespace, err) + } + + return nil +} + +func PrometheusPathAndPort(pod *v1.Pod) (string, int, error) { + path := "/metrics" + port := 9090 + for key, val := range pod.ObjectMeta.Annotations { + switch strutil.SanitizeLabelName(key) { + case "prometheus_io_port": + p, err := strconv.Atoi(val) + if err != nil { + return "", 0, fmt.Errorf("failed to parse port from annotation: %v", err) + } + + port = p + case "prometheus_io_path": + path = val + } + } + + return path, port, nil +} diff --git a/operator/pkg/util/k8s_test.go b/operator/pkg/util/k8s_test.go new file mode 100644 index 0000000..7259c01 --- /dev/null +++ b/operator/pkg/util/k8s_test.go @@ -0,0 +1,165 @@ +// Copyright Istio 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 util + +import ( + "testing" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/version" + fakediscovery "k8s.io/client-go/discovery/fake" + "sigs.k8s.io/yaml" + + pkgAPI "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "istio.io/istio/pkg/kube" + "istio.io/istio/pkg/test/util/assert" +) + +var ( + o1 = ` +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + values: + global: + pilotCertProvider: kubernetes +` + o2 = ` +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + values: + global: + pilotCertProvider: istiod +` + o3 = ` +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + values: +` +) + +func TestValidateIOPCAConfig(t *testing.T) { + var err error + + tests := []struct { + major string + minor string + expErr bool + operatorYaml string + }{ + { + major: "1", + minor: "16", + expErr: false, + operatorYaml: o1, + }, + { + major: "1", + minor: "22", + expErr: true, + operatorYaml: o1, + }, + { + major: "1", + minor: "23", + expErr: false, + operatorYaml: o2, + }, + { + major: "1", + minor: "24", + expErr: false, + operatorYaml: o3, + }, + } + + for i, tt := range tests { + k8sClient := kube.NewFakeClient() + k8sClient.Kube().Discovery().(*fakediscovery.FakeDiscovery).FakedServerVersion = &version.Info{ + Major: tt.major, + Minor: tt.minor, + } + op := &pkgAPI.IstioOperator{} + err = yaml.Unmarshal([]byte(tt.operatorYaml), op) + if err != nil { + t.Fatalf("Failure in test case %v. Error %s", i, err) + } + err = ValidateIOPCAConfig(k8sClient, op) + if !tt.expErr && err != nil { + t.Fatalf("Failure in test case %v. Expected No Error. Got %s", i, err) + } else if tt.expErr && err == nil { + t.Fatalf("Failure in test case %v. Expected Error. Got No error", i) + } + } +} + +func TestPrometheusPathAndPort(t *testing.T) { + cases := []struct { + pod *v1.Pod + path string + port int + }{ + { + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "case-1", + Annotations: map[string]string{ + "prometheus.io/path": "/metrics", + "prometheus.io/port": "15020", + }, + }, + }, + path: "/metrics", + port: 15020, + }, + { + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "case-2", + Annotations: map[string]string{ + "prometheus.io.path": "/metrics", + "prometheus.io.port": "15020", + }, + }, + }, + path: "/metrics", + port: 15020, + }, + { + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "case-3", + Annotations: map[string]string{ + "prometheus-io/path": "/metrics", + "prometheus-io/port": "15020", + }, + }, + }, + path: "/metrics", + port: 15020, + }, + } + + for _, tc := range cases { + t.Run(tc.pod.Name, func(t *testing.T) { + path, port, err := PrometheusPathAndPort(tc.pod) + assert.NoError(t, err) + assert.Equal(t, tc.path, path) + assert.Equal(t, tc.port, port) + }) + } +} diff --git a/operator/pkg/util/label.go b/operator/pkg/util/label.go new file mode 100644 index 0000000..307a5dc --- /dev/null +++ b/operator/pkg/util/label.go @@ -0,0 +1,35 @@ +// Copyright Istio 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 util + +import ( + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" +) + +// SetLabel is a helper function which sets the specified label and value on the specified object. +func SetLabel(resource runtime.Object, label, value string) error { + resourceAccessor, err := meta.Accessor(resource) + if err != nil { + return err + } + labels := resourceAccessor.GetLabels() + if labels == nil { + labels = map[string]string{} + } + labels[label] = value + resourceAccessor.SetLabels(labels) + return nil +} diff --git a/operator/pkg/util/label_test.go b/operator/pkg/util/label_test.go new file mode 100644 index 0000000..070df0f --- /dev/null +++ b/operator/pkg/util/label_test.go @@ -0,0 +1,49 @@ +// Copyright Istio 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 util + +import ( + "testing" + + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func TestSetLabel(t *testing.T) { + tests := []struct { + desc string + wantLabel string + wantValue string + wantErr error + }{ + { + desc: "AddMapLabelMapValue", + wantLabel: "foo", + wantValue: "bar", + wantErr: nil, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + resource := &unstructured.Unstructured{Object: make(map[string]any)} + gotErr := SetLabel(resource, tt.wantLabel, tt.wantValue) + resourceAccessor, _ := meta.Accessor(resource) + labels := resourceAccessor.GetLabels() + if gotVal, ok := labels[tt.wantLabel]; !ok || gotVal != tt.wantValue || gotErr != tt.wantErr { + t.Errorf("%s: ok: %v, got value: %v, want value: %v, got error: %v, want error: %v", tt.desc, ok, gotVal, tt.wantValue, gotErr, tt.wantErr) + } + }) + } +} diff --git a/operator/pkg/util/merge_iop.go b/operator/pkg/util/merge_iop.go new file mode 100644 index 0000000..bfabddf --- /dev/null +++ b/operator/pkg/util/merge_iop.go @@ -0,0 +1,267 @@ +// Copyright Istio 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 util + +import ( + "fmt" + "strings" + + "google.golang.org/protobuf/types/known/durationpb" + wrappers "google.golang.org/protobuf/types/known/wrapperspb" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/strategicpatch" + yaml2 "sigs.k8s.io/yaml" + + v1alpha13 "istio.io/api/mesh/v1alpha1" + "istio.io/api/networking/v1alpha3" + "istio.io/api/123/operator/v1alpha1" + v1alpha12 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" +) + +// Partially mirrored from istio/api and operator/pkg/api (for values). +// Struct tags are required to use k8s strategic merge library. It would be possible +// to add these to source protos but because the values field is defined as +// map[string]interface{} here (and similar for MeshConfig in v1alpha1.Values) +// that alone would not be sufficient. +// Only non-scalar types require tags, therefore most fields are omitted here. +type iopMergeStructType struct { + metav1.ObjectMeta `json:"metadata" patchStrategy:"merge"` + Spec istioOperatorSpec `json:"spec" patchStrategy:"merge"` +} + +type istioOperatorSpec struct { + MeshConfig *meshConfig `json:"meshConfig" patchStrategy:"merge"` + Components *istioComponentSetSpec `json:"components" patchStrategy:"merge"` + Values *values `json:"values" patchStrategy:"merge"` +} + +type istioComponentSetSpec struct { + Base *baseComponentSpec `json:"base" patchStrategy:"merge"` + Pilot *componentSpec `json:"pilot" patchStrategy:"merge"` + Cni *componentSpec `json:"cni" patchStrategy:"merge"` + Ztunel *componentSpec `json:"ztunnel" patchStrategy:"merge"` + IstiodRemote *componentSpec `json:"istiodRemote" patchStrategy:"merge"` + IngressGateways []*gatewaySpec `json:"ingressGateways" patchStrategy:"merge" patchMergeKey:"name"` + EgressGateways []*gatewaySpec `json:"egressGateways" patchStrategy:"merge" patchMergeKey:"name"` +} + +type baseComponentSpec struct { + K8S *v1alpha1.KubernetesResourcesSpec `json:"k8s" patchStrategy:"merge"` +} + +type componentSpec struct { + K8S *v1alpha1.KubernetesResourcesSpec `json:"k8s" patchStrategy:"merge"` +} + +type gatewaySpec struct { + Label map[string]string `json:"label" patchStrategy:"merge"` + K8S *v1alpha1.KubernetesResourcesSpec `json:"k8s" patchStrategy:"merge"` +} + +type values struct { + Cni *v1alpha12.CNIConfig `json:"cni" patchStrategy:"merge"` + Gateways *gatewaysConfig `json:"gateways" patchStrategy:"merge"` + Global *v1alpha12.GlobalConfig `json:"global" patchStrategy:"merge"` + Pilot *v1alpha12.PilotConfig `json:"pilot" patchStrategy:"merge"` + Telemetry *telemetryConfig `json:"telemetry" patchStrategy:"merge"` + SidecarInjectorWebhook *v1alpha12.SidecarInjectorConfig `json:"sidecarInjectorWebhook" patchStrategy:"merge"` + IstioCni *v1alpha12.CNIConfig `json:"istio_cni" patchStrategy:"merge"` + MeshConfig *meshConfig `json:"meshConfig" patchStrategy:"merge"` + Base *v1alpha12.BaseConfig `json:"base" patchStrategy:"merge"` + IstiodRemote *v1alpha12.IstiodRemoteConfig `json:"istiodRemote" patchStrategy:"merge"` + Ztunnel map[string]any `json:"ztunnel" patchStrategy:"merge"` +} + +type gatewaysConfig struct { + SecurityContext *v1alpha1.PodSecurityContext `json:"securityContext" patchStrategy:"merge"` + IstioEgressgateway *egressGatewayConfig `json:"istio-egressgateway" patchStrategy:"merge"` + IstioIngressgateway *ingressGatewayConfig `json:"istio-ingressgateway" patchStrategy:"merge"` +} + +// Configuration for an ingress gateway. +type ingressGatewayConfig struct { + Env map[string]any `json:"env" patchStrategy:"merge"` + Labels map[string]string `json:"labels" patchStrategy:"merge"` + CPU *v1alpha12.TargetUtilizationConfig `json:"cpu" patchStrategy:"replace"` + Memory *v1alpha12.TargetUtilizationConfig `json:"memory" patchStrategy:"replace"` + LoadBalancerSourceRanges []string `json:"loadBalancerSourceRanges" patchStrategy:"replace"` + NodeSelector map[string]any `json:"nodeSelector" patchStrategy:"merge"` + PodAntiAffinityLabelSelector []map[string]any `json:"podAntiAffinityLabelSelector" patchStrategy:"replace"` + PodAntiAffinityTermLabelSelector []map[string]any `json:"podAntiAffinityTermLabelSelector" patchStrategy:"replace"` + PodAnnotations map[string]any `json:"podAnnotations" patchStrategy:"merge"` + MeshExpansionPorts []*v1alpha12.PortsConfig `json:"meshExpansionPorts" patchStrategy:"merge" patchMergeKey:"name"` + Ports []*v1alpha12.PortsConfig `json:"ports" patchStrategy:"merge" patchMergeKey:"name"` + Resources *resources `json:"resources" patchStrategy:"merge"` + SecretVolumes []*v1alpha12.SecretVolume `json:"secretVolumes" patchStrategy:"merge" patchMergeKey:"name"` + ServiceAnnotations map[string]any `json:"serviceAnnotations" patchStrategy:"merge"` + Tolerations []map[string]any `json:"tolerations" patchStrategy:"replace"` + IngressPorts []map[string]any `json:"ingressPorts" patchStrategy:"replace"` + AdditionalContainers []map[string]any `json:"additionalContainers" patchStrategy:"replace"` + ConfigVolumes []map[string]any `json:"configVolumes" patchStrategy:"replace"` + Zvpn *v1alpha12.IngressGatewayZvpnConfig `json:"zvpn" patchStrategy:"merge"` +} + +type resources struct { + Limits map[string]string `json:"limits" patchStrategy:"merge"` + Requests map[string]string `json:"requests" patchStrategy:"merge"` +} + +type egressGatewayConfig struct { + Env map[string]any `json:"env" patchStrategy:"merge"` + Labels map[string]string `json:"labels" patchStrategy:"merge"` + NodeSelector map[string]any `json:"nodeSelector" patchStrategy:"merge"` + PodAntiAffinityLabelSelector []map[string]any `json:"podAntiAffinityLabelSelector" patchStrategy:"replace"` + PodAntiAffinityTermLabelSelector []map[string]any `json:"podAntiAffinityTermLabelSelector" patchStrategy:"replace"` + PodAnnotations map[string]any `json:"podAnnotations" patchStrategy:"merge"` + Ports []*v1alpha12.PortsConfig `json:"ports" patchStrategy:"merge" patchMergeKey:"name"` + Resources *resources `json:"resources" patchStrategy:"merge"` + SecretVolumes []*v1alpha12.SecretVolume `json:"secretVolumes" patchStrategy:"merge" patchMergeKey:"name"` + Tolerations []map[string]any `json:"tolerations" patchStrategy:"replace"` + ConfigVolumes []map[string]any `json:"configVolumes" patchStrategy:"replace"` + AdditionalContainers []map[string]any `json:"additionalContainers" patchStrategy:"replace"` + Zvpn *v1alpha12.ZeroVPNConfig `json:"zvpn" patchStrategy:"replace"` +} + +type meshConfig struct { + ConnectTimeout *durationpb.Duration `json:"connectTimeout" patchStrategy:"replace"` + ProtocolDetectionTimeout *durationpb.Duration `json:"protocolDetectionTimeout" patchStrategy:"replace"` + RdsRefreshDelay *durationpb.Duration `json:"rdsRefreshDelay" patchStrategy:"replace"` + EnableAutoMtls *wrappers.BoolValue `json:"enableAutoMtls" patchStrategy:"replace"` + EnablePrometheusMerge *wrappers.BoolValue `json:"enablePrometheusMerge" patchStrategy:"replace"` + OutboundTrafficPolicy *v1alpha13.MeshConfig_OutboundTrafficPolicy `json:"outboundTrafficPolicy" patchStrategy:"merge"` + InboundTrafficPolicy *v1alpha13.MeshConfig_InboundTrafficPolicy `json:"inboundTrafficPolicy" patchStrategy:"merge"` + TCPKeepalive *v1alpha3.ConnectionPoolSettings_TCPSettings_TcpKeepalive `json:"tcpKeepalive" patchStrategy:"merge"` + DefaultConfig *proxyConfig `json:"defaultConfig" patchStrategy:"merge"` + ConfigSources []*v1alpha13.ConfigSource `json:"configSources" patchStrategy:"merge" patchMergeKey:"address"` + TrustDomainAliases []string `json:"trustDomainAliases" patchStrategy:"merge"` + DefaultServiceExportTo []string `json:"defaultServiceExportTo" patchStrategy:"merge"` + DefaultVirtualServiceExportTo []string `json:"defaultVirtualServiceExportTo" patchStrategy:"merge"` + DefaultDestinationRuleExportTo []string `json:"defaultDestinationRuleExportTo" patchStrategy:"merge"` + LocalityLbSetting *v1alpha3.LocalityLoadBalancerSetting `json:"localityLbSetting" patchStrategy:"merge"` + DNSRefreshRate *durationpb.Duration `json:"dnsRefreshRate" patchStrategy:"replace"` + Certificates []*v1alpha13.Certificate `json:"certificates" patchStrategy:"merge" patchMergeKey:"secretName"` + ServiceSettings []*meshConfigServiceSettings `json:"serviceSettings" patchStrategy:"replace"` + DefaultProviders *meshConfigDefaultProviders `json:"defaultProviders" patchStrategy:"merge"` + ExtensionProviders []*meshConfigExtensionProvider `json:"extensionProviders" patchStrategy:"merge" patchMergeKey:"name"` +} + +type ( + meshConfigDefaultProviders struct { + AccessLogging []struct{} `json:"accessLogging"` + Tracing []struct{} `json:"tracing"` + Metrics []struct{} `json:"metrics"` + } + meshConfigExtensionProvider struct { + Name string `json:"string"` + EnvoyOtelAls struct{} `json:"envoyOtelAls"` + Prometheus struct{} `json:"prometheus"` + EnvoyFileAccessLog struct{} `json:"envoyFileAccessLog"` + Stackdriver struct{} `json:"stackdriver"` + EnvoyExtAuthzHTTP struct{} `json:"envoyExtAuthzHttp"` + EnvoyExtAuthzGrpc struct{} `json:"envoyExtAuthzGrpc"` + Zipkin struct{} `json:"zipkin"` + Lightstep struct{} `json:"lightstep"` + Datadog struct{} `json:"datadog"` + Opencensus struct{} `json:"opencensus"` + Skywalking struct{} `json:"skywalking"` + EnvoyHTTPAls struct{} `json:"envoyHttpAls"` + EnvoyTCPAls struct{} `json:"envoyTcpAls"` + OpenTelemetry struct{} `json:"opentelemetry"` + } + clusterName struct { + ServiceCluster *v1alpha13.ProxyConfig_ServiceCluster `json:"serviceCluster,omitempty"` + TracingServiceName *v1alpha13.ProxyConfig_TracingServiceName_ `json:"tracingServiceName,omitempty"` + } +) + +type proxyConfig struct { + DrainDuration *durationpb.Duration `json:"drainDuration" patchStrategy:"replace"` + DiscoveryRefreshDelay *durationpb.Duration `json:"discoveryRefreshDelay" patchStrategy:"replace"` + TerminationDrainDuration *durationpb.Duration `json:"terminationDrainDuration" patchStrategy:"replace"` + Concurrency *wrappers.Int32Value `json:"concurrency" patchStrategy:"replace"` + ConfigSources []*v1alpha13.ConfigSource `json:"configSources" patchStrategy:"replace"` + ClusterName *clusterName `json:"clusterName" patchStrategy:"replace"` + TrustDomainAliases []string `json:"trustDomainAliases" patchStrategy:"replace"` + DefaultServiceExportTo []string `json:"defaultServiceExportTo" patchStrategy:"replace"` + DefaultVirtualServiceExportTo []string `json:"defaultVirtualServiceExportTo" patchStrategy:"replace"` + DefaultDestinationRuleExportTo []string `json:"defaultDestinationRuleExportTo" patchStrategy:"replace"` + LocalityLbSetting *v1alpha3.LocalityLoadBalancerSetting `json:"localityLbSetting" patchStrategy:"merge"` + DNSRefreshRate *durationpb.Duration `json:"dnsRefreshRate" patchStrategy:"replace"` + Certificates []*v1alpha13.Certificate `json:"certificates" patchStrategy:"replace"` + ServiceSettings []*v1alpha13.MeshConfig_ServiceSettings `json:"serviceSettings" patchStrategy:"replace"` + Tracing *tracing `json:"tracing" patchStrategy:"replace"` + Sds *v1alpha13.SDS `json:"sds" patchStrategy:"replace"` + EnvoyAccessLogService *v1alpha13.RemoteService `json:"envoyAccessLogService" patchStrategy:"merge" patchMergeKey:"address"` + EnvoyMetricsService *v1alpha13.RemoteService `json:"envoyMetricsService" patchStrategy:"merge" patchMergeKey:"address"` + ProxyMetadata map[string]string `json:"proxyMetadata" patchStrategy:"merge"` + ExtraStatTags []string `json:"extraStatTags" patchStrategy:"replace"` + GatewayTopology *v1alpha13.Topology `json:"gatewayTopology" patchStrategy:"replace"` +} + +type tracing struct { + TlSSettings *v1alpha3.ClientTLSSettings `json:"tlsSettings" patchStrategy:"merge"` +} + +type meshConfigServiceSettings struct { + Settings *v1alpha13.MeshConfig_ServiceSettings_Settings `json:"settings" patchStrategy:"merge"` + Hosts []string `json:"hosts" patchStrategy:"merge"` +} + +type telemetryConfig struct { + V2 *telemetryV2Config `json:"v2" patchStrategy:"merge"` +} + +type telemetryV2Config struct { + Prometheus *v1alpha12.TelemetryV2PrometheusConfig `json:"prometheus" patchStrategy:"merge"` + Stackdriver *v1alpha12.TelemetryV2StackDriverConfig `json:"stackdriver" patchStrategy:"merge"` +} + +var iopMergeStruct iopMergeStructType + +// OverlayIOP overlays over base using JSON strategic merge. +func OverlayIOP(base, overlay string) (string, error) { + if strings.TrimSpace(base) == "" { + return overlay, nil + } + if strings.TrimSpace(overlay) == "" { + return base, nil + } + bj, err := yaml2.YAMLToJSON([]byte(base)) + if err != nil { + return "", fmt.Errorf("yamlToJSON error in base: %s\n%s", err, bj) + } + oj, err := yaml2.YAMLToJSON([]byte(overlay)) + if err != nil { + return "", fmt.Errorf("yamlToJSON error in overlay: %s\n%s", err, oj) + } + if base == "" { + bj = []byte("{}") + } + if overlay == "" { + oj = []byte("{}") + } + + merged, err := strategicpatch.StrategicMergePatch(bj, oj, &iopMergeStruct) + if err != nil { + return "", fmt.Errorf("json merge error (%s) for base object: \n%s\n override object: \n%s", err, bj, oj) + } + + my, err := yaml2.JSONToYAML(merged) + if err != nil { + return "", fmt.Errorf("jsonToYAML error (%s) for merged object: \n%s", err, merged) + } + + return string(my), nil +} diff --git a/operator/pkg/util/merge_iop_test.go b/operator/pkg/util/merge_iop_test.go new file mode 100644 index 0000000..74ec635 --- /dev/null +++ b/operator/pkg/util/merge_iop_test.go @@ -0,0 +1,143 @@ +// Copyright Istio 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 util + +import ( + "os" + "path/filepath" + "testing" + + "sigs.k8s.io/yaml" + + meshconfig "istio.io/api/mesh/v1alpha1" + v1alpha12 "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "istio.io/istio/pkg/config/mesh" + "istio.io/istio/pkg/test/env" + "istio.io/istio/pkg/util/protomarshal" +) + +func TestOverlayIOP(t *testing.T) { + cases := []struct { + path string + }{ + { + filepath.Join(env.IstioSrc, "manifests/profiles/default.yaml"), + }, + { + filepath.Join(env.IstioSrc, "manifests/profiles/demo.yaml"), + }, + { + filepath.Join("testdata", "overlay-iop.yaml"), + }, + } + + for _, tc := range cases { + t.Run(tc.path, func(t *testing.T) { + b, err := os.ReadFile(tc.path) + if err != nil { + t.Fatal(err) + } + // overlaying tree over itself exercises all paths for merging + if _, err := OverlayIOP(string(b), string(b)); err != nil { + t.Fatal(err) + } + }) + } +} + +// TestOverlayIOPExhaustiveness exhaustiveness check of `OverlayIOP` +// Once some one add a new `Provider` in api, we should update `wellknownProviders` and +// add to `meshConfigExtensionProvider` +func TestOverlayIOPExhaustiveness(t *testing.T) { + wellknownProviders := map[string]struct{}{ + "prometheus": {}, + "envoy_file_access_log": {}, + "stackdriver": {}, + "envoy_otel_als": {}, + "envoy_ext_authz_http": {}, + "envoy_ext_authz_grpc": {}, + "zipkin": {}, + "lightstep": {}, + "datadog": {}, + "opencensus": {}, + "skywalking": {}, + "envoy_http_als": {}, + "envoy_tcp_als": {}, + "opentelemetry": {}, + } + + unexpectedProviders := make([]string, 0) + + msg := &meshconfig.MeshConfig_ExtensionProvider{} + pb := msg.ProtoReflect() + md := pb.Descriptor() + + of := md.Oneofs().Get(0) + for i := 0; i < of.Fields().Len(); i++ { + o := of.Fields().Get(i) + n := string(o.Name()) + if _, ok := wellknownProviders[n]; ok { + delete(wellknownProviders, n) + } else { + unexpectedProviders = append(unexpectedProviders, n) + } + } + + if len(wellknownProviders) != 0 || len(unexpectedProviders) != 0 { + t.Errorf("unexpected provider not implemented in OverlayIOP, wellknownProviders: %v unexpectedProviders: %v", wellknownProviders, unexpectedProviders) + t.Fail() + } +} + +func TestOverlayIOPDefaultMeshConfig(t *testing.T) { + // Transform default mesh config into map[string]interface{} for inclusion in IstioOperator. + m := mesh.DefaultMeshConfig() + my, err := protomarshal.ToJSONMap(m) + if err != nil { + t.Fatal(err) + } + + iop := &v1alpha1.IstioOperator{ + Spec: &v1alpha12.IstioOperatorSpec{ + MeshConfig: MustStruct(my), + }, + } + + iy, err := yaml.Marshal(iop) + if err != nil { + t.Fatal(err) + } + + // overlaying tree over itself exercises all paths for merging + if _, err := OverlayIOP(string(iy), string(iy)); err != nil { + t.Fatal(err) + } +} + +func TestOverlayIOPIngressGatewayLabel(t *testing.T) { + l1, err := os.ReadFile("testdata/yaml/input/yaml_layer1.yaml") + if err != nil { + t.Fatal(err) + } + l2, err := os.ReadFile("testdata/yaml/input/yaml_layer2.yaml") + if err != nil { + t.Fatal(err) + } + + if _, err := OverlayIOP(string(l1), string(l2)); err != nil { + t.Fatal(err) + } +} diff --git a/operator/pkg/util/path.go b/operator/pkg/util/path.go new file mode 100644 index 0000000..43653d4 --- /dev/null +++ b/operator/pkg/util/path.go @@ -0,0 +1,207 @@ +// Copyright Istio 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 util + +import ( + "fmt" + "path/filepath" + "regexp" + "strconv" + "strings" +) + +const ( + // PathSeparator is the separator between path elements. + PathSeparator = "." + kvSeparatorRune = ':' + + // InsertIndex is the index that means "insert" when setting values + InsertIndex = -1 + + // PathSeparatorRune is the separator between path elements, as a rune. + pathSeparatorRune = '.' + // EscapedPathSeparator is what to use when the path shouldn't separate + EscapedPathSeparator = "\\" + PathSeparator +) + +// ValidKeyRegex is a regex for a valid path key element. +var ValidKeyRegex = regexp.MustCompile("^[a-zA-Z0-9_-]*$") + +// Path is a path in slice form. +type Path []string + +// PathFromString converts a string path of form a.b.c to a string slice representation. +func PathFromString(path string) Path { + path = filepath.Clean(path) + path = strings.TrimPrefix(path, PathSeparator) + path = strings.TrimSuffix(path, PathSeparator) + pv := splitEscaped(path, pathSeparatorRune) + var r []string + for _, str := range pv { + if str != "" { + str = strings.ReplaceAll(str, EscapedPathSeparator, PathSeparator) + // Is str of the form node[expr], convert to "node", "[expr]"? + nBracket := strings.IndexRune(str, '[') + if nBracket > 0 { + r = append(r, str[:nBracket], str[nBracket:]) + } else { + // str is "[expr]" or "node" + r = append(r, str) + } + } + } + return r +} + +// String converts a string slice path representation of form ["a", "b", "c"] to a string representation like "a.b.c". +func (p Path) String() string { + return strings.Join(p, PathSeparator) +} + +func (p Path) Equals(p2 Path) bool { + if len(p) != len(p2) { + return false + } + for i, pp := range p { + if pp != p2[i] { + return false + } + } + return true +} + +// ToYAMLPath converts a path string to path such that the first letter of each path element is lower case. +func ToYAMLPath(path string) Path { + p := PathFromString(path) + for i := range p { + p[i] = firstCharToLowerCase(p[i]) + } + return p +} + +// ToYAMLPathString converts a path string such that the first letter of each path element is lower case. +func ToYAMLPathString(path string) string { + return ToYAMLPath(path).String() +} + +// IsValidPathElement reports whether pe is a valid path element. +func IsValidPathElement(pe string) bool { + return ValidKeyRegex.MatchString(pe) +} + +// IsKVPathElement report whether pe is a key/value path element. +func IsKVPathElement(pe string) bool { + pe, ok := RemoveBrackets(pe) + if !ok { + return false + } + + kv := splitEscaped(pe, kvSeparatorRune) + if len(kv) != 2 || len(kv[0]) == 0 || len(kv[1]) == 0 { + return false + } + return IsValidPathElement(kv[0]) +} + +// IsVPathElement report whether pe is a value path element. +func IsVPathElement(pe string) bool { + pe, ok := RemoveBrackets(pe) + if !ok { + return false + } + + return len(pe) > 1 && pe[0] == ':' +} + +// IsNPathElement report whether pe is an index path element. +func IsNPathElement(pe string) bool { + pe, ok := RemoveBrackets(pe) + if !ok { + return false + } + + n, err := strconv.Atoi(pe) + return err == nil && n >= InsertIndex +} + +// PathKV returns the key and value string parts of the entire key/value path element. +// It returns an error if pe is not a key/value path element. +func PathKV(pe string) (k, v string, err error) { + if !IsKVPathElement(pe) { + return "", "", fmt.Errorf("%s is not a valid key:value path element", pe) + } + pe, _ = RemoveBrackets(pe) + kv := splitEscaped(pe, kvSeparatorRune) + return kv[0], kv[1], nil +} + +// PathV returns the value string part of the entire value path element. +// It returns an error if pe is not a value path element. +func PathV(pe string) (string, error) { + // For :val, return the value only + if IsVPathElement(pe) { + v, _ := RemoveBrackets(pe) + return v[1:], nil + } + + // For key:val, return the whole thing + v, _ := RemoveBrackets(pe) + if len(v) > 0 { + return v, nil + } + return "", fmt.Errorf("%s is not a valid value path element", pe) +} + +// PathN returns the index part of the entire value path element. +// It returns an error if pe is not an index path element. +func PathN(pe string) (int, error) { + if !IsNPathElement(pe) { + return -1, fmt.Errorf("%s is not a valid index path element", pe) + } + v, _ := RemoveBrackets(pe) + return strconv.Atoi(v) +} + +// RemoveBrackets removes the [] around pe and returns the resulting string. It returns false if pe is not surrounded +// by []. +func RemoveBrackets(pe string) (string, bool) { + if !strings.HasPrefix(pe, "[") || !strings.HasSuffix(pe, "]") { + return "", false + } + return pe[1 : len(pe)-1], true +} + +// splitEscaped splits a string using the rune r as a separator. It does not split on r if it's prefixed by \. +func splitEscaped(s string, r rune) []string { + var prev rune + if len(s) == 0 { + return []string{} + } + prevIdx := 0 + var out []string + for i, c := range s { + if c == r && (i == 0 || (i > 0 && prev != '\\')) { + out = append(out, s[prevIdx:i]) + prevIdx = i + 1 + } + prev = c + } + out = append(out, s[prevIdx:]) + return out +} + +func firstCharToLowerCase(s string) string { + return strings.ToLower(s[0:1]) + s[1:] +} diff --git a/operator/pkg/util/path_test.go b/operator/pkg/util/path_test.go new file mode 100644 index 0000000..79f5168 --- /dev/null +++ b/operator/pkg/util/path_test.go @@ -0,0 +1,383 @@ +// Copyright Istio 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 util + +import ( + "errors" + "testing" +) + +func TestSplitEscaped(t *testing.T) { + tests := []struct { + desc string + in string + want []string + }{ + { + desc: "empty", + in: "", + want: []string{}, + }, + { + desc: "no match", + in: "foo", + want: []string{"foo"}, + }, + { + desc: "first", + in: ":foo", + want: []string{"", "foo"}, + }, + { + desc: "last", + in: "foo:", + want: []string{"foo", ""}, + }, + { + desc: "multiple", + in: "foo:bar:baz", + want: []string{"foo", "bar", "baz"}, + }, + { + desc: "multiple with escapes", + in: `foo\:bar:baz\:qux`, + want: []string{`foo\:bar`, `baz\:qux`}, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got, want := splitEscaped(tt.in, kvSeparatorRune), tt.want; !stringSlicesEqual(got, want) { + t.Errorf("%s: got:%v, want:%v", tt.desc, got, want) + } + }) + } +} + +func TestIsNPathElement(t *testing.T) { + tests := []struct { + desc string + in string + expect bool + }{ + { + desc: "empty", + in: "", + expect: false, + }, + { + desc: "negative", + in: "[-45]", + expect: false, + }, + { + desc: "negative-1", + in: "[-1]", + expect: true, + }, + { + desc: "valid", + in: "[0]", + expect: true, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := IsNPathElement(tt.in); got != tt.expect { + t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) + } + }) + } +} + +func stringSlicesEqual(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i, aa := range a { + if aa != b[i] { + return false + } + } + return true +} + +func TestPathFromString(t *testing.T) { + tests := []struct { + desc string + in string + expect Path + }{ + { + desc: "no-path", + in: "", + expect: Path{}, + }, + { + desc: "valid-path", + in: "a.b.c", + expect: Path{"a", "b", "c"}, + }, + { + desc: "surround-periods", + in: ".a.", + expect: Path{"a"}, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := PathFromString(tt.in); !got.Equals(tt.expect) { + t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) + } + }) + } +} + +func TestToYAMLPath(t *testing.T) { + tests := []struct { + desc string + in string + expect Path + }{ + { + desc: "all-uppercase", + in: "A.B.C.D", + expect: Path{"a", "b", "c", "d"}, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := ToYAMLPath(tt.in); !got.Equals(tt.expect) { + t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) + } + }) + } +} + +func TestIsKVPathElement(t *testing.T) { + tests := []struct { + desc string + in string + expect bool + }{ + { + desc: "valid", + in: "[1:2]", + expect: true, + }, + { + desc: "invalid", + in: "[:2]", + expect: false, + }, + { + desc: "invalid-2", + in: "[1:]", + expect: false, + }, + { + desc: "empty", + in: "", + expect: false, + }, + { + desc: "no-brackets", + in: "1:2", + expect: false, + }, + { + desc: "one-bracket", + in: "[1:2", + expect: false, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := IsKVPathElement(tt.in); got != tt.expect { + t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) + } + }) + } +} + +func TestIsVPathElement(t *testing.T) { + tests := []struct { + desc string + in string + expect bool + }{ + { + desc: "valid", + in: "[:1]", + expect: true, + }, + { + desc: "kv-path-elem", + in: "[1:2]", + expect: false, + }, + { + desc: "invalid", + in: "1:2", + expect: false, + }, + { + desc: "empty", + in: "", + expect: false, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := IsVPathElement(tt.in); got != tt.expect { + t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) + } + }) + } +} + +func TestPathKV(t *testing.T) { + tests := []struct { + desc string + in string + wantK string + wantV string + wantErr error + }{ + { + desc: "valid", + in: "[1:2]", + wantK: "1", + wantV: "2", + wantErr: nil, + }, + { + desc: "invalid", + in: "[1:", + wantErr: errors.New(""), + }, + { + desc: "empty", + in: "", + wantErr: errors.New(""), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if k, v, err := PathKV(tt.in); k != tt.wantK || v != tt.wantV || errNilCheck(err, tt.wantErr) { + t.Errorf("%s: expect %v %v %v got %v %v %v", tt.desc, tt.wantK, tt.wantV, tt.wantErr, k, v, err) + } + }) + } +} + +func TestPathV(t *testing.T) { + tests := []struct { + desc string + in string + want string + err error + }{ + { + desc: "valid-kv", + in: "[1:2]", + want: "1:2", + err: nil, + }, + { + desc: "valid-v", + in: "[:1]", + want: "1", + err: nil, + }, + { + desc: "invalid", + in: "083fj", + want: "", + err: errors.New(""), + }, + { + desc: "empty", + in: "", + want: "", + err: errors.New(""), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got, err := PathV(tt.in); got != tt.want || errNilCheck(err, tt.err) { + t.Errorf("%s: expect %v %v got %v %v", tt.desc, tt.want, tt.err, got, err) + } + }) + } +} + +func TestRemoveBrackets(t *testing.T) { + tests := []struct { + desc string + in string + expect string + expectStat bool + }{ + { + desc: "has-brackets", + in: "[yo]", + expect: "yo", + expectStat: true, + }, + { + desc: "one-bracket", + in: "[yo", + expect: "", + expectStat: false, + }, + { + desc: "other-bracket", + in: "yo]", + expect: "", + expectStat: false, + }, + { + desc: "no-brackets", + in: "yo", + expect: "", + expectStat: false, + }, + { + desc: "empty", + in: "", + expect: "", + expectStat: false, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got, stat := RemoveBrackets(tt.in); got != tt.expect || stat != tt.expectStat { + t.Errorf("%s: expect %v %v got %v %v", tt.desc, tt.expect, tt.expectStat, got, stat) + } + }) + } +} + +func errNilCheck(err1, err2 error) bool { + return (err1 == nil && err2 != nil) || (err1 != nil && err2 == nil) +} diff --git a/operator/pkg/util/progress/progress.go b/operator/pkg/util/progress/progress.go new file mode 100644 index 0000000..eb81e29 --- /dev/null +++ b/operator/pkg/util/progress/progress.go @@ -0,0 +1,236 @@ +// Copyright Istio 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 progress + +import ( + "fmt" + "io" + "sort" + "strings" + "sync" + + "github.com/cheggaaa/pb/v3" + + "github.com/jehawley/istio/operator/pkg/name" +) + +type InstallState int + +const ( + StateInstalling InstallState = iota + StatePruning + StateComplete + StateUninstallComplete +) + +// Log records the progress of an installation +// This aims to provide information about the install of multiple components in parallel, while working +// around the limitations of the pb library, which will only support single lines. To do this, we aggregate +// the current components into a single line, and as components complete there final state is persisted to a new line. +type Log struct { + components map[string]*ManifestLog + bar *pb.ProgressBar + template string + mu sync.Mutex + state InstallState +} + +func NewLog() *Log { + return &Log{ + components: map[string]*ManifestLog{}, + bar: createBar(), + } +} + +const inProgress = `{{ yellow (cycle . "-" "-" "-" " ") }} ` + +// createStatus will return a string to report the current status. +// ex: - Processing resources for components. Waiting for foo, bar +func (p *Log) createStatus(maxWidth int) string { + comps := make([]string, 0, len(p.components)) + wait := make([]string, 0, len(p.components)) + for c, l := range p.components { + comps = append(comps, name.UserFacingComponentName(name.ComponentName(c))) + wait = append(wait, l.waitingResources()...) + } + sort.Strings(comps) + sort.Strings(wait) + msg := fmt.Sprintf(`Processing resources for %s.`, strings.Join(comps, ", ")) + if len(wait) > 0 { + msg += fmt.Sprintf(` Waiting for %s`, strings.Join(wait, ", ")) + } + prefix := inProgress + if !p.bar.GetBool(pb.Terminal) { + // If we aren't a terminal, no need to spam extra lines + prefix = `{{ yellow "-" }} ` + } + // reduce by 2 to allow for the "- " that will be added below + maxWidth -= 2 + if maxWidth > 0 && len(msg) > maxWidth { + return prefix + msg[:maxWidth-3] + "..." + } + // cycle will alternate between "-" and " ". "-" is given multiple times to avoid quick flashing back and forth + return prefix + msg +} + +// For testing only +var testWriter *io.Writer + +func createBar() *pb.ProgressBar { + // Don't set a total and use Static so we can explicitly control when you write. This is needed + // for handling the multiline issues. + bar := pb.New(0) + bar.Set(pb.Static, true) + if testWriter != nil { + bar.SetWriter(*testWriter) + } + bar.Start() + // if we aren't a terminal, we will return a new line for each new message + if !bar.GetBool(pb.Terminal) { + bar.Set(pb.ReturnSymbol, "\n") + } + return bar +} + +// reportProgress will report an update for a given component +// Because the bar library does not support multiple lines/bars at once, we need to aggregate current +// progress into a single line. For example "Waiting for x, y, z". Once a component completes, we want +// a new line created so the information is not lost. To do this, we spin up a new bar with the remaining components +// on a new line, and create a new bar. For example, this becomes "x succeeded", "waiting for y, z". +func (p *Log) reportProgress(component string) func() { + return func() { + cmpName := name.ComponentName(component) + cliName := name.UserFacingComponentName(cmpName) + p.mu.Lock() + defer p.mu.Unlock() + cmp := p.components[component] + // The component has completed + cmp.mu.Lock() + finished := cmp.finished + cmpErr := cmp.err + cmp.mu.Unlock() + successIcon := "✅" + if icon, found := name.IstioComponentIcons[cmpName]; found { + successIcon = icon + } + if finished || cmpErr != "" { + if finished { + p.SetMessage(fmt.Sprintf(`{{ green "✔" }} %s installed %s`, cliName, successIcon), true) + } else { + p.SetMessage(fmt.Sprintf(`{{ red "✘" }} %s encountered an error: %s`, cliName, cmpErr), true) + } + // Close the bar out, outputting a new line + delete(p.components, component) + + // Now we create a new bar, which will have the remaining components + p.bar = createBar() + return + } + p.SetMessage(p.createStatus(p.bar.Width()), false) + } +} + +func (p *Log) SetState(state InstallState) { + p.mu.Lock() + defer p.mu.Unlock() + p.state = state + switch p.state { + case StatePruning: + p.bar.SetTemplateString(inProgress + `Pruning removed resources`) + p.bar.Write() + case StateComplete: + p.bar.SetTemplateString(`{{ green "✔" }} Installation complete`) + p.bar.Write() + case StateUninstallComplete: + p.bar.SetTemplateString(`{{ green "✔" }} Uninstall complete`) + p.bar.Write() + } +} + +func (p *Log) NewComponent(component string) *ManifestLog { + ml := &ManifestLog{ + report: p.reportProgress(component), + } + p.mu.Lock() + defer p.mu.Unlock() + p.components[component] = ml + return ml +} + +func (p *Log) SetMessage(status string, finish bool) { + // if we are not a terminal and there is no change, do not write + // This avoids redundant lines + if !p.bar.GetBool(pb.Terminal) && status == p.template { + return + } + p.template = status + p.bar.SetTemplateString(p.template) + if finish { + p.bar.Finish() + } + p.bar.Write() +} + +// ManifestLog records progress for a single component +type ManifestLog struct { + report func() + err string + finished bool + waiting []string + mu sync.Mutex +} + +func (p *ManifestLog) ReportProgress() { + if p == nil { + return + } + p.report() +} + +func (p *ManifestLog) ReportError(err string) { + if p == nil { + return + } + p.mu.Lock() + p.err = err + p.mu.Unlock() + p.report() +} + +func (p *ManifestLog) ReportFinished() { + if p == nil { + return + } + p.mu.Lock() + p.finished = true + p.mu.Unlock() + p.report() +} + +func (p *ManifestLog) ReportWaiting(resources []string) { + if p == nil { + return + } + p.mu.Lock() + p.waiting = resources + p.mu.Unlock() + p.report() +} + +func (p *ManifestLog) waitingResources() []string { + p.mu.Lock() + defer p.mu.Unlock() + return p.waiting +} diff --git a/operator/pkg/util/progress/progress_test.go b/operator/pkg/util/progress/progress_test.go new file mode 100644 index 0000000..984d5c9 --- /dev/null +++ b/operator/pkg/util/progress/progress_test.go @@ -0,0 +1,76 @@ +// Copyright Istio 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 progress + +import ( + "bytes" + "io" + "testing" + + "github.com/jehawley/istio/operator/pkg/name" +) + +func TestProgressLog(t *testing.T) { + buf := bytes.NewBuffer(nil) + testBuf := io.Writer(buf) + testWriter = &testBuf + expected := "" + expect := func(e string) { + t.Helper() + // In buffer mode we don't overwrite old data, so we are constantly appending to the expected + newExpected := expected + "\n" + e + if newExpected != buf.String() { + t.Fatalf("expected '%v', \ngot '%v'", newExpected, buf.String()) + } + expected = newExpected + } + + p := NewLog() + cnp := name.PilotComponentName + cnpo := name.UserFacingComponentName(cnp) + cnb := name.IstioBaseComponentName + cnbo := name.UserFacingComponentName(cnb) + foo := p.NewComponent(string(cnp)) + foo.ReportProgress() + expect(`- Processing resources for ` + cnpo + `.`) + + bar := p.NewComponent(string(cnb)) + bar.ReportProgress() + // string buffer won't rewrite, so we append + expect(`- Processing resources for ` + cnbo + `, ` + cnpo + `.`) + bar.ReportProgress() + bar.ReportProgress() + + bar.ReportWaiting([]string{"deployment"}) + expect(`- Processing resources for ` + cnbo + `, ` + cnpo + `. Waiting for deployment`) + + bar.ReportError("some error") + expect(`✘ ` + cnbo + ` encountered an error: some error`) + + foo.ReportProgress() + expect(`- Processing resources for ` + cnpo + `.`) + + foo.ReportFinished() + expect(`✔ ` + cnpo + ` installed 🧠`) + + p.SetState(StatePruning) + expect(`- Pruning removed resources`) + + p.SetState(StateComplete) + expect(`✔ Installation complete`) + + p.SetState(StateUninstallComplete) + expect(`✔ Uninstall complete`) +} diff --git a/operator/pkg/util/reflect.go b/operator/pkg/util/reflect.go new file mode 100644 index 0000000..33bb6c5 --- /dev/null +++ b/operator/pkg/util/reflect.go @@ -0,0 +1,316 @@ +// Copyright Istio 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 util + +import ( + "fmt" + "reflect" +) + +// kindOf returns the reflection Kind that represents the dynamic type of value. +// If value is a nil interface value, kindOf returns reflect.Invalid. +func kindOf(value any) reflect.Kind { + if value == nil { + return reflect.Invalid + } + return reflect.TypeOf(value).Kind() +} + +// IsString reports whether value is a string type. +func IsString(value any) bool { + return kindOf(value) == reflect.String +} + +// IsPtr reports whether value is a ptr type. +func IsPtr(value any) bool { + return kindOf(value) == reflect.Ptr +} + +// IsMap reports whether value is a map type. +func IsMap(value any) bool { + return kindOf(value) == reflect.Map +} + +// IsMapPtr reports whether v is a map ptr type. +func IsMapPtr(v any) bool { + t := reflect.TypeOf(v) + return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Map +} + +// IsSlice reports whether value is a slice type. +func IsSlice(value any) bool { + return kindOf(value) == reflect.Slice +} + +// IsStruct reports whether value is a struct type +func IsStruct(value any) bool { + return kindOf(value) == reflect.Struct +} + +// IsSlicePtr reports whether v is a slice ptr type. +func IsSlicePtr(v any) bool { + return kindOf(v) == reflect.Ptr && reflect.TypeOf(v).Elem().Kind() == reflect.Slice +} + +// IsSliceInterfacePtr reports whether v is a slice ptr type. +func IsSliceInterfacePtr(v any) bool { + // Must use ValueOf because Elem().Elem() type resolves dynamically. + vv := reflect.ValueOf(v) + return vv.Kind() == reflect.Ptr && vv.Elem().Kind() == reflect.Interface && vv.Elem().Elem().Kind() == reflect.Slice +} + +// IsTypeStructPtr reports whether v is a struct ptr type. +func IsTypeStructPtr(t reflect.Type) bool { + if t == reflect.TypeOf(nil) { + return false + } + return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct +} + +// IsTypeSlicePtr reports whether v is a slice ptr type. +func IsTypeSlicePtr(t reflect.Type) bool { + if t == reflect.TypeOf(nil) { + return false + } + return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Slice +} + +// IsTypeMap reports whether v is a map type. +func IsTypeMap(t reflect.Type) bool { + if t == reflect.TypeOf(nil) { + return false + } + return t.Kind() == reflect.Map +} + +// IsTypeInterface reports whether v is an interface. +func IsTypeInterface(t reflect.Type) bool { + if t == reflect.TypeOf(nil) { + return false + } + return t.Kind() == reflect.Interface +} + +// IsTypeSliceOfInterface reports whether v is a slice of interface. +func IsTypeSliceOfInterface(t reflect.Type) bool { + if t == reflect.TypeOf(nil) { + return false + } + return t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Interface +} + +// IsNilOrInvalidValue reports whether v is nil or reflect.Zero. +func IsNilOrInvalidValue(v reflect.Value) bool { + return !v.IsValid() || (v.Kind() == reflect.Ptr && v.IsNil()) || IsValueNil(v.Interface()) +} + +// IsValueNil returns true if either value is nil, or has dynamic type {ptr, +// map, slice} with value nil. +func IsValueNil(value any) bool { + if value == nil { + return true + } + switch kindOf(value) { + case reflect.Slice, reflect.Ptr, reflect.Map: + return reflect.ValueOf(value).IsNil() + } + return false +} + +// IsValueNilOrDefault returns true if either IsValueNil(value) or the default +// value for the type. +func IsValueNilOrDefault(value any) bool { + if IsValueNil(value) { + return true + } + if !IsValueScalar(reflect.ValueOf(value)) { + // Default value is nil for non-scalar types. + return false + } + return value == reflect.New(reflect.TypeOf(value)).Elem().Interface() +} + +// IsValuePtr reports whether v is a ptr type. +func IsValuePtr(v reflect.Value) bool { + return v.Kind() == reflect.Ptr +} + +// IsValueInterface reports whether v is an interface type. +func IsValueInterface(v reflect.Value) bool { + return v.Kind() == reflect.Interface +} + +// IsValueStruct reports whether v is a struct type. +func IsValueStruct(v reflect.Value) bool { + return v.Kind() == reflect.Struct +} + +// IsValueStructPtr reports whether v is a struct ptr type. +func IsValueStructPtr(v reflect.Value) bool { + return v.Kind() == reflect.Ptr && IsValueStruct(v.Elem()) +} + +// IsValueMap reports whether v is a map type. +func IsValueMap(v reflect.Value) bool { + return v.Kind() == reflect.Map +} + +// IsValueSlice reports whether v is a slice type. +func IsValueSlice(v reflect.Value) bool { + return v.Kind() == reflect.Slice +} + +// IsValueScalar reports whether v is a scalar type. +func IsValueScalar(v reflect.Value) bool { + if IsNilOrInvalidValue(v) { + return false + } + if IsValuePtr(v) { + if v.IsNil() { + return false + } + v = v.Elem() + } + return !IsValueStruct(v) && !IsValueMap(v) && !IsValueSlice(v) +} + +// ValuesAreSameType returns true if v1 and v2 has the same reflect.Type, +// otherwise it returns false. +func ValuesAreSameType(v1 reflect.Value, v2 reflect.Value) bool { + return v1.Type() == v2.Type() +} + +// IsEmptyString returns true if value is an empty string. +func IsEmptyString(value any) bool { + if value == nil { + return true + } + switch kindOf(value) { + case reflect.String: + if _, ok := value.(string); ok { + return value.(string) == "" + } + } + return false +} + +// DeleteFromSlicePtr deletes an entry at index from the parent, which must be a slice ptr. +func DeleteFromSlicePtr(parentSlice any, index int) error { + scope.Debugf("DeleteFromSlicePtr index=%d, slice=\n%v", index, parentSlice) + pv := reflect.ValueOf(parentSlice) + + if !IsSliceInterfacePtr(parentSlice) { + return fmt.Errorf("deleteFromSlicePtr parent type is %T, must be *[]interface{}", parentSlice) + } + + pvv := pv.Elem() + if pvv.Kind() == reflect.Interface { + pvv = pvv.Elem() + } + + pv.Elem().Set(reflect.AppendSlice(pvv.Slice(0, index), pvv.Slice(index+1, pvv.Len()))) + + return nil +} + +// UpdateSlicePtr updates an entry at index in the parent, which must be a slice ptr, with the given value. +func UpdateSlicePtr(parentSlice any, index int, value any) error { + scope.Debugf("UpdateSlicePtr parent=\n%v\n, index=%d, value=\n%v", parentSlice, index, value) + pv := reflect.ValueOf(parentSlice) + v := reflect.ValueOf(value) + + if !IsSliceInterfacePtr(parentSlice) { + return fmt.Errorf("updateSlicePtr parent type is %T, must be *[]interface{}", parentSlice) + } + + pvv := pv.Elem() + if pvv.Kind() == reflect.Interface { + pv.Elem().Elem().Index(index).Set(v) + return nil + } + pv.Elem().Index(index).Set(v) + + return nil +} + +// InsertIntoMap inserts value with key into parent which must be a map, map ptr, or interface to map. +func InsertIntoMap(parentMap any, key any, value any) error { + scope.Debugf("InsertIntoMap key=%v, value=%v, map=\n%v", key, value, parentMap) + v := reflect.ValueOf(parentMap) + kv := reflect.ValueOf(key) + vv := reflect.ValueOf(value) + + if v.Type().Kind() == reflect.Ptr { + v = v.Elem() + } + if v.Type().Kind() == reflect.Interface { + v = v.Elem() + } + + if v.Type().Kind() != reflect.Map { + scope.Debugf("error %v", v.Type().Kind()) + return fmt.Errorf("insertIntoMap parent type is %T, must be map", parentMap) + } + + v.SetMapIndex(kv, vv) + + return nil +} + +// DeleteFromMap deletes an entry with the given key parent, which must be a map. +func DeleteFromMap(parentMap any, key any) error { + scope.Debugf("DeleteFromMap key=%s, parent:\n%v\n", key, parentMap) + pv := reflect.ValueOf(parentMap) + + if !IsMap(parentMap) { + return fmt.Errorf("deleteFromMap parent type is %T, must be map", parentMap) + } + pv.SetMapIndex(reflect.ValueOf(key), reflect.Value{}) + + return nil +} + +// ToIntValue returns 0, false if val is not a number type, otherwise it returns the int value of val. +func ToIntValue(val any) (int, bool) { + if IsValueNil(val) { + return 0, false + } + v := reflect.ValueOf(val) + switch { + case IsIntKind(v.Kind()): + return int(v.Int()), true + case IsUintKind(v.Kind()): + return int(v.Uint()), true + } + return 0, false +} + +// IsIntKind reports whether k is an integer kind of any size. +func IsIntKind(k reflect.Kind) bool { + switch k { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + } + return false +} + +// IsUintKind reports whether k is an unsigned integer kind of any size. +func IsUintKind(k reflect.Kind) bool { + switch k { + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return true + } + return false +} diff --git a/operator/pkg/util/reflect_test.go b/operator/pkg/util/reflect_test.go new file mode 100644 index 0000000..7473d7d --- /dev/null +++ b/operator/pkg/util/reflect_test.go @@ -0,0 +1,413 @@ +// Copyright Istio 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 util + +import ( + "reflect" + "testing" +) + +// TODO: add missing unit tests (istio/istio#17246). + +// errToString returns the string representation of err and the empty string if +// err is nil. +func errToString(err error) string { + if err == nil { + return "" + } + return err.Error() +} + +// to ptr conversion utility functions +func toInt8Ptr(i int8) *int8 { return &i } + +func TestIsValueNil(t *testing.T) { + if !IsValueNil(nil) { + t.Error("got IsValueNil(nil) false, want true") + } + if !IsValueNil((*int)(nil)) { + t.Error("got IsValueNil(ptr) false, want true") + } + if !IsValueNil(map[int]int(nil)) { + t.Error("got IsValueNil(map) false, want true") + } + if !IsValueNil([]int(nil)) { + t.Error("got IsValueNil(slice) false, want true") + } + if !IsValueNil(any(nil)) { + t.Error("got IsValueNil(interface) false, want true") + } + + if IsValueNil(toInt8Ptr(42)) { + t.Error("got IsValueNil(ptr) true, want false") + } + if IsValueNil(map[int]int{42: 42}) { + t.Error("got IsValueNil(map) true, want false") + } + if IsValueNil([]int{1, 2, 3}) { + t.Error("got IsValueNil(slice) true, want false") + } + if IsValueNil(any(42)) { + t.Error("got IsValueNil(interface) true, want false") + } +} + +func TestIsValueNilOrDefault(t *testing.T) { + if !IsValueNilOrDefault(nil) { + t.Error("got IsValueNilOrDefault(nil) false, want true") + } + if !IsValueNilOrDefault((*int)(nil)) { + t.Error("got IsValueNilOrDefault(ptr) false, want true") + } + if !IsValueNilOrDefault(map[int]int(nil)) { + t.Error("got IsValueNilOrDefault(map) false, want true") + } + if !IsValueNilOrDefault([]int(nil)) { + t.Error("got IsValueNilOrDefault(slice) false, want true") + } + if !IsValueNilOrDefault(any(nil)) { + t.Error("got IsValueNilOrDefault(interface) false, want true") + } + if !IsValueNilOrDefault(int(0)) { + t.Error("got IsValueNilOrDefault(int(0)) false, want true") + } + if !IsValueNilOrDefault("") { + t.Error("got IsValueNilOrDefault(\"\") false, want true") + } + if !IsValueNilOrDefault(false) { + t.Error("got IsValueNilOrDefault(false) false, want true") + } + i := 32 + ip := &i + if IsValueNilOrDefault(&ip) { + t.Error("got IsValueNilOrDefault(ptr to ptr) false, want true") + } +} + +func TestIsValueFuncs(t *testing.T) { + testInt := int(42) + testStruct := struct{}{} + testSlice := []bool{} + testMap := map[bool]bool{} + var testNilSlice []bool + var testNilMap map[bool]bool + + allValues := []any{nil, testInt, &testInt, testStruct, &testStruct, testNilSlice, testSlice, &testSlice, testNilMap, testMap, &testMap} + + tests := []struct { + desc string + function func(v reflect.Value) bool + okValues []any + }{ + { + desc: "IsValuePtr", + function: IsValuePtr, + okValues: []any{&testInt, &testStruct, &testSlice, &testMap}, + }, + { + desc: "IsValueStruct", + function: IsValueStruct, + okValues: []any{testStruct}, + }, + { + desc: "IsValueInterface", + function: IsValueInterface, + okValues: []any{}, + }, + { + desc: "IsValueStructPtr", + function: IsValueStructPtr, + okValues: []any{&testStruct}, + }, + { + desc: "IsValueMap", + function: IsValueMap, + okValues: []any{testNilMap, testMap}, + }, + { + desc: "IsValueSlice", + function: IsValueSlice, + okValues: []any{testNilSlice, testSlice}, + }, + { + desc: "IsValueScalar", + function: IsValueScalar, + okValues: []any{testInt, &testInt}, + }, + } + + for _, tt := range tests { + for vidx, v := range allValues { + if got, want := tt.function(reflect.ValueOf(v)), isInListOfInterface(tt.okValues, v); got != want { + t.Errorf("%s with %s (#%d): got: %t, want: %t", tt.desc, reflect.TypeOf(v), vidx, got, want) + } + } + } +} + +func TestValuesAreSameType(t *testing.T) { + type EnumType int64 + + tests := []struct { + inDesc string + inV1 any + inV2 any + want bool + }{ + { + inDesc: "success both are int32 types", + inV1: int32(42), + inV2: int32(43), + want: true, + }, + { + inDesc: "fail unmatching int types", + inV1: int16(42), + inV2: int32(43), + want: false, + }, + { + inDesc: "fail unmatching int and string type", + inV1: int32(42), + inV2: "42", + want: false, + }, + { + inDesc: "fail EnumType and int64 types", + inV1: EnumType(42), + inV2: int64(43), + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.inDesc, func(t *testing.T) { + got := ValuesAreSameType(reflect.ValueOf(tt.inV1), reflect.ValueOf(tt.inV2)) + if got != tt.want { + t.Errorf("got %v, want %v for comparing %T against %T", got, tt.want, tt.inV1, tt.inV2) + } + }) + } +} + +func TestIsTypeFuncs(t *testing.T) { + testInt := int(42) + testStruct := struct{}{} + testSlice := []bool{} + testSliceOfInterface := []any{} + testMap := map[bool]bool{} + var testNilSlice []bool + var testNilMap map[bool]bool + + allTypes := []any{ + nil, testInt, &testInt, testStruct, &testStruct, testNilSlice, + testSlice, &testSlice, testSliceOfInterface, testNilMap, testMap, &testMap, + } + + tests := []struct { + desc string + function func(v reflect.Type) bool + okTypes []any + }{ + { + desc: "IsTypeStructPtr", + function: IsTypeStructPtr, + okTypes: []any{&testStruct}, + }, + { + desc: "IsTypeSlicePtr", + function: IsTypeSlicePtr, + okTypes: []any{&testSlice}, + }, + { + desc: "IsTypeMap", + function: IsTypeMap, + okTypes: []any{testNilMap, testMap}, + }, + { + desc: "IsTypeInterface", + function: IsTypeInterface, + okTypes: []any{}, + }, + { + desc: "IsTypeSliceOfInterface", + function: IsTypeSliceOfInterface, + okTypes: []any{testSliceOfInterface}, + }, + } + + for _, tt := range tests { + for vidx, v := range allTypes { + if got, want := tt.function(reflect.TypeOf(v)), isInListOfInterface(tt.okTypes, v); got != want { + t.Errorf("%s with %s (#%d): got: %t, want: %t", tt.desc, reflect.TypeOf(v), vidx, got, want) + } + } + } +} + +type interfaceContainer struct { + I anInterface +} + +type anInterface interface { + IsU() +} + +type implementsInterface struct { + A string +} + +func (*implementsInterface) IsU() {} + +func TestIsValueInterface(t *testing.T) { + intf := &interfaceContainer{ + I: &implementsInterface{ + A: "a", + }, + } + iField := reflect.ValueOf(intf).Elem().FieldByName("I") + if !IsValueInterface(iField) { + t.Errorf("IsValueInterface(): got false, want true") + } +} + +func TestIsTypeInterface(t *testing.T) { + intf := &interfaceContainer{ + I: &implementsInterface{ + A: "a", + }, + } + testIfField := reflect.ValueOf(intf).Elem().Field(0) + + if !IsTypeInterface(testIfField.Type()) { + t.Errorf("IsTypeInterface(): got false, want true") + } +} + +func isInListOfInterface(lv []any, v any) bool { + for _, vv := range lv { + if reflect.DeepEqual(vv, v) { + return true + } + } + return false +} + +func TestDeleteFromSlicePtr(t *testing.T) { + parentSlice := []int{42, 43, 44, 45} + var parentSliceI any = parentSlice + if err := DeleteFromSlicePtr(&parentSliceI, 1); err != nil { + t.Fatalf("got error: %s, want error: nil", err) + } + wantSlice := []int{42, 44, 45} + if got, want := parentSliceI, wantSlice; !reflect.DeepEqual(got, want) { + t.Errorf("got:\n%v\nwant:\n%v\n", got, want) + } + + badParent := struct{}{} + wantErr := `deleteFromSlicePtr parent type is *struct {}, must be *[]interface{}` + if got, want := errToString(DeleteFromSlicePtr(&badParent, 1)), wantErr; got != want { + t.Fatalf("got error: %s, want error: %s", got, want) + } +} + +func TestUpdateSlicePtr(t *testing.T) { + parentSlice := []int{42, 43, 44, 45} + var parentSliceI any = parentSlice + if err := UpdateSlicePtr(&parentSliceI, 1, 42); err != nil { + t.Fatalf("got error: %s, want error: nil", err) + } + wantSlice := []int{42, 42, 44, 45} + if got, want := parentSliceI, wantSlice; !reflect.DeepEqual(got, want) { + t.Errorf("got:\n%v\nwant:\n%v\n", got, want) + } + + badParent := struct{}{} + wantErr := `updateSlicePtr parent type is *struct {}, must be *[]interface{}` + if got, want := errToString(UpdateSlicePtr(&badParent, 1, 42)), wantErr; got != want { + t.Fatalf("got error: %s, want error: %s", got, want) + } +} + +func TestInsertIntoMap(t *testing.T) { + parentMap := map[int]string{42: "forty two", 43: "forty three"} + key := 44 + value := "forty four" + if err := InsertIntoMap(parentMap, key, value); err != nil { + t.Fatalf("got error: %s, want error: nil", err) + } + wantMap := map[int]string{42: "forty two", 43: "forty three", 44: "forty four"} + if got, want := parentMap, wantMap; !reflect.DeepEqual(got, want) { + t.Errorf("got:\n%v\nwant:\n%v\n", got, want) + } + + badParent := struct{}{} + wantErr := `insertIntoMap parent type is *struct {}, must be map` + if got, want := errToString(InsertIntoMap(&badParent, key, value)), wantErr; got != want { + t.Fatalf("got error: %s, want error: %s", got, want) + } +} + +var ( + allIntTypes = []any{int(-42), int8(-43), int16(-44), int32(-45), int64(-46)} + allUintTypes = []any{uint(42), uint8(43), uint16(44), uint32(45), uint64(46)} + allIntegerTypes = append(allIntTypes, allUintTypes...) + nonIntTypes = []any{nil, "", []int{}, map[string]bool{}} + allTypes = append(allIntegerTypes, nonIntTypes...) +) + +func TestIsInteger(t *testing.T) { + tests := []struct { + desc string + function func(v reflect.Kind) bool + want []any + }{ + { + desc: "ints", + function: IsIntKind, + want: allIntTypes, + }, + { + desc: "uints", + function: IsUintKind, + want: allUintTypes, + }, + } + + for _, tt := range tests { + var got []any + for _, v := range allTypes { + if tt.function(reflect.ValueOf(v).Kind()) { + got = append(got, v) + } + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("%s: got %v, want %v", tt.desc, got, tt.want) + } + } +} + +func TestToIntValue(t *testing.T) { + var got []int + for _, v := range allTypes { + if i, ok := ToIntValue(v); ok { + got = append(got, i) + } + } + want := []int{-42, -43, -44, -45, -46, 42, 43, 44, 45, 46} + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v, want %v", got, want) + } +} diff --git a/operator/pkg/util/testdata/overlay-iop.yaml b/operator/pkg/util/testdata/overlay-iop.yaml new file mode 100644 index 0000000..4fa7fbc --- /dev/null +++ b/operator/pkg/util/testdata/overlay-iop.yaml @@ -0,0 +1,104 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + meshConfig: + accessLogFile: /dev/stdout + extensionProviders: + - name: otel + envoyOtelAls: + service: otel-collector.istio-system.svc.cluster.local + port: 4317 + - name: prometheus + prometheus: + - name: stackdriver + stackdriver: + - name: envoy + envoyFileAccessLog: + path: /dev/stdout + - name: envoyExtAuthzHttp + envoyExtAuthzHttp: + - name: envoyExtAuthzGrpc + envoyExtAuthzGrpc: + - name: zipkin + zipkin: + - name: lightstep + lightstep: + - name: datadog + datadog: + - name: opencensus + opencensus: + - name: skywalking + skywalking: + - name: envoyHttpAls + envoyHttpAls: + - name: envoyTcpAls + envoyTcpAls: + - name: opentelemetry + opentelemetry: + components: + egressGateways: + - name: istio-egressgateway + enabled: true + k8s: + resources: + requests: + cpu: 10m + memory: 40Mi + + ingressGateways: + - name: istio-ingressgateway + enabled: true + k8s: + resources: + requests: + cpu: 10m + memory: 40Mi + service: + ports: + ## You can add custom gateway ports in user values overrides, but it must include those ports since helm replaces. + # Note that AWS ELB will by default perform health checks on the first port + # on this list. Setting this to the health check port will ensure that health + # checks always work. https://github.com/istio/istio/issues/12503 + - port: 15021 + targetPort: 15021 + name: status-port + - port: 80 + targetPort: 8080 + name: http2 + - port: 443 + targetPort: 8443 + name: https + - port: 31400 + targetPort: 31400 + name: tcp + # This is the port where sni routing happens + - port: 15443 + targetPort: 15443 + name: tls + + pilot: + k8s: + env: + - name: PILOT_TRACE_SAMPLING + value: "100" + resources: + requests: + cpu: 10m + memory: 100Mi + + values: + global: + proxy: + resources: + requests: + cpu: 10m + memory: 40Mi + + pilot: + autoscaleEnabled: false + + gateways: + istio-egressgateway: + autoscaleEnabled: false + istio-ingressgateway: + autoscaleEnabled: false diff --git a/operator/pkg/util/testdata/yaml/input/convention_boolean.yaml b/operator/pkg/util/testdata/yaml/input/convention_boolean.yaml new file mode 100644 index 0000000..0a2040c --- /dev/null +++ b/operator/pkg/util/testdata/yaml/input/convention_boolean.yaml @@ -0,0 +1,15 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +metadata: + name: istio +spec: + profile: default + meshConfig: + defaultConfig: + proxyMetadata: + ISTIO_DUAL_STACK: false + PROXY_XDS_VIA_AGENT: false + values: + pilot: + env: + ISTIO_DUAL_STACK: false diff --git a/operator/pkg/util/testdata/yaml/input/convention_float.yaml b/operator/pkg/util/testdata/yaml/input/convention_float.yaml new file mode 100644 index 0000000..298cdd6 --- /dev/null +++ b/operator/pkg/util/testdata/yaml/input/convention_float.yaml @@ -0,0 +1,15 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +metadata: + name: istio +spec: + profile: default + meshConfig: + defaultConfig: + proxyMetadata: + PROXY_UPSTREAM_WEIGHT: 0.85 + PROXY_DOWNSTREAM_WEIGHT: 0.15 + values: + pilot: + env: + ISTIO_DUAL_STACK: false diff --git a/operator/pkg/util/testdata/yaml/input/convention_integer.yaml b/operator/pkg/util/testdata/yaml/input/convention_integer.yaml new file mode 100644 index 0000000..c54d110 --- /dev/null +++ b/operator/pkg/util/testdata/yaml/input/convention_integer.yaml @@ -0,0 +1,15 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +metadata: + name: istio +spec: + profile: default + meshConfig: + defaultConfig: + proxyMetadata: + ISTIO_MULTI_CLUSTERS: 10 + PROXY_XDS_LISTENERS: 20 + values: + pilot: + env: + ISTIO_DUAL_STACK: false diff --git a/operator/pkg/util/testdata/yaml/input/yaml_layer1.yaml b/operator/pkg/util/testdata/yaml/input/yaml_layer1.yaml new file mode 100644 index 0000000..d35f837 --- /dev/null +++ b/operator/pkg/util/testdata/yaml/input/yaml_layer1.yaml @@ -0,0 +1,19 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + components: + base: + enabled: false + pilot: + enabled: false + ingressGateways: + - namespace: istio-system + name: istio-ingressgateway + enabled: true + label: + api: default + k8s: + service: + externalTrafficPolicy: Local + serviceAnnotations: + manifest-generate: "testserviceAnnotation" diff --git a/operator/pkg/util/testdata/yaml/input/yaml_layer1_stdin.yaml b/operator/pkg/util/testdata/yaml/input/yaml_layer1_stdin.yaml new file mode 100644 index 0000000..600a3aa --- /dev/null +++ b/operator/pkg/util/testdata/yaml/input/yaml_layer1_stdin.yaml @@ -0,0 +1,15 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + components: + pilot: + enabled: false + ingressGateways: + - namespace: istio-system + name: istio-ingressgateway + enabled: true + k8s: + service: + externalTrafficPolicy: Local + serviceAnnotations: + manifest-generate: "testserviceAnnotation" diff --git a/operator/pkg/util/testdata/yaml/input/yaml_layer2.yaml b/operator/pkg/util/testdata/yaml/input/yaml_layer2.yaml new file mode 100644 index 0000000..bde7b19 --- /dev/null +++ b/operator/pkg/util/testdata/yaml/input/yaml_layer2.yaml @@ -0,0 +1,15 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + components: + pilot: + enabled: true + ingressGateways: + - namespace: istio-system + name: istio-ingressgateway + enabled: true + label: + foo: bar + k8s: + service: + externalTrafficPolicy: Test diff --git a/operator/pkg/util/testdata/yaml/input/yaml_layer3.yaml b/operator/pkg/util/testdata/yaml/input/yaml_layer3.yaml new file mode 100644 index 0000000..8cba8e9 --- /dev/null +++ b/operator/pkg/util/testdata/yaml/input/yaml_layer3.yaml @@ -0,0 +1,13 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + components: + base: + enabled: true + ingressGateways: + - namespace: istio-system + name: istio-ingressgateway + enabled: false + k8s: + service: + externalTrafficPolicy: Test diff --git a/operator/pkg/util/testdata/yaml/output/convention_boolean.yaml b/operator/pkg/util/testdata/yaml/output/convention_boolean.yaml new file mode 100644 index 0000000..f7d0001 --- /dev/null +++ b/operator/pkg/util/testdata/yaml/output/convention_boolean.yaml @@ -0,0 +1,15 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +metadata: + name: istio +spec: + profile: default + meshConfig: + defaultConfig: + proxyMetadata: + ISTIO_DUAL_STACK: "false" + PROXY_XDS_VIA_AGENT: "false" + values: + pilot: + env: + ISTIO_DUAL_STACK: false diff --git a/operator/pkg/util/testdata/yaml/output/convention_float.yaml b/operator/pkg/util/testdata/yaml/output/convention_float.yaml new file mode 100644 index 0000000..a7c3100 --- /dev/null +++ b/operator/pkg/util/testdata/yaml/output/convention_float.yaml @@ -0,0 +1,15 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +metadata: + name: istio +spec: + profile: default + meshConfig: + defaultConfig: + proxyMetadata: + PROXY_UPSTREAM_WEIGHT: "0.85" + PROXY_DOWNSTREAM_WEIGHT: "0.15" + values: + pilot: + env: + ISTIO_DUAL_STACK: false diff --git a/operator/pkg/util/testdata/yaml/output/convention_integer.yaml b/operator/pkg/util/testdata/yaml/output/convention_integer.yaml new file mode 100644 index 0000000..56650a7 --- /dev/null +++ b/operator/pkg/util/testdata/yaml/output/convention_integer.yaml @@ -0,0 +1,15 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +metadata: + name: istio +spec: + profile: default + meshConfig: + defaultConfig: + proxyMetadata: + ISTIO_MULTI_CLUSTERS: "10" + PROXY_XDS_LISTENERS: "20" + values: + pilot: + env: + ISTIO_DUAL_STACK: false diff --git a/operator/pkg/util/testdata/yaml/output/layer1.yaml b/operator/pkg/util/testdata/yaml/output/layer1.yaml new file mode 100644 index 0000000..d35f837 --- /dev/null +++ b/operator/pkg/util/testdata/yaml/output/layer1.yaml @@ -0,0 +1,19 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + components: + base: + enabled: false + pilot: + enabled: false + ingressGateways: + - namespace: istio-system + name: istio-ingressgateway + enabled: true + label: + api: default + k8s: + service: + externalTrafficPolicy: Local + serviceAnnotations: + manifest-generate: "testserviceAnnotation" diff --git a/operator/pkg/util/testdata/yaml/output/layer1_2.yaml b/operator/pkg/util/testdata/yaml/output/layer1_2.yaml new file mode 100644 index 0000000..1820a14 --- /dev/null +++ b/operator/pkg/util/testdata/yaml/output/layer1_2.yaml @@ -0,0 +1,20 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + components: + base: + enabled: false + ingressGateways: + - enabled: true + label: + api: default + foo: bar + k8s: + service: + externalTrafficPolicy: Test + serviceAnnotations: + manifest-generate: testserviceAnnotation + name: istio-ingressgateway + namespace: istio-system + pilot: + enabled: true diff --git a/operator/pkg/util/testdata/yaml/output/layer1_2_3.yaml b/operator/pkg/util/testdata/yaml/output/layer1_2_3.yaml new file mode 100644 index 0000000..b3ec6be --- /dev/null +++ b/operator/pkg/util/testdata/yaml/output/layer1_2_3.yaml @@ -0,0 +1,20 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + components: + base: + enabled: true + ingressGateways: + - enabled: false + label: + api: default + foo: bar + k8s: + service: + externalTrafficPolicy: Test + serviceAnnotations: + manifest-generate: testserviceAnnotation + name: istio-ingressgateway + namespace: istio-system + pilot: + enabled: true diff --git a/operator/pkg/util/testdata/yaml/output/layer1_stdin.yaml b/operator/pkg/util/testdata/yaml/output/layer1_stdin.yaml new file mode 100644 index 0000000..d35f837 --- /dev/null +++ b/operator/pkg/util/testdata/yaml/output/layer1_stdin.yaml @@ -0,0 +1,19 @@ +apiVersion: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + components: + base: + enabled: false + pilot: + enabled: false + ingressGateways: + - namespace: istio-system + name: istio-ingressgateway + enabled: true + label: + api: default + k8s: + service: + externalTrafficPolicy: Local + serviceAnnotations: + manifest-generate: "testserviceAnnotation" diff --git a/operator/pkg/util/util.go b/operator/pkg/util/util.go new file mode 100644 index 0000000..0ab8322 --- /dev/null +++ b/operator/pkg/util/util.go @@ -0,0 +1,159 @@ +// Copyright Istio 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 util + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "text/template" + + "google.golang.org/protobuf/types/known/structpb" +) + +type FileFilter func(fileName string) bool + +// StringBoolMapToSlice creates and returns a slice of all the map keys with true. +func StringBoolMapToSlice(m map[string]bool) []string { + s := make([]string, 0, len(m)) + for k, v := range m { + if v { + s = append(s, k) + } + } + return s +} + +// ReadFilesWithFilter reads files from path, for a directory it recursively reads files and filters the results +// for single file it directly reads the file. It returns a concatenated output of all matching files' content. +func ReadFilesWithFilter(path string, filter FileFilter) (string, error) { + fileList, err := FindFiles(path, filter) + if err != nil { + return "", err + } + var sb strings.Builder + for _, file := range fileList { + a, err := os.ReadFile(file) + if err != nil { + return "", err + } + if _, err := sb.WriteString(string(a) + "\n"); err != nil { + return "", err + } + } + return sb.String(), nil +} + +// FindFiles reads files from path, and returns the file names that match the filter. +func FindFiles(path string, filter FileFilter) ([]string, error) { + fi, err := os.Stat(path) + if err != nil { + return nil, err + } + var fileList []string + if fi.IsDir() { + err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() || !filter(path) { + return nil + } + fileList = append(fileList, path) + return nil + }) + if err != nil { + return nil, err + } + } else { + fileList = append(fileList, path) + } + return fileList, nil +} + +// ParseValue parses string into a value +func ParseValue(valueStr string) any { + var value any + if v, err := strconv.Atoi(valueStr); err == nil { + value = v + } else if v, err := strconv.ParseFloat(valueStr, 64); err == nil { + value = v + } else if v, err := strconv.ParseBool(valueStr); err == nil { + value = v + } else { + value = strings.ReplaceAll(valueStr, "\\,", ",") + } + return value +} + +// ConsolidateLog is a helper function to dedup the log message. +func ConsolidateLog(logMessage string) string { + logCountMap := make(map[string]int) + stderrSlice := strings.Split(logMessage, "\n") + for _, item := range stderrSlice { + if item == "" { + continue + } + _, exist := logCountMap[item] + if exist { + logCountMap[item]++ + } else { + logCountMap[item] = 1 + } + } + var sb strings.Builder + for _, item := range stderrSlice { + if logCountMap[item] == 0 { + continue + } + sb.WriteString(fmt.Sprintf("%s (repeated %v times)\n", item, logCountMap[item])) + // reset seen log count + logCountMap[item] = 0 + } + return sb.String() +} + +// RenderTemplate is a helper method to render a template with the given values. +func RenderTemplate(tmpl string, ts any) (string, error) { + t, err := template.New("").Parse(tmpl) + if err != nil { + return "", err + } + buf := new(bytes.Buffer) + err = t.Execute(buf, ts) + if err != nil { + return "", err + } + return buf.String(), nil +} + +func ValueString(v *structpb.Value) string { + switch x := v.Kind.(type) { + case *structpb.Value_StringValue: + return x.StringValue + case *structpb.Value_NumberValue: + return fmt.Sprint(x.NumberValue) + default: + return v.String() + } +} + +func MustStruct(m map[string]any) *structpb.Struct { + s, _ := structpb.NewStruct(m) + return s +} diff --git a/operator/pkg/util/util_test.go b/operator/pkg/util/util_test.go new file mode 100644 index 0000000..7fa1486 --- /dev/null +++ b/operator/pkg/util/util_test.go @@ -0,0 +1,252 @@ +// Copyright Istio 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 util + +import ( + "errors" + "testing" +) + +func TestParseValue(t *testing.T) { + tests := []struct { + desc string + in string + want any + }{ + { + desc: "empty", + in: "", + want: "", + }, + { + desc: "true", + in: "true", + want: true, + }, + { + desc: "false", + in: "false", + want: false, + }, + { + desc: "numeric-one", + in: "1", + want: 1, + }, + { + desc: "numeric-zero", + in: "0", + want: 0, + }, + { + desc: "numeric-large", + in: "12345678", + want: 12345678, + }, + { + desc: "numeric-negative", + in: "-12345678", + want: -12345678, + }, + { + desc: "float", + in: "1.23456", + want: 1.23456, + }, + { + desc: "float-zero", + in: "0.00", + want: 0.00, + }, + { + desc: "float-negative", + in: "-6.54321", + want: -6.54321, + }, + { + desc: "string", + in: "foobar", + want: "foobar", + }, + { + desc: "string-number-prefix", + in: "123foobar", + want: "123foobar", + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got, want := ParseValue(tt.in), tt.want; !(got == want) { + t.Errorf("%s: got:%v, want:%v", tt.desc, got, want) + } + }) + } +} + +func TestConsolidateLog(t *testing.T) { + tests := []struct { + desc string + in string + want string + }{ + { + desc: "empty", + in: "", + want: "", + }, + { + desc: "2 errors once", + in: "err1\nerr2\n", + want: "err1 (repeated 1 times)\nerr2 (repeated 1 times)\n", + }, + { + desc: "3 errors multiple times", + in: "err1\nerr2\nerr3\nerr1\nerr2\nerr3\nerr3\nerr3\n", + want: "err1 (repeated 2 times)\nerr2 (repeated 2 times)\nerr3 (repeated 4 times)\n", + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got, want := ConsolidateLog(tt.in), tt.want; !(got == want) { + t.Errorf("%s: got:%s, want:%s", tt.desc, got, want) + } + }) + } +} + +func TestStringBoolMapToSlice(t *testing.T) { + tests := []struct { + desc string + in map[string]bool + want []string + }{ + { + desc: "empty", + in: make(map[string]bool), + want: make([]string, 0), + }, + { + desc: "", + in: map[string]bool{ + "yo": true, + "yolo": false, + "test1": true, + "water bottle": false, + "baseball hat": true, + }, + want: []string{ + "yo", + "test1", + "baseball hat", + }, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got, want := StringBoolMapToSlice(tt.in), tt.want; !(sameStringSlice(got, want)) { + t.Errorf("%s: got:%s, want: %s", tt.desc, got, want) + } + }) + } +} + +// Helper function to check if values in 2 slices are the same, +// no correspondence to order +func sameStringSlice(x, y []string) bool { + if len(x) != len(y) { + return false + } + diff := make(map[string]int, len(x)) + for _, _x := range x { + diff[_x]++ + } + for _, _y := range y { + if _, ok := diff[_y]; !ok { + return false + } + diff[_y]-- + if diff[_y] == 0 { + delete(diff, _y) + } + } + return len(diff) == 0 +} + +func TestRenderTemplate(t *testing.T) { + type tmplValue struct { + Name string + Proxy string + } + tests := []struct { + desc string + template string + in tmplValue + want string + err error + }{ + { + desc: "valid-template", + template: "{{.Name}} uses {{.Proxy}} as sidecar", + in: tmplValue{ + Name: "istio", + Proxy: "envoy", + }, + want: "istio uses envoy as sidecar", + err: nil, + }, + { + desc: "empty-template", + template: "", + in: tmplValue{ + Name: "istio", + Proxy: "envoy", + }, + want: "", + err: nil, + }, + { + desc: "template with no template strings", + template: "this template is without handlebar expressions", + in: tmplValue{ + Name: "istio", + Proxy: "envoy", + }, + want: "this template is without handlebar expressions", + err: nil, + }, + { + desc: "template with missing variable", + template: "{{ .Name }} has replaced its control plane with {{ .Istiod }} component", + in: tmplValue{ + Name: "istio", + Proxy: "envoy", + }, + want: "", + err: errors.New(""), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + got, err := RenderTemplate(tt.template, tt.in) + if got != tt.want { + t.Errorf("%s: got :%v, wanted output: %v", tt.desc, got, tt.want) + } + + if (err == nil && tt.err != nil) || (err != nil && tt.err == nil) { + t.Errorf("%s: got error :%v, wanted error: %v", tt.desc, err, tt.err) + } + }) + } +} diff --git a/operator/pkg/util/yaml.go b/operator/pkg/util/yaml.go new file mode 100644 index 0000000..b117d62 --- /dev/null +++ b/operator/pkg/util/yaml.go @@ -0,0 +1,322 @@ +// Copyright Istio 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 util + +import ( + "bufio" + "bytes" + "fmt" + "io" + "reflect" + "strings" + + jsonpatch "github.com/evanphx/json-patch/v5" // nolint: staticcheck + "github.com/kylelemons/godebug/diff" + "google.golang.org/protobuf/proto" + yaml3 "k8s.io/apimachinery/pkg/util/yaml" + "sigs.k8s.io/yaml" + + "istio.io/istio/pkg/util/protomarshal" +) + +func ToYAMLGeneric(root any) ([]byte, error) { + var vs []byte + if proto, ok := root.(proto.Message); ok { + v, err := protomarshal.ToYAML(proto) + if err != nil { + return nil, err + } + vs = []byte(v) + } else { + v, err := yaml.Marshal(root) + if err != nil { + return nil, err + } + vs = v + } + return vs, nil +} + +func MustToYAMLGeneric(root any) string { + var vs []byte + if proto, ok := root.(proto.Message); ok { + v, err := protomarshal.ToYAML(proto) + if err != nil { + return err.Error() + } + vs = []byte(v) + } else { + v, err := yaml.Marshal(root) + if err != nil { + return err.Error() + } + vs = v + } + return string(vs) +} + +// ToYAML returns a YAML string representation of val, or the error string if an error occurs. +func ToYAML(val any) string { + y, err := yaml.Marshal(val) + if err != nil { + return err.Error() + } + return string(y) +} + +// ToYAMLWithJSONPB returns a YAML string representation of val (using jsonpb), or the error string if an error occurs. +func ToYAMLWithJSONPB(val proto.Message) string { + v := reflect.ValueOf(val) + if val == nil || (v.Kind() == reflect.Ptr && v.IsNil()) { + return "null" + } + js, err := protomarshal.ToJSONWithOptions(val, "", true) + if err != nil { + return err.Error() + } + yb, err := yaml.JSONToYAML([]byte(js)) + if err != nil { + return err.Error() + } + return string(yb) +} + +// MarshalWithJSONPB returns a YAML string representation of val (using jsonpb). +func MarshalWithJSONPB(val proto.Message) (string, error) { + return protomarshal.ToYAML(val) +} + +// UnmarshalWithJSONPB unmarshals y into out using gogo jsonpb (required for many proto defined structs). +func UnmarshalWithJSONPB(y string, out proto.Message, allowUnknownField bool) error { + // Treat nothing as nothing. If we called jsonpb.Unmarshaler it would return the same. + if y == "" { + return nil + } + jb, err := yaml.YAMLToJSON([]byte(y)) + if err != nil { + return err + } + + if allowUnknownField { + err = protomarshal.UnmarshalAllowUnknown(jb, out) + } else { + err = protomarshal.Unmarshal(jb, out) + } + if err != nil { + return err + } + return nil +} + +// OverlayTrees performs a sequential JSON strategic of overlays over base. +func OverlayTrees(base map[string]any, overlays ...map[string]any) (map[string]any, error) { + needsOverlay := false + for _, o := range overlays { + if len(o) > 0 { + needsOverlay = true + break + } + } + if !needsOverlay { + // Avoid expensive overlay if possible + return base, nil + } + bby, err := yaml.Marshal(base) + if err != nil { + return nil, err + } + by := string(bby) + + for _, o := range overlays { + oy, err := yaml.Marshal(o) + if err != nil { + return nil, err + } + + by, err = OverlayYAML(by, string(oy)) + if err != nil { + return nil, err + } + } + + out := make(map[string]any) + err = yaml.Unmarshal([]byte(by), &out) + if err != nil { + return nil, err + } + return out, nil +} + +// OverlayYAML patches the overlay tree over the base tree and returns the result. All trees are expressed as YAML +// strings. +func OverlayYAML(base, overlay string) (string, error) { + if strings.TrimSpace(base) == "" { + return overlay, nil + } + if strings.TrimSpace(overlay) == "" { + return base, nil + } + bj, err := yaml.YAMLToJSON([]byte(base)) + if err != nil { + return "", fmt.Errorf("yamlToJSON error in base: %s\n%s", err, bj) + } + oj, err := yaml.YAMLToJSON([]byte(overlay)) + if err != nil { + return "", fmt.Errorf("yamlToJSON error in overlay: %s\n%s", err, oj) + } + if base == "" { + bj = []byte("{}") + } + if overlay == "" { + oj = []byte("{}") + } + + merged, err := jsonpatch.MergePatch(bj, oj) + if err != nil { + return "", fmt.Errorf("json merge error (%s) for base object: \n%s\n override object: \n%s", err, bj, oj) + } + my, err := yaml.JSONToYAML(merged) + if err != nil { + return "", fmt.Errorf("jsonToYAML error (%s) for merged object: \n%s", err, merged) + } + + return string(my), nil +} + +// yamlDiff compares single YAML file +func yamlDiff(a, b string) string { + ao, bo := make(map[string]any), make(map[string]any) + if err := yaml.Unmarshal([]byte(a), &ao); err != nil { + return err.Error() + } + if err := yaml.Unmarshal([]byte(b), &bo); err != nil { + return err.Error() + } + + ay, err := yaml.Marshal(ao) + if err != nil { + return err.Error() + } + by, err := yaml.Marshal(bo) + if err != nil { + return err.Error() + } + + return diff.Diff(string(ay), string(by)) +} + +// yamlStringsToList yaml string parse to string list +func yamlStringsToList(str string) []string { + reader := bufio.NewReader(strings.NewReader(str)) + decoder := yaml3.NewYAMLReader(reader) + res := make([]string, 0) + for { + doc, err := decoder.Read() + if err == io.EOF { + break + } + if err != nil { + break + } + + chunk := bytes.TrimSpace(doc) + res = append(res, string(chunk)) + } + return res +} + +// multiYamlDiffOutput multi yaml diff output format +func multiYamlDiffOutput(res, diff string) string { + if res == "" { + return diff + } + if diff == "" { + return res + } + + return res + "\n" + diff +} + +func diffStringList(l1, l2 []string) string { + var maxLen int + var minLen int + var l1Max bool + res := "" + if len(l1)-len(l2) > 0 { + maxLen = len(l1) + minLen = len(l2) + l1Max = true + } else { + maxLen = len(l2) + minLen = len(l1) + l1Max = false + } + + for i := 0; i < maxLen; i++ { + d := "" + if i >= minLen { + if l1Max { + d = yamlDiff(l1[i], "") + } else { + d = yamlDiff("", l2[i]) + } + } else { + d = yamlDiff(l1[i], l2[i]) + } + res = multiYamlDiffOutput(res, d) + } + return res +} + +// YAMLDiff compares multiple YAML files and single YAML file +func YAMLDiff(a, b string) string { + al := yamlStringsToList(a) + bl := yamlStringsToList(b) + res := diffStringList(al, bl) + + return res +} + +// IsYAMLEqual reports whether the YAML in strings a and b are equal. +func IsYAMLEqual(a, b string) bool { + if strings.TrimSpace(a) == "" && strings.TrimSpace(b) == "" { + return true + } + ajb, err := yaml.YAMLToJSON([]byte(a)) + if err != nil { + scope.Debugf("bad YAML in isYAMLEqual:\n%s", a) + return false + } + bjb, err := yaml.YAMLToJSON([]byte(b)) + if err != nil { + scope.Debugf("bad YAML in isYAMLEqual:\n%s", b) + return false + } + + return bytes.Equal(ajb, bjb) +} + +// IsYAMLEmpty reports whether the YAML string y is logically empty. +func IsYAMLEmpty(y string) bool { + var yc []string + for _, l := range strings.Split(y, "\n") { + yt := strings.TrimSpace(l) + if !strings.HasPrefix(yt, "#") && !strings.HasPrefix(yt, "---") { + yc = append(yc, l) + } + } + res := strings.TrimSpace(strings.Join(yc, "\n")) + return res == "{}" || res == "" +} diff --git a/operator/pkg/util/yaml_test.go b/operator/pkg/util/yaml_test.go new file mode 100644 index 0000000..10a03d7 --- /dev/null +++ b/operator/pkg/util/yaml_test.go @@ -0,0 +1,362 @@ +// Copyright Istio 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 util + +import ( + "reflect" + "testing" +) + +func TestToYAML(t *testing.T) { + tests := []struct { + desc string + inVals any + expectedOut string + }{ + { + desc: "valid-yaml", + inVals: map[string]any{ + "foo": "bar", + "yo": map[string]any{ + "istio": "bar", + }, + }, + expectedOut: `foo: bar +yo: + istio: bar +`, + }, + { + desc: "alphabetical", + inVals: map[string]any{ + "foo": "yaml", + "abc": "f", + }, + expectedOut: `abc: f +foo: yaml +`, + }, + { + desc: "expected-err-nil", + inVals: nil, + expectedOut: "null\n", + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := ToYAML(tt.inVals); got != tt.expectedOut { + t.Errorf("%s: expected out %v got %s", tt.desc, tt.expectedOut, got) + } + }) + } +} + +func TestOverlayTrees(t *testing.T) { + tests := []struct { + desc string + inBase map[string]any + inOverlays map[string]any + expectedOverlay map[string]any + expectedErr error + }{ + { + desc: "overlay-valid", + inBase: map[string]any{ + "foo": "bar", + "baz": "naz", + }, + inOverlays: map[string]any{ + "foo": "laz", + }, + expectedOverlay: map[string]any{ + "baz": "naz", + "foo": "laz", + }, + expectedErr: nil, + }, + { + desc: "overlay-key-does-not-exist", + inBase: map[string]any{ + "foo": "bar", + "baz": "naz", + }, + inOverlays: map[string]any{ + "i-dont-exist": "i-really-dont-exist", + }, + expectedOverlay: map[string]any{ + "baz": "naz", + "foo": "bar", + "i-dont-exist": "i-really-dont-exist", + }, + expectedErr: nil, + }, + { + desc: "remove-key-val", + inBase: map[string]any{ + "foo": "bar", + }, + inOverlays: map[string]any{ + "foo": nil, + }, + expectedOverlay: map[string]any{}, + expectedErr: nil, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if gotOverlays, err := OverlayTrees(tt.inBase, tt.inOverlays); !reflect.DeepEqual(gotOverlays, tt.expectedOverlay) || + ((err != nil && tt.expectedErr == nil) || (err == nil && tt.expectedErr != nil)) { + t.Errorf("%s: expected overlay & err %v %v got %v %v", tt.desc, tt.expectedOverlay, tt.expectedErr, + gotOverlays, err) + } + }) + } +} + +func TestOverlayYAML(t *testing.T) { + tests := []struct { + desc string + base string + overlay string + expect string + err error + }{ + { + desc: "overlay-yaml", + base: `foo: bar +yo: lo +`, + overlay: `yo: go`, + expect: `foo: bar +yo: go +`, + err: nil, + }, + { + desc: "combine-yaml", + base: `foo: bar`, + overlay: `baz: razmatazz`, + expect: `baz: razmatazz +foo: bar +`, + err: nil, + }, + { + desc: "blank", + base: `R#)*J#FN`, + overlay: `FM#)M#F(*#M`, + expect: "FM#)M#F(*#M\n", + err: nil, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got, err := OverlayYAML(tt.base, tt.overlay); got != tt.expect || ((tt.err != nil && err == nil) || (tt.err == nil && err != nil)) { + t.Errorf("%s: expected overlay&err %v %v got %v %v", tt.desc, tt.expect, tt.err, got, err) + } + }) + } +} + +func TestYAMLDiff(t *testing.T) { + tests := []struct { + desc string + diff1 string + diff2 string + expect string + }{ + { + desc: "1-line-diff", + diff1: `hola: yo +foo: bar +goo: tar +`, + diff2: `hola: yo +foo: bar +notgoo: nottar +`, + expect: ` foo: bar +-goo: tar + hola: yo ++notgoo: nottar + `, + }, + { + desc: "no-diff", + diff1: `foo: bar`, + diff2: `foo: bar`, + expect: ``, + }, + { + desc: "invalid-yaml", + diff1: `Ij#**#f#`, + diff2: `fm*##)n`, + expect: "error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}", + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := YAMLDiff(tt.diff1, tt.diff2); got != tt.expect { + t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) + } + }) + } +} + +func TestMultipleYAMLDiff(t *testing.T) { + tests := []struct { + desc string + diff1 string + diff2 string + expect string + }{ + { + desc: "1-line-diff", + diff1: `hola: yo +foo: bar +goo: tar +--- +hola: yo1 +foo: bar1 +goo: tar1 +`, + diff2: `hola: yo +foo: bar +notgoo: nottar +`, + expect: ` foo: bar +-goo: tar + hola: yo ++notgoo: nottar + +-foo: bar1 +-goo: tar1 +-hola: yo1 ++{} + `, + }, + { + desc: "no-diff", + diff1: `foo: bar +--- +foo: bar1 +`, + diff2: `foo: bar +--- +foo: bar1 +`, + expect: ``, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := YAMLDiff(tt.diff1, tt.diff2); got != tt.expect { + t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) + } + }) + } +} + +func TestIsYAMLEqual(t *testing.T) { + tests := []struct { + desc string + in1 string + in2 string + expect bool + }{ + { + desc: "yaml-equal", + in1: `foo: bar`, + in2: `foo: bar`, + expect: true, + }, + { + desc: "bad-yaml-1", + in1: "O#JF*()#", + in2: `foo: bar`, + expect: false, + }, + { + desc: "bad-yaml-2", + in1: `foo: bar`, + in2: "#OHJ*#()F", + expect: false, + }, + { + desc: "yaml-not-equal", + in1: `zinc: iron +stoichiometry: avagadro +`, + in2: `i-swear: i-am +definitely-not: in1 +`, + expect: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := IsYAMLEqual(tt.in1, tt.in2); got != tt.expect { + t.Errorf("%v: got %v want %v", tt.desc, got, tt.expect) + } + }) + } +} + +func TestIsYAMLEmpty(t *testing.T) { + tests := []struct { + desc string + in string + expect bool + }{ + { + desc: "completely-empty", + in: "", + expect: true, + }, + { + desc: "comment-logically-empty", + in: `# this is a comment +# this is another comment that serves no purpose +# (like all comments usually do) +`, + expect: true, + }, + { + desc: "start-yaml", + in: `--- I dont mean anything`, + expect: true, + }, + { + desc: "combine-comments-and-yaml", + in: `#this is another comment +foo: bar +# ^ that serves purpose +`, + expect: false, + }, + { + desc: "yaml-not-empty", + in: `foo: bar`, + expect: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if got := IsYAMLEmpty(tt.in); got != tt.expect { + t.Errorf("%v: expect %v got %v", tt.desc, tt.expect, got) + } + }) + } +} diff --git a/operator/pkg/validate/common.go b/operator/pkg/validate/common.go new file mode 100644 index 0000000..a18f6fd --- /dev/null +++ b/operator/pkg/validate/common.go @@ -0,0 +1,345 @@ +// Copyright Istio 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 validate + +import ( + "fmt" + "net/netip" + "reflect" + "regexp" + "strconv" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/yaml" + + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/util" + "istio.io/istio/pkg/log" +) + +var ( + scope = log.RegisterScope("validation", "API validation") + + // alphaNumericRegexp defines the alpha numeric atom, typically a + // component of names. This only allows lower case characters and digits. + alphaNumericRegexp = match(`[a-z0-9]+`) + + // separatorRegexp defines the separators allowed to be embedded in name + // components. This allow one period, one or two underscore and multiple + // dashes. + separatorRegexp = match(`(?:[._]|__|[-]*)`) + + // nameComponentRegexp restricts registry path component names to start + // with at least one letter or number, with following parts able to be + // separated by one period, one or two underscore and multiple dashes. + nameComponentRegexp = expression( + alphaNumericRegexp, + optional(repeated(separatorRegexp, alphaNumericRegexp))) + + // domainComponentRegexp restricts the registry domain component of a + // repository name to start with a component as defined by DomainRegexp + // and followed by an optional port. + domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`) + + // DomainRegexp defines the structure of potential domain components + // that may be part of image names. This is purposely a subset of what is + // allowed by DNS to ensure backwards compatibility with Docker image + // names. + DomainRegexp = expression( + domainComponentRegexp, + optional(repeated(literal(`.`), domainComponentRegexp)), + optional(literal(`:`), match(`[0-9]+`))) + + // TagRegexp matches valid tag names. From docker/docker:graph/tags.go. + TagRegexp = match(`[\w][\w.-]{0,127}`) + + // DigestRegexp matches valid digests. + DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`) + + // NameRegexp is the format for the name component of references. The + // regexp has capturing groups for the domain and name part omitting + // the separating forward slash from either. + NameRegexp = expression( + optional(DomainRegexp, literal(`/`)), + nameComponentRegexp, + optional(repeated(literal(`/`), nameComponentRegexp))) + + // ReferenceRegexp is the full supported format of a reference. The regexp + // is anchored and has capturing groups for name, tag, and digest + // components. + ReferenceRegexp = anchored(capture(NameRegexp), + optional(literal(":"), capture(TagRegexp)), + optional(literal("@"), capture(DigestRegexp))) + + // ObjectNameRegexp is a legal name for a k8s object. + ObjectNameRegexp = match(`[a-z0-9.-]{1,254}`) +) + +// validateWithRegex checks whether the given value matches the regexp r. +func validateWithRegex(path util.Path, val any, r *regexp.Regexp) (errs util.Errors) { + valStr := fmt.Sprint(val) + if len(r.FindString(valStr)) != len(valStr) { + errs = util.AppendErr(errs, fmt.Errorf("invalid value %s: %v", path, val)) + printError(errs.ToError()) + } + return errs +} + +// validateStringList returns a validator function that works on a string list, using the supplied ValidatorFunc vf on +// each element. +func validateStringList(vf ValidatorFunc) ValidatorFunc { + return func(path util.Path, val any) util.Errors { + msg := fmt.Sprintf("validateStringList %v", val) + if !util.IsString(val) { + err := fmt.Errorf("validateStringList %s got %T, want string", path, val) + printError(err) + return util.NewErrs(err) + } + var errs util.Errors + for _, s := range strings.Split(val.(string), ",") { + errs = util.AppendErrs(errs, vf(path, s)) + scope.Debugf("\nerrors(%d): %v", len(errs), errs) + msg += fmt.Sprintf("\nerrors(%d): %v", len(errs), errs) + } + logWithError(errs.ToError(), msg) + return errs + } +} + +// validatePortNumberString checks if val is a string with a valid port number. +func validatePortNumberString(path util.Path, val any) util.Errors { + scope.Debugf("validatePortNumberString %v:", val) + if !util.IsString(val) { + return util.NewErrs(fmt.Errorf("validatePortNumberString(%s) bad type %T, want string", path, val)) + } + if val.(string) == "*" || val.(string) == "" { + return nil + } + intV, err := strconv.ParseInt(val.(string), 10, 32) + if err != nil { + return util.NewErrs(fmt.Errorf("%s : %s", path, err)) + } + return validatePortNumber(path, intV) +} + +// validatePortNumber checks whether val is an integer representing a valid port number. +func validatePortNumber(path util.Path, val any) util.Errors { + return validateIntRange(path, val, 0, 65535) +} + +// validateIPRangesOrStar validates IP ranges and also allow star, examples: "1.1.0.256/16,2.2.0.257/16", "*" +func validateIPRangesOrStar(path util.Path, val any) (errs util.Errors) { + scope.Debugf("validateIPRangesOrStar at %v: %v", path, val) + + if !util.IsString(val) { + err := fmt.Errorf("validateIPRangesOrStar %s got %T, want string", path, val) + printError(err) + return util.NewErrs(err) + } + + if val.(string) == "*" || val.(string) == "" { + return errs + } + + return validateStringList(validateCIDR)(path, val) +} + +// validateIntRange checks whether val is an integer in [min, max]. +func validateIntRange(path util.Path, val any, minimum, maximum int64) util.Errors { + k := reflect.TypeOf(val).Kind() + var err error + switch { + case util.IsIntKind(k): + v := reflect.ValueOf(val).Int() + if v < minimum || v > maximum { + err = fmt.Errorf("value %s:%v falls outside range [%v, %v]", path, v, minimum, maximum) + } + case util.IsUintKind(k): + v := reflect.ValueOf(val).Uint() + if int64(v) < minimum || int64(v) > maximum { + err = fmt.Errorf("value %s:%v falls out side range [%v, %v]", path, v, minimum, maximum) + } + default: + err = fmt.Errorf("validateIntRange %s unexpected type %T, want int type", path, val) + } + logWithError(err, "validateIntRange %s:%v in [%d, %d]?: ", path, val, minimum, maximum) + return util.NewErrs(err) +} + +// validateCIDR checks whether val is a string with a valid CIDR. +func validateCIDR(path util.Path, val any) util.Errors { + var err error + if !util.IsString(val) { + err = fmt.Errorf("validateCIDR %s got %T, want string", path, val) + } else { + if _, err = netip.ParsePrefix(val.(string)); err != nil { + err = fmt.Errorf("%s %s", path, err) + } + } + logWithError(err, "validateCIDR (%s): ", val) + return util.NewErrs(err) +} + +func printError(err error) { + if err == nil { + scope.Debug("OK") + return + } + scope.Debugf("%v", err) +} + +// logWithError prints debug log with err message +func logWithError(err error, format string, args ...any) { + msg := fmt.Sprintf(format, args...) + if err == nil { + msg += ": OK\n" + } else { + msg += fmt.Sprintf(": %v\n", err) + } + scope.Debug(msg) +} + +// match compiles the string to a regular expression. +var match = regexp.MustCompile + +// literal compiles s into a literal regular expression, escaping any regexp +// reserved characters. +func literal(s string) *regexp.Regexp { + re := match(regexp.QuoteMeta(s)) + + if _, complete := re.LiteralPrefix(); !complete { + panic("must be a literal") + } + + return re +} + +// expression defines a full expression, where each regular expression must +// follow the previous. +func expression(res ...*regexp.Regexp) *regexp.Regexp { + var s string + for _, re := range res { + s += re.String() + } + + return match(s) +} + +// optional wraps the expression in a non-capturing group and makes the +// production optional. +func optional(res ...*regexp.Regexp) *regexp.Regexp { + return match(group(expression(res...)).String() + `?`) +} + +// repeated wraps the regexp in a non-capturing group to get one or more +// matches. +func repeated(res ...*regexp.Regexp) *regexp.Regexp { + return match(group(expression(res...)).String() + `+`) +} + +// group wraps the regexp in a non-capturing group. +func group(res ...*regexp.Regexp) *regexp.Regexp { + return match(`(?:` + expression(res...).String() + `)`) +} + +// capture wraps the expression in a capturing group. +func capture(res ...*regexp.Regexp) *regexp.Regexp { + return match(`(` + expression(res...).String() + `)`) +} + +// anchored anchors the regular expression by adding start and end delimiters. +func anchored(res ...*regexp.Regexp) *regexp.Regexp { + return match(`^` + expression(res...).String() + `$`) +} + +// ValidatorFunc validates a value. +type ValidatorFunc func(path util.Path, i any) util.Errors + +// UnmarshalIOP unmarshals a string containing IstioOperator as YAML. +func UnmarshalIOP(iopYAML string) (*v1alpha1.IstioOperator, error) { + // Remove creationDate (util.UnmarshalWithJSONPB fails if present) + mapIOP := make(map[string]any) + if err := yaml.Unmarshal([]byte(iopYAML), &mapIOP); err != nil { + return nil, err + } + // Don't bother trying to remove the timestamp if there are no fields. + // This also preserves iopYAML if it is ""; we don't want iopYAML to be the string "null" + if len(mapIOP) > 0 { + un := &unstructured.Unstructured{Object: mapIOP} + un.SetCreationTimestamp(metav1.Time{}) // UnmarshalIstioOperator chokes on these + iopYAML = util.ToYAML(un) + } + iop := &v1alpha1.IstioOperator{} + + if err := yaml.UnmarshalStrict([]byte(iopYAML), iop); err != nil { + return nil, fmt.Errorf("%s:\n\nYAML:\n%s", err, iopYAML) + } + return iop, nil +} + +// ValidIOP validates the given IstioOperator object. +func ValidIOP(iop *v1alpha1.IstioOperator) error { + errs := CheckIstioOperatorSpec(iop.Spec, false) + return errs.ToError() +} + +// compose path for slice s with index i +func indexPathForSlice(s string, i int) string { + return fmt.Sprintf("%s[%d]", s, i) +} + +// get validation function for specified path +func getValidationFuncForPath(validations map[string]ValidatorFunc, path util.Path) (ValidatorFunc, bool) { + pstr := path.String() + // fast match + if !strings.Contains(pstr, "[") && !strings.Contains(pstr, "]") { + vf, ok := validations[pstr] + return vf, ok + } + for p, vf := range validations { + ps := strings.Split(p, ".") + if len(ps) != len(path) { + continue + } + for i, v := range ps { + if !matchPathNode(v, path[i]) { + break + } + if i == len(ps)-1 { + return vf, true + } + } + } + return nil, false +} + +// check whether the pn path node match pattern. +// pattern may contain '*', e.g. [1] match [*]. +func matchPathNode(pattern, pn string) bool { + if !strings.Contains(pattern, "[") && !strings.Contains(pattern, "]") { + return pattern == pn + } + if !strings.Contains(pn, "[") && !strings.Contains(pn, "]") { + return false + } + indexPattern := pattern[strings.IndexByte(pattern, '[')+1 : strings.IndexByte(pattern, ']')] + if indexPattern == "*" { + return true + } + index := pn[strings.IndexByte(pn, '[')+1 : strings.IndexByte(pn, ']')] + return indexPattern == index +} diff --git a/operator/pkg/validate/validate.go b/operator/pkg/validate/validate.go new file mode 100644 index 0000000..5d5fac7 --- /dev/null +++ b/operator/pkg/validate/validate.go @@ -0,0 +1,233 @@ +// Copyright Istio 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 validate + +import ( + "errors" + "fmt" + "reflect" + + "google.golang.org/protobuf/types/known/structpb" + + "istio.io/api/123/operator/v1alpha1" + operator_v1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/metrics" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" + "istio.io/istio/pkg/config/labels" + "istio.io/istio/pkg/config/mesh" + "istio.io/istio/pkg/util/protomarshal" +) + +var ( + // DefaultValidations maps a data path to a validation function. + DefaultValidations = map[string]ValidatorFunc{ + "Values": func(path util.Path, i any) util.Errors { + return CheckValues(i) + }, + "MeshConfig": validateMeshConfig, + "Hub": validateHub, + "Tag": validateTag, + "Revision": validateRevision, + "Components.IngressGateways": validateGatewayName, + "Components.EgressGateways": validateGatewayName, + } + // requiredValues lists all the values that must be non-empty. + requiredValues = map[string]bool{} +) + +// CheckIstioOperator validates the operator CR. +func CheckIstioOperator(iop *operator_v1alpha1.IstioOperator, checkRequiredFields bool) error { + if iop == nil { + return nil + } + + errs := CheckIstioOperatorSpec(iop.Spec, checkRequiredFields) + return errs.ToError() +} + +// CheckIstioOperatorSpec validates the values in the given Installer spec, using the field map DefaultValidations to +// call the appropriate validation function. checkRequiredFields determines whether missing mandatory fields generate +// errors. +func CheckIstioOperatorSpec(is *v1alpha1.IstioOperatorSpec, checkRequiredFields bool) (errs util.Errors) { + if is == nil { + return util.Errors{} + } + + return Validate2(DefaultValidations, is) +} + +func Validate2(validations map[string]ValidatorFunc, iop *v1alpha1.IstioOperatorSpec) (errs util.Errors) { + for path, validator := range validations { + v, f, _ := tpath.GetFromStructPath(iop, path) + if f { + errs = append(errs, validator(util.PathFromString(path), v)...) + } + } + return +} + +// Validate function below is used by third party for integrations and has to be public + +// Validate validates the values of the tree using the supplied Func. +func Validate(validations map[string]ValidatorFunc, structPtr any, path util.Path, checkRequired bool) (errs util.Errors) { + scope.Debugf("validate with path %s, %v (%T)", path, structPtr, structPtr) + if structPtr == nil { + return nil + } + if util.IsStruct(structPtr) { + scope.Debugf("validate path %s, skipping struct type %T", path, structPtr) + return nil + } + if !util.IsPtr(structPtr) { + metrics.CRValidationErrorTotal.Increment() + return util.NewErrs(fmt.Errorf("validate path %s, value: %v, expected ptr, got %T", path, structPtr, structPtr)) + } + structElems := reflect.ValueOf(structPtr).Elem() + if !util.IsStruct(structElems) { + metrics.CRValidationErrorTotal.Increment() + return util.NewErrs(fmt.Errorf("validate path %s, value: %v, expected struct, got %T", path, structElems, structElems)) + } + + if util.IsNilOrInvalidValue(structElems) { + return + } + + for i := 0; i < structElems.NumField(); i++ { + fieldName := structElems.Type().Field(i).Name + fieldValue := structElems.Field(i) + if !fieldValue.CanInterface() { + continue + } + kind := structElems.Type().Field(i).Type.Kind() + if a, ok := structElems.Type().Field(i).Tag.Lookup("json"); ok && a == "-" { + continue + } + + scope.Debugf("Checking field %s", fieldName) + switch kind { + case reflect.Struct: + errs = util.AppendErrs(errs, Validate(validations, fieldValue.Addr().Interface(), append(path, fieldName), checkRequired)) + case reflect.Map: + newPath := append(path, fieldName) + errs = util.AppendErrs(errs, validateLeaf(validations, newPath, fieldValue.Interface(), checkRequired)) + for _, key := range fieldValue.MapKeys() { + nnp := append(newPath, key.String()) + errs = util.AppendErrs(errs, validateLeaf(validations, nnp, fieldValue.MapIndex(key), checkRequired)) + } + case reflect.Slice: + for i := 0; i < fieldValue.Len(); i++ { + newValue := fieldValue.Index(i).Interface() + newPath := append(path, indexPathForSlice(fieldName, i)) + if util.IsStruct(newValue) || util.IsPtr(newValue) { + errs = util.AppendErrs(errs, Validate(validations, newValue, newPath, checkRequired)) + } else { + errs = util.AppendErrs(errs, validateLeaf(validations, newPath, newValue, checkRequired)) + } + } + case reflect.Ptr: + if util.IsNilOrInvalidValue(fieldValue.Elem()) { + continue + } + newPath := append(path, fieldName) + if fieldValue.Elem().Kind() == reflect.Struct { + errs = util.AppendErrs(errs, Validate(validations, fieldValue.Interface(), newPath, checkRequired)) + } else { + errs = util.AppendErrs(errs, validateLeaf(validations, newPath, fieldValue, checkRequired)) + } + default: + if structElems.Field(i).CanInterface() { + errs = util.AppendErrs(errs, validateLeaf(validations, append(path, fieldName), fieldValue.Interface(), checkRequired)) + } + } + } + if len(errs) > 0 { + metrics.CRValidationErrorTotal.Increment() + } + return errs +} + +func validateLeaf(validations map[string]ValidatorFunc, path util.Path, val any, checkRequired bool) util.Errors { + pstr := path.String() + msg := fmt.Sprintf("validate %s:%v(%T) ", pstr, val, val) + if util.IsValueNil(val) || util.IsEmptyString(val) { + if checkRequired && requiredValues[pstr] { + return util.NewErrs(fmt.Errorf("field %s is required but not set", util.ToYAMLPathString(pstr))) + } + msg += fmt.Sprintf("validate %s: OK (empty value)", pstr) + scope.Debug(msg) + return nil + } + + vf, ok := getValidationFuncForPath(validations, path) + if !ok { + msg += fmt.Sprintf("validate %s: OK (no validation)", pstr) + scope.Debug(msg) + // No validation defined. + return nil + } + scope.Debug(msg) + return vf(path, val) +} + +func validateMeshConfig(path util.Path, root any) util.Errors { + vs, err := util.ToYAMLGeneric(root) + if err != nil { + return util.Errors{err} + } + // ApplyMeshConfigDefaults allows unknown fields, so we first check for unknown fields + if err := protomarshal.ApplyYAMLStrict(string(vs), mesh.DefaultMeshConfig()); err != nil { + return util.Errors{fmt.Errorf("failed to unmarshall mesh config: %v", err)} + } + // This method will also perform validation automatically + if _, validErr := mesh.ApplyMeshConfigDefaults(string(vs)); validErr != nil { + return util.Errors{validErr} + } + return nil +} + +func validateHub(path util.Path, val any) util.Errors { + if val == "" { + return nil + } + return validateWithRegex(path, val, ReferenceRegexp) +} + +func validateTag(path util.Path, val any) util.Errors { + return validateWithRegex(path, val.(*structpb.Value).GetStringValue(), TagRegexp) +} + +func validateRevision(_ util.Path, val any) util.Errors { + if val == "" { + return nil + } + if !labels.IsDNS1123Label(val.(string)) { + err := fmt.Errorf("invalid revision specified: %s", val.(string)) + return util.Errors{err} + } + return nil +} + +func validateGatewayName(path util.Path, val any) (errs util.Errors) { + v := val.([]*v1alpha1.GatewaySpec) + for _, n := range v { + if n == nil { + errs = append(errs, util.NewErrs(errors.New("badly formatted gateway configuration"))) + } else { + errs = append(errs, validateWithRegex(path, n.Name, ObjectNameRegexp)...) + } + } + return +} diff --git a/operator/pkg/validate/validate_test.go b/operator/pkg/validate/validate_test.go new file mode 100644 index 0000000..a6cc0a0 --- /dev/null +++ b/operator/pkg/validate/validate_test.go @@ -0,0 +1,202 @@ +// Copyright Istio 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 validate + +import ( + "testing" + + "istio.io/api/123/operator/v1alpha1" + "github.com/jehawley/istio/operator/pkg/util" +) + +func TestValidate(t *testing.T) { + tests := []struct { + desc string + yamlStr string + wantErrs util.Errors + }{ + { + desc: "nil success", + }, + { + desc: "complicated k8s overlay", + yamlStr: ` +profile: default +components: + ingressGateways: + - enabled: true + k8s: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: zone + operator: In + values: + - istio`, + }, + { + desc: "CommonConfig", + yamlStr: ` +hub: docker.io/istio +tag: v1.2.3 +meshConfig: + rootNamespace: istio-system +values: + global: + proxy: + includeIPRanges: "1.1.0.0/16,2.2.0.0/16" + excludeIPRanges: "3.3.0.0/16,4.4.0.0/16" + +`, + }, + { + desc: "BadTag", + yamlStr: ` +hub: ?illegal-tag! +`, + wantErrs: makeErrors([]string{`invalid value Hub: ?illegal-tag!`}), + }, + { + desc: "BadHub", + yamlStr: ` +hub: docker.io:tag/istio +`, + wantErrs: makeErrors([]string{`invalid value Hub: docker.io:tag/istio`}), + }, + { + desc: "GoodURL", + yamlStr: ` +installPackagePath: /local/file/path +`, + }, + { + desc: "BadGatewayName", + yamlStr: ` +components: + ingressGateways: + - namespace: istio-ingress-ns2 + name: istio@ingress-1 + enabled: true +`, + wantErrs: makeErrors([]string{`invalid value Components.IngressGateways: istio@ingress-1`}), + }, + { + desc: "BadValuesIP", + yamlStr: ` +values: + global: + proxy: + includeIPRanges: "1.1.0.300/16,2.2.0.0/16" +`, + wantErrs: makeErrors([]string{`global.proxy.includeIPRanges netip.ParsePrefix("1.1.0.300/16"): ParseAddr("1.1.0.300"): IPv4 field has value >255`}), + }, + { + desc: "EmptyValuesIP", + yamlStr: ` +values: + global: + proxy: + includeIPRanges: "" +`, + }, + { + desc: "Bad mesh config", + yamlStr: ` +meshConfig: + defaultConfig: + discoveryAddress: missingport +`, + wantErrs: makeErrors([]string{`1 error occurred: + * invalid discovery address: unable to split "missingport": address missingport: missing port in address + +`}), + }, + { + desc: "Bad mesh config values", + yamlStr: ` +values: + meshConfig: + defaultConfig: + discoveryAddress: missingport +`, + wantErrs: makeErrors([]string{`1 error occurred: + * invalid discovery address: unable to split "missingport": address missingport: missing port in address + +`}), + }, + { + desc: "Unknown mesh config", + yamlStr: ` +meshConfig: + foo: bar +`, + wantErrs: makeErrors([]string{`failed to unmarshall mesh config: unknown field "foo" in istio.mesh.v1alpha1.MeshConfig`}), + }, + { + desc: "Unknown mesh config values", + yamlStr: ` +values: + meshConfig: + foo: bar +`, + wantErrs: makeErrors([]string{`failed to unmarshall mesh config: unknown field "foo" in istio.mesh.v1alpha1.MeshConfig`}), + }, + { + desc: "Good mesh config", + yamlStr: ` +meshConfig: + defaultConfig: + discoveryAddress: istiod:15012 +`, + }, + { + desc: "BadValuesIP with Space start", + yamlStr: ` +values: + global: + proxy: + includeIPRanges: "1.1.0.0/16, 2.2.0.0/16" +`, + wantErrs: makeErrors([]string{`global.proxy.includeIPRanges netip.ParsePrefix(" 2.2.0.0/16"): ParseAddr(" 2.2.0.0"): unexpected character (at " 2.2.0.0")`}), + }, + { + desc: "BadValuesIP with Space end", + yamlStr: ` +values: + global: + proxy: + includeIPRanges: "1.1.0.0/16 ,2.2.0.0/16" +`, + wantErrs: makeErrors([]string{`global.proxy.includeIPRanges netip.ParsePrefix("1.1.0.0/16 "): bad bits after slash: "16 "`}), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + ispec := &v1alpha1.IstioOperatorSpec{} + err := util.UnmarshalWithJSONPB(tt.yamlStr, ispec, false) + if err != nil { + t.Fatalf("unmarshalWithJSONPB(%s): got error %s", tt.desc, err) + } + + errs := CheckIstioOperatorSpec(ispec, false) + if gotErrs, wantErrs := errs, tt.wantErrs; !util.EqualErrors(gotErrs, wantErrs) { + t.Errorf("ProtoToValues(%s)(%v): gotErrs:%s, wantErrs:%s", tt.desc, tt.yamlStr, gotErrs, wantErrs) + } + }) + } +} diff --git a/operator/pkg/validate/validate_values.go b/operator/pkg/validate/validate_values.go new file mode 100644 index 0000000..28d411f --- /dev/null +++ b/operator/pkg/validate/validate_values.go @@ -0,0 +1,71 @@ +// Copyright Istio 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 validate + +import ( + "reflect" + + "google.golang.org/protobuf/types/known/structpb" + + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/util" +) + +// DefaultValuesValidations maps a data path to a validation function. +var DefaultValuesValidations = map[string]ValidatorFunc{ + "global.proxy.includeIPRanges": validateIPRangesOrStar, + "global.proxy.excludeIPRanges": validateIPRangesOrStar, + "global.proxy.includeInboundPorts": validateStringList(validatePortNumberString), + "global.proxy.excludeInboundPorts": validateStringList(validatePortNumberString), + "meshConfig": validateMeshConfig, +} + +// CheckValues validates the values in the given tree, which follows the Istio values.yaml schema. +func CheckValues(root any) util.Errors { + v := reflect.ValueOf(root) + if root == nil || (v.Kind() == reflect.Ptr && v.IsNil()) { + return nil + } + vs, err := util.ToYAMLGeneric(root) + if err != nil { + return util.Errors{err} + } + val := &v1alpha1.Values{} + if err := util.UnmarshalWithJSONPB(string(vs), val, false); err != nil { + return util.Errors{err} + } + return ValuesValidate(DefaultValuesValidations, root.(*structpb.Struct).AsMap(), nil) +} + +// ValuesValidate validates the values of the tree using the supplied Func +func ValuesValidate(validations map[string]ValidatorFunc, node any, path util.Path) (errs util.Errors) { + pstr := path.String() + scope.Debugf("ValuesValidate %s", pstr) + vf := validations[pstr] + if vf != nil { + errs = util.AppendErrs(errs, vf(path, node)) + } + + nn, ok := node.(map[string]any) + if !ok { + // Leaf, nothing more to recurse. + return errs + } + for k, v := range nn { + errs = util.AppendErrs(errs, ValuesValidate(validations, v, append(path, k))) + } + + return errs +} diff --git a/operator/pkg/validate/validate_values_test.go b/operator/pkg/validate/validate_values_test.go new file mode 100644 index 0000000..d8d369f --- /dev/null +++ b/operator/pkg/validate/validate_values_test.go @@ -0,0 +1,246 @@ +// Copyright Istio 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 validate + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "sigs.k8s.io/yaml" + + "github.com/jehawley/istio/operator/pkg/helm" + "github.com/jehawley/istio/operator/pkg/object" + "github.com/jehawley/istio/operator/pkg/util" + "istio.io/istio/pkg/test/env" +) + +var repoRootDir string + +func init() { + repoRootDir = env.IstioSrc +} + +func TestValidateValues(t *testing.T) { + tests := []struct { + desc string + yamlStr string + wantErrs util.Errors + }{ + { + desc: "nil success", + }, + { + desc: "StarIPRange", + yamlStr: ` +global: + proxy: + includeIPRanges: "*" + excludeIPRanges: "*" +`, + }, + { + desc: "ProxyConfig", + yamlStr: ` +global: + podDNSSearchNamespaces: + - "my-namespace" + proxy: + includeIPRanges: "1.1.0.0/16,2.2.0.0/16" + excludeIPRanges: "3.3.0.0/16,4.4.0.0/16" + excludeInboundPorts: "333,444" + clusterDomain: "my.domain" + lifecycle: + preStop: + exec: + command: ["/bin/sh", "-c", "sleep 30"] +`, + }, + { + desc: "CNIConfig", + yamlStr: ` +cni: + cniBinDir: "/var/lib/cni/bin" + cniConfDir: "/var/run/multus/cni/net.d" +`, + }, + + { + desc: "BadIPRange", + yamlStr: ` +global: + proxy: + includeIPRanges: "1.1.0.256/16,2.2.0.257/16" + excludeIPRanges: "3.3.0.0/33,4.4.0.0/34" +`, + wantErrs: makeErrors([]string{ + `global.proxy.excludeIPRanges netip.ParsePrefix("3.3.0.0/33"): prefix length out of range`, + `global.proxy.excludeIPRanges netip.ParsePrefix("4.4.0.0/34"): prefix length out of range`, + `global.proxy.includeIPRanges netip.ParsePrefix("1.1.0.256/16"): ParseAddr("1.1.0.256"): IPv4 field has value >255`, + `global.proxy.includeIPRanges netip.ParsePrefix("2.2.0.257/16"): ParseAddr("2.2.0.257"): IPv4 field has value >255`, + }), + }, + { + desc: "BadIPMalformed", + yamlStr: ` +global: + proxy: + includeIPRanges: "1.2.3/16,1.2.3.x/16" +`, + wantErrs: makeErrors([]string{ + `global.proxy.includeIPRanges netip.ParsePrefix("1.2.3/16"): ParseAddr("1.2.3"): IPv4 address too short`, + `global.proxy.includeIPRanges netip.ParsePrefix("1.2.3.x/16"): ParseAddr("1.2.3.x"): unexpected character (at "x")`, + }), + }, + { + desc: "BadIPWithStar", + yamlStr: ` +global: + proxy: + includeIPRanges: "*,1.1.0.0/16,2.2.0.0/16" +`, + wantErrs: makeErrors([]string{`global.proxy.includeIPRanges netip.ParsePrefix("*"): no '/'`}), + }, + { + desc: "BadPortRange", + yamlStr: ` +global: + proxy: + excludeInboundPorts: "-1,444" +`, + wantErrs: makeErrors([]string{`value global.proxy.excludeInboundPorts:-1 falls outside range [0, 65535]`}), + }, + { + desc: "BadPortMalformed", + yamlStr: ` +global: + proxy: + excludeInboundPorts: "111,222x" +`, + wantErrs: makeErrors([]string{`global.proxy.excludeInboundPorts : strconv.ParseInt: parsing "222x": invalid syntax`}), + }, + { + desc: "unknown field", + yamlStr: ` +global: + proxy: + foo: "bar" +`, + wantErrs: makeErrors([]string{`unknown field "foo" in v1alpha1.ProxyConfig`}), + }, + { + desc: "unknown cni field", + yamlStr: ` +cni: + foo: "bar" +`, + wantErrs: makeErrors([]string{`unknown field "foo" in v1alpha1.CNIConfig`}), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + root := make(map[string]any) + err := yaml.Unmarshal([]byte(tt.yamlStr), &root) + if err != nil { + t.Fatalf("yaml.Unmarshal(%s): got error %s", tt.desc, err) + } + errs := CheckValues(util.MustStruct(root)) + if gotErr, wantErr := errs, tt.wantErrs; !util.EqualErrors(gotErr, wantErr) { + t.Errorf("CheckValues(%s)(%v): gotErr:%s, wantErr:%s", tt.desc, tt.yamlStr, gotErr, wantErr) + } + }) + } +} + +func TestValidateValuesFromProfile(t *testing.T) { + tests := []struct { + profile string + wantErrs util.Errors + }{ + { + profile: "default", + }, + { + profile: "demo", + }, + { + profile: "minimal", + }, + { + profile: "ambient", + }, + } + for _, tt := range tests { + t.Run(tt.profile, func(t *testing.T) { + pf, err := helm.ReadProfileYAML(tt.profile, filepath.Join(env.IstioSrc, "manifests")) + if err != nil { + t.Fatalf("fail to read profile: %s", tt.profile) + } + val, _, err := object.ParseK8SYAMLToIstioOperator(pf) + if err != nil { + t.Fatalf(" fail to parse profile to ISCP: (%s), got error %s", tt.profile, err) + } + errs := CheckValues(val.Spec.Values) + if gotErr, wantErr := errs, tt.wantErrs; !util.EqualErrors(gotErr, wantErr) { + t.Errorf("CheckValues of (%v): gotErr:%s, wantErr:%s", tt.profile, gotErr, wantErr) + } + }) + } +} + +func TestValidateValuesFromValuesYAMLs(t *testing.T) { + valuesYAML := "" + var allFiles []string + manifestDir := filepath.Join(repoRootDir, "manifests/charts") + for _, sd := range []string{"base", "gateways", "istio-cni", "istio-control"} { + dir := filepath.Join(manifestDir, sd) + files, err := util.FindFiles(dir, yamlFileFilter) + if err != nil { + t.Fatalf(err.Error()) + } + allFiles = append(allFiles, files...) + } + for _, f := range allFiles { + b, err := os.ReadFile(f) + if err != nil { + t.Fatal(err.Error()) + } + valuesYAML, err = util.OverlayYAML(valuesYAML, string(b)) + if err != nil { + t.Fatal(err.Error()) + } + valuesTree := make(map[string]any) + if err := yaml.Unmarshal([]byte(valuesYAML), &valuesTree); err != nil { + t.Fatal(err.Error()) + } + if err := CheckValues(util.MustStruct(valuesTree["defaults"].(map[string]any))); err != nil { + t.Fatalf("file %s failed validation with: %s", f, err) + } + } +} + +func makeErrors(estr []string) util.Errors { + var errs util.Errors + for _, s := range estr { + errs = util.AppendErr(errs, fmt.Errorf("%s", s)) + } + return errs +} + +func yamlFileFilter(path string) bool { + return filepath.Base(path) == "values.yaml" +} diff --git a/operator/pkg/version/version.go b/operator/pkg/version/version.go new file mode 100644 index 0000000..6a600e6 --- /dev/null +++ b/operator/pkg/version/version.go @@ -0,0 +1,183 @@ +// Copyright Istio 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 version + +import ( + "fmt" + "strconv" + "strings" + + goversion "github.com/hashicorp/go-version" + "gopkg.in/yaml.v2" +) + +const ( + // releasePrefix is the prefix we used in http://gcr.io/istio-release for releases + releasePrefix = "release-" +) + +// NewVersionFromString creates a new Version from the provided SemVer formatted string and returns a pointer to it. +func NewVersionFromString(s string) (*Version, error) { + ver, err := goversion.NewVersion(s) + if err != nil { + return nil, err + } + + newVer := &Version{} + vv := ver.Segments() + if len(vv) > 0 { + newVer.Major = uint32(vv[0]) + } + if len(vv) > 1 { + newVer.Minor = uint32(vv[1]) + } + if len(vv) > 2 { + newVer.Patch = uint32(vv[2]) + } + + sv := strings.Split(s, "-") + if len(sv) > 0 { + newVer.Suffix = strings.Join(sv[1:], "-") + } + + return newVer, nil +} + +// IsVersionString checks whether the given string is a version string +func IsVersionString(path string) bool { + _, err := goversion.NewSemver(path) + if err != nil { + return false + } + vs := Version{} + return yaml.Unmarshal([]byte(path), &vs) == nil +} + +// TagToVersionString converts an istio container tag into a version string +func TagToVersionString(path string) (string, error) { + path = strings.TrimPrefix(path, releasePrefix) + ver, err := goversion.NewSemver(path) + if err != nil { + return "", err + } + segments := ver.Segments() + fmtParts := make([]string, len(segments)) + for i, s := range segments { + str := strconv.Itoa(s) + fmtParts[i] = str + } + return strings.Join(fmtParts, "."), nil +} + +// TagToVersionStringGrace converts an Istio container tag into a version string, +// if any error, fallback to use the original tag. +func TagToVersionStringGrace(path string) string { + v, err := TagToVersionString(path) + if err != nil { + return path + } + return v +} + +// MajorVersion represents a major version. +type MajorVersion struct { + Major uint32 +} + +// MinorVersion represents a minor version. +type MinorVersion struct { + MajorVersion + Minor uint32 +} + +// PatchVersion represents a patch version. +type PatchVersion struct { + MinorVersion + Patch uint32 +} + +// Version represents a version with an optional suffix. +type Version struct { + PatchVersion + Suffix string +} + +// NewMajorVersion creates an initialized MajorVersion struct. +func NewMajorVersion(major uint32) MajorVersion { + return MajorVersion{ + Major: major, + } +} + +// NewMinorVersion creates an initialized MinorVersion struct. +func NewMinorVersion(major, minor uint32) MinorVersion { + return MinorVersion{ + MajorVersion: NewMajorVersion(major), + Minor: minor, + } +} + +// NewPatchVersion creates an initialized PatchVersion struct. +func NewPatchVersion(major, minor, patch uint32) PatchVersion { + return PatchVersion{ + MinorVersion: NewMinorVersion(major, minor), + Patch: patch, + } +} + +// NewVersion creates an initialized Version struct. +func NewVersion(major, minor, patch uint32, suffix string) Version { + return Version{ + PatchVersion: NewPatchVersion(major, minor, patch), + Suffix: suffix, + } +} + +// String implements the Stringer interface. +func (v MajorVersion) String() string { + return fmt.Sprintf("%d", v.Major) +} + +// String implements the Stringer interface. +func (v MinorVersion) String() string { + return fmt.Sprintf("%s.%d", v.MajorVersion, v.Minor) +} + +// String implements the Stringer interface. +func (v PatchVersion) String() string { + return fmt.Sprintf("%s.%d", v.MinorVersion, v.Patch) +} + +// String implements the Stringer interface. +func (v *Version) String() string { + if v.Suffix == "" { + return v.PatchVersion.String() + } + return fmt.Sprintf("%s-%s", v.PatchVersion, v.Suffix) +} + +// UnmarshalYAML implements the Unmarshaler interface. +func (v *Version) UnmarshalYAML(unmarshal func(any) error) error { + s := "" + if err := unmarshal(&s); err != nil { + return err + } + out, err := NewVersionFromString(s) + if err != nil { + return err + } + *v = *out + return nil +} diff --git a/operator/pkg/version/version_test.go b/operator/pkg/version/version_test.go new file mode 100644 index 0000000..072b45c --- /dev/null +++ b/operator/pkg/version/version_test.go @@ -0,0 +1,317 @@ +// Copyright Istio 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 version + +import ( + "fmt" + "testing" + + "gopkg.in/yaml.v2" + + "istio.io/istio/pkg/test/util/assert" +) + +func TestVersion(t *testing.T) { + tests := []struct { + desc string + yamlStr string + want Version + wantErr string + }{ + { + desc: "nil success", + }, + { + desc: "major success", + yamlStr: "1", + want: NewVersion(1, 0, 0, ""), + }, + { + desc: "major fail", + yamlStr: "1..", + wantErr: `Malformed version: 1..`, + }, + { + desc: "major fail prefix", + yamlStr: ".1", + wantErr: `Malformed version: .1`, + }, + { + desc: "minor success", + yamlStr: "1.2", + want: NewVersion(1, 2, 0, ""), + }, + { + desc: "minor fail", + yamlStr: "1.1..", + wantErr: `Malformed version: 1.1..`, + }, + { + desc: "patch success", + yamlStr: "1.2.3", + want: NewVersion(1, 2, 3, ""), + }, + { + desc: "patch fail", + yamlStr: "1.1.-1", + wantErr: `Malformed version: 1.1.-1`, + }, + { + desc: "suffix success", + yamlStr: "1.2.3-istio-test", + want: NewVersion(1, 2, 3, "istio-test"), + }, + { + desc: "suffix fail", + yamlStr: ".1.1.1-something", + wantErr: `Malformed version: .1.1.1-something`, + }, + { + desc: "Malformed version fail", + yamlStr: "istio-testing-distroless", + wantErr: `Malformed version: istio-testing-distroless`, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + got := Version{} + err := yaml.Unmarshal([]byte(tt.yamlStr), &got) + if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { + t.Fatalf("yaml.Unmarshal(%s): got error: %s, want error: %s", tt.desc, gotErr, wantErr) + } + if tt.wantErr == "" { + assert.Equal(t, got, tt.want) + } + }) + } +} + +// errToString returns the string representation of err and the empty string if +// err is nil. +func errToString(err error) string { + if err == nil { + return "" + } + return err.Error() +} + +func TestIsVersionString(t *testing.T) { + tests := []struct { + name string + ver string + want bool + }{ + { + name: "empty", + ver: "", + want: false, + }, + { + name: "unknown", + ver: "unknown", + want: false, + }, + { + name: "release branch dev", + ver: "1.4-dev", + want: true, + }, + { + name: "release", + ver: "1.4.5", + want: true, + }, + { + name: "incorrect", + ver: "1.4.xxx", + want: false, + }, + { + name: "dev sha", + ver: "a3703b76cf4745f3d56bf653ed751509be116351", + want: false, + }, + { + name: "dev sha digit prefix", + ver: "60023b76cf4745f3d56bf653ed751509be116351", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsVersionString(tt.ver); got != tt.want { + t.Errorf("IsVersionString() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTagToVersionString(t *testing.T) { + //type args struct { + // path string + //} + tests := []struct { + name string + // args args + want string + wantErr bool + }{ + { + name: "1.4.3", + want: "1.4.3", + wantErr: false, + }, + { + name: "1.4.3-distroless", + want: "1.4.3", + wantErr: false, + }, + { + name: "1.5.0-alpha.0", + want: "1.5.0", + wantErr: false, + }, + { + name: "1.5.0-alpha.0-distroless", + want: "1.5.0", + wantErr: false, + }, + { + name: "1.2.10", + want: "1.2.10", + wantErr: false, + }, + { + name: "1.4.0-beta.5", + want: "1.4.0", + wantErr: false, + }, + { + name: "1.3.0-rc.3", + want: "1.3.0", + wantErr: false, + }, + { + name: "1.3.0-rc.3-distroless", + want: "1.3.0", + wantErr: false, + }, + { + name: "1.5-dev", + want: "1.5.0", + wantErr: false, + }, + { + name: "1.5-dev-distroless", + want: "1.5.0", + wantErr: false, + }, + { + name: "1.5-alpha.f850909d7ac95501bbb2ae91f57df218bcf7c630", + want: "1.5.0", + wantErr: false, + }, + { + name: "1.5-alpha.f850909d7ac95501bbb2ae91f57df218bcf7c630-distroless", + want: "1.5.0", + wantErr: false, + }, + { + name: "release-1.3-20200108-10-15", + want: "1.3.0", + wantErr: false, + }, + { + name: "release-1.3-latest-daily", + want: "1.3.0", + wantErr: false, + }, + { + name: "release-1.3-20200108-10-15-distroless", + want: "1.3.0", + wantErr: false, + }, + { + name: "release-1.3-latest-daily-distroless", + want: "1.3.0", + wantErr: false, + }, + { + name: "latest", + want: "", + wantErr: true, + }, + { + name: "latest-distroless", + want: "", + wantErr: true, + }, + { + name: "999450fd4add69e26ba04d001b811863cba8175b", + want: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := TagToVersionString(tt.name) + if (err != nil) != tt.wantErr { + t.Errorf("TagToVersionString() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("TagToVersionString() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestVersionString(t *testing.T) { + tests := map[string]struct { + version Version + want string + }{ + "with suffix": { + version: NewVersion(1, 2, 3, "xyz"), + want: "1.2.3-xyz", + }, + "without suffix": { + version: NewVersion(1, 5, 0, ""), + want: "1.5.0", + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + got := tt.version.String() + if got != tt.want { + t.Errorf("Version.String(): got: %s, want: %s", got, tt.want) + } + }) + } +} + +func TestUnmarshalYAML(t *testing.T) { + v := &Version{} + expectedErr := fmt.Errorf("test error") + errReturn := func(any) error { return expectedErr } + gotErr := v.UnmarshalYAML(errReturn) + if gotErr == nil { + t.Errorf("expected error but got nil") + } + if gotErr != expectedErr { + t.Errorf("error mismatch") + } +} diff --git a/operator/version/version.go b/operator/version/version.go new file mode 100644 index 0000000..62a1ce8 --- /dev/null +++ b/operator/version/version.go @@ -0,0 +1,56 @@ +// Copyright Istio 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 version + +import ( + "time" + + pkgversion "github.com/jehawley/istio/operator/pkg/version" + buildversion "istio.io/istio/pkg/version" +) + +const ( + // OperatorCodeBaseVersion is the version string from the code base. + OperatorCodeBaseVersion = "1.23.0" + OperatorEOLYear = 2025 + OperatorEOLMonth = time.April +) + +var ( + // OperatorVersionString is the version string of this operator binary. + OperatorVersionString string + // OperatorBinaryVersion is the Istio operator version. + OperatorBinaryVersion pkgversion.Version +) + +func init() { + var err error + OperatorVersionString = OperatorCodeBaseVersion + // If dockerinfo has a tag (e.g., specified by LDFlags), we will use it as the version of operator + tag := buildversion.DockerInfo.Tag + if pkgversion.IsVersionString(tag) { + OperatorVersionString = pkgversion.TagToVersionStringGrace(tag) + } + v, err := pkgversion.NewVersionFromString(OperatorVersionString) + if err != nil { + panic(err) + } + OperatorBinaryVersion = *v +} + +func IsEOL() bool { + t := time.Now() + return t.Year() > OperatorEOLYear || (t.Year() == OperatorEOLYear && t.Month() >= OperatorEOLMonth) +} From fa6091a21a824ba34509bcf96f84bec6c1627b97 Mon Sep 17 00:00:00 2001 From: John Hawley Date: Fri, 8 Nov 2024 11:41:09 -0500 Subject: [PATCH 2/4] update module name --- go.mod | 2 +- .../pkg/apis/addtoscheme_istio_v1alpha1.go | 2 +- operator/pkg/apis/istio/common.go | 6 ++--- .../pkg/apis/istio/v1alpha1/deepcopy_test.go | 4 ++-- .../istio/v1alpha1/validation/validation.go | 6 ++--- .../v1alpha1/validation/validation_test.go | 12 +++++----- .../apis/istio/v1alpha1/values_types.proto | 2 +- operator/pkg/component/component.go | 12 +++++----- operator/pkg/controlplane/control_plane.go | 10 ++++---- .../pkg/controlplane/control_plane_test.go | 8 +++---- operator/pkg/helm/helm.go | 2 +- operator/pkg/helm/renderer.go | 2 +- operator/pkg/manifest/shared.go | 24 +++++++++---------- operator/pkg/manifest/shared_test.go | 2 +- operator/pkg/metrics/resource_counts.go | 2 +- operator/pkg/metrics/utils.go | 2 +- operator/pkg/name/name.go | 6 ++--- operator/pkg/name/name_test.go | 4 ++-- operator/pkg/object/objects.go | 10 ++++---- operator/pkg/object/objects_test.go | 2 +- operator/pkg/patch/patch.go | 10 ++++---- operator/pkg/patch/patch_test.go | 2 +- operator/pkg/tpath/struct.go | 2 +- operator/pkg/tpath/struct_test.go | 2 +- operator/pkg/tpath/tree.go | 2 +- operator/pkg/tpath/tree_test.go | 2 +- operator/pkg/tpath/util.go | 2 +- operator/pkg/translate/translate.go | 16 ++++++------- operator/pkg/translate/translate_common.go | 6 ++--- .../pkg/translate/translate_common_test.go | 4 ++-- operator/pkg/translate/translate_test.go | 8 +++---- operator/pkg/translate/translate_value.go | 12 +++++----- .../pkg/translate/translate_value_test.go | 6 ++--- operator/pkg/translate/yaml_tree.go | 4 ++-- operator/pkg/util/k8s.go | 2 +- operator/pkg/util/k8s_test.go | 2 +- operator/pkg/util/merge_iop.go | 2 +- operator/pkg/util/merge_iop_test.go | 2 +- operator/pkg/util/progress/progress.go | 2 +- operator/pkg/util/progress/progress_test.go | 2 +- operator/pkg/validate/common.go | 4 ++-- operator/pkg/validate/validate.go | 8 +++---- operator/pkg/validate/validate_test.go | 2 +- operator/pkg/validate/validate_values.go | 4 ++-- operator/pkg/validate/validate_values_test.go | 6 ++--- operator/version/version.go | 2 +- 46 files changed, 118 insertions(+), 118 deletions(-) diff --git a/go.mod b/go.mod index 7671d9a..d84db63 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/jehawley/istio +module github.com/solo.io/istio-operator-legacy go 1.22.0 diff --git a/operator/pkg/apis/addtoscheme_istio_v1alpha1.go b/operator/pkg/apis/addtoscheme_istio_v1alpha1.go index 4e51515..77d2bfc 100644 --- a/operator/pkg/apis/addtoscheme_istio_v1alpha1.go +++ b/operator/pkg/apis/addtoscheme_istio_v1alpha1.go @@ -15,7 +15,7 @@ package apis import ( - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" ) func init() { diff --git a/operator/pkg/apis/istio/common.go b/operator/pkg/apis/istio/common.go index 388e1c0..1b1cd70 100644 --- a/operator/pkg/apis/istio/common.go +++ b/operator/pkg/apis/istio/common.go @@ -20,9 +20,9 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - operator_v1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/util" - "github.com/jehawley/istio/operator/pkg/validate" + operator_v1alpha1 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/validate" ) // UnmarshalAndValidateIOPS unmarshals a string containing IstioOperator YAML, validates it, and returns a struct diff --git a/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go b/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go index a8c7839..3919a57 100644 --- a/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go +++ b/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go @@ -23,8 +23,8 @@ import ( "sigs.k8s.io/yaml" v1alpha12 "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio" - install "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio" + install "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" ) // This is to verify that certain proto types handle marshal and unmarshal properly diff --git a/operator/pkg/apis/istio/v1alpha1/validation/validation.go b/operator/pkg/apis/istio/v1alpha1/validation/validation.go index 5acfd35..a22f1ff 100644 --- a/operator/pkg/apis/istio/v1alpha1/validation/validation.go +++ b/operator/pkg/apis/istio/v1alpha1/validation/validation.go @@ -25,9 +25,9 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "istio.io/api/123/operator/v1alpha1" - valuesv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + valuesv1alpha1 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) const ( diff --git a/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go b/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go index baf116a..4847b08 100644 --- a/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go +++ b/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go @@ -24,12 +24,12 @@ import ( wrappers "google.golang.org/protobuf/types/known/wrapperspb" v1alpha12 "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1/validation" - "github.com/jehawley/istio/operator/pkg/helm" - "github.com/jehawley/istio/operator/pkg/manifest" - "github.com/jehawley/istio/operator/pkg/util" - "github.com/jehawley/istio/operator/pkg/util/clog" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1/validation" + "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" + "github.com/solo.io/istio-operator-legacy/operator/pkg/manifest" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util/clog" "istio.io/istio/pkg/test/env" ) diff --git a/operator/pkg/apis/istio/v1alpha1/values_types.proto b/operator/pkg/apis/istio/v1alpha1/values_types.proto index f8eb9ea..c8735e4 100644 --- a/operator/pkg/apis/istio/v1alpha1/values_types.proto +++ b/operator/pkg/apis/istio/v1alpha1/values_types.proto @@ -24,7 +24,7 @@ import "k8s.io/api/core/v1/generated.proto"; import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto"; // Package-wide variables from generator "generated". -option go_package = "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1"; +option go_package = "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1"; // ArchConfig specifies the pod scheduling target architecture(amd64, ppc64le, s390x, arm64) // for all the Istio control plane components. diff --git a/operator/pkg/component/component.go b/operator/pkg/component/component.go index 804e397..4fad8c6 100644 --- a/operator/pkg/component/component.go +++ b/operator/pkg/component/component.go @@ -26,12 +26,12 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/helm" - "github.com/jehawley/istio/operator/pkg/metrics" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/patch" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/translate" + "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" + "github.com/solo.io/istio-operator-legacy/operator/pkg/metrics" + "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/patch" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo.io/istio-operator-legacy/operator/pkg/translate" "istio.io/istio/pkg/log" "istio.io/istio/pkg/util/sets" ) diff --git a/operator/pkg/controlplane/control_plane.go b/operator/pkg/controlplane/control_plane.go index 378a725..ac94620 100644 --- a/operator/pkg/controlplane/control_plane.go +++ b/operator/pkg/controlplane/control_plane.go @@ -21,11 +21,11 @@ import ( "k8s.io/apimachinery/pkg/version" "istio.io/api/123/operator/v1alpha1" - iop "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/component" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/translate" - "github.com/jehawley/istio/operator/pkg/util" + iop "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/component" + "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/translate" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/util/sets" ) diff --git a/operator/pkg/controlplane/control_plane_test.go b/operator/pkg/controlplane/control_plane_test.go index cf3bc51..96613f5 100644 --- a/operator/pkg/controlplane/control_plane_test.go +++ b/operator/pkg/controlplane/control_plane_test.go @@ -20,10 +20,10 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/component" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/translate" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/component" + "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/translate" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) func TestOrderedKeys(t *testing.T) { diff --git a/operator/pkg/helm/helm.go b/operator/pkg/helm/helm.go index b877689..c614916 100644 --- a/operator/pkg/helm/helm.go +++ b/operator/pkg/helm/helm.go @@ -30,7 +30,7 @@ import ( "istio.io/istio/istioctl/pkg/install/k8sversion" "istio.io/istio/manifests" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/helm/renderer.go b/operator/pkg/helm/renderer.go index 2e986c4..a099a35 100644 --- a/operator/pkg/helm/renderer.go +++ b/operator/pkg/helm/renderer.go @@ -26,7 +26,7 @@ import ( "k8s.io/apimachinery/pkg/version" "istio.io/istio/manifests" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) const ( diff --git a/operator/pkg/manifest/shared.go b/operator/pkg/manifest/shared.go index 3fa66c2..bebc52a 100644 --- a/operator/pkg/manifest/shared.go +++ b/operator/pkg/manifest/shared.go @@ -26,18 +26,18 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio" - iopv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1/validation" - "github.com/jehawley/istio/operator/pkg/controlplane" - "github.com/jehawley/istio/operator/pkg/helm" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/object" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/translate" - "github.com/jehawley/istio/operator/pkg/util" - "github.com/jehawley/istio/operator/pkg/util/clog" - "github.com/jehawley/istio/operator/pkg/validate" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio" + iopv1alpha1 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1/validation" + "github.com/solo.io/istio-operator-legacy/operator/pkg/controlplane" + "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" + "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/object" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo.io/istio-operator-legacy/operator/pkg/translate" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util/clog" + "github.com/solo.io/istio-operator-legacy/operator/pkg/validate" "istio.io/istio/pkg/kube" "istio.io/istio/pkg/log" "istio.io/istio/pkg/util/sets" diff --git a/operator/pkg/manifest/shared_test.go b/operator/pkg/manifest/shared_test.go index 89f620b..c7c8ace 100644 --- a/operator/pkg/manifest/shared_test.go +++ b/operator/pkg/manifest/shared_test.go @@ -21,7 +21,7 @@ import ( "path/filepath" "testing" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/test/env" ) diff --git a/operator/pkg/metrics/resource_counts.go b/operator/pkg/metrics/resource_counts.go index 31c1d25..32f6600 100644 --- a/operator/pkg/metrics/resource_counts.go +++ b/operator/pkg/metrics/resource_counts.go @@ -19,7 +19,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) // resourceCounts keeps track of the number of resources owned by each diff --git a/operator/pkg/metrics/utils.go b/operator/pkg/metrics/utils.go index 1473708..0cb264e 100644 --- a/operator/pkg/metrics/utils.go +++ b/operator/pkg/metrics/utils.go @@ -17,7 +17,7 @@ package metrics import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/jehawley/istio/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/name" ) // CountCRMergeFail increments the count of CR merge failure diff --git a/operator/pkg/name/name.go b/operator/pkg/name/name.go index bb8c9b2..5afcdce 100644 --- a/operator/pkg/name/name.go +++ b/operator/pkg/name/name.go @@ -19,9 +19,9 @@ import ( "strings" "istio.io/api/123/operator/v1alpha1" - iop "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/helm" - "github.com/jehawley/istio/operator/pkg/tpath" + iop "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" ) // Kubernetes Kind strings. diff --git a/operator/pkg/name/name_test.go b/operator/pkg/name/name_test.go index ac22541..25ef8a4 100644 --- a/operator/pkg/name/name_test.go +++ b/operator/pkg/name/name_test.go @@ -21,8 +21,8 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) func TestGetFromTreePath(t *testing.T) { diff --git a/operator/pkg/object/objects.go b/operator/pkg/object/objects.go index 954c277..8454e74 100644 --- a/operator/pkg/object/objects.go +++ b/operator/pkg/object/objects.go @@ -32,11 +32,11 @@ import ( k8syaml "k8s.io/apimachinery/pkg/util/yaml" "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/helm" - names "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" + names "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/object/objects_test.go b/operator/pkg/object/objects_test.go index fb7fd7f..380aa33 100644 --- a/operator/pkg/object/objects_test.go +++ b/operator/pkg/object/objects_test.go @@ -25,7 +25,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" k8syaml "k8s.io/apimachinery/pkg/util/yaml" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/test/util/assert" ) diff --git a/operator/pkg/patch/patch.go b/operator/pkg/patch/patch.go index f3ec151..f9d872a 100644 --- a/operator/pkg/patch/patch.go +++ b/operator/pkg/patch/patch.go @@ -102,11 +102,11 @@ import ( yaml2 "gopkg.in/yaml.v2" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/helm" - "github.com/jehawley/istio/operator/pkg/metrics" - "github.com/jehawley/istio/operator/pkg/object" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" + "github.com/solo.io/istio-operator-legacy/operator/pkg/metrics" + "github.com/solo.io/istio-operator-legacy/operator/pkg/object" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/patch/patch_test.go b/operator/pkg/patch/patch_test.go index 779d199..1cd9a9d 100644 --- a/operator/pkg/patch/patch_test.go +++ b/operator/pkg/patch/patch_test.go @@ -19,7 +19,7 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) func TestPatchYAMLManifestSuccess(t *testing.T) { diff --git a/operator/pkg/tpath/struct.go b/operator/pkg/tpath/struct.go index 9add224..238a7dc 100644 --- a/operator/pkg/tpath/struct.go +++ b/operator/pkg/tpath/struct.go @@ -24,7 +24,7 @@ import ( "google.golang.org/protobuf/types/known/structpb" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) // GetFromStructPath returns the value at path from the given node, or false if the path does not exist. diff --git a/operator/pkg/tpath/struct_test.go b/operator/pkg/tpath/struct_test.go index 7c06107..d77b1e0 100644 --- a/operator/pkg/tpath/struct_test.go +++ b/operator/pkg/tpath/struct_test.go @@ -19,7 +19,7 @@ import ( "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) func TestGetFromStructPath(t *testing.T) { diff --git a/operator/pkg/tpath/tree.go b/operator/pkg/tpath/tree.go index 0f8f3b4..9f68483 100644 --- a/operator/pkg/tpath/tree.go +++ b/operator/pkg/tpath/tree.go @@ -33,7 +33,7 @@ import ( "gopkg.in/yaml.v2" yaml2 "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/tpath/tree_test.go b/operator/pkg/tpath/tree_test.go index c9ae9cb..465d531 100644 --- a/operator/pkg/tpath/tree_test.go +++ b/operator/pkg/tpath/tree_test.go @@ -19,7 +19,7 @@ import ( "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) func TestWritePathContext(t *testing.T) { diff --git a/operator/pkg/tpath/util.go b/operator/pkg/tpath/util.go index de45b16..51893de 100644 --- a/operator/pkg/tpath/util.go +++ b/operator/pkg/tpath/util.go @@ -22,7 +22,7 @@ import ( "gopkg.in/yaml.v2" yaml2 "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) // AddSpecRoot adds a root node called "spec" to the given tree and returns the resulting tree. diff --git a/operator/pkg/translate/translate.go b/operator/pkg/translate/translate.go index 2d9dc3e..b89a59c 100644 --- a/operator/pkg/translate/translate.go +++ b/operator/pkg/translate/translate.go @@ -31,14 +31,14 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio" - iopv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/object" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" - "github.com/jehawley/istio/operator/pkg/version" - oversion "github.com/jehawley/istio/operator/version" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio" + iopv1alpha1 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/object" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/version" + oversion "github.com/solo.io/istio-operator-legacy/operator/version" "istio.io/istio/pkg/log" "istio.io/istio/pkg/util/sets" ) diff --git a/operator/pkg/translate/translate_common.go b/operator/pkg/translate/translate_common.go index 2f9ffe2..57c4985 100644 --- a/operator/pkg/translate/translate_common.go +++ b/operator/pkg/translate/translate_common.go @@ -20,9 +20,9 @@ import ( "google.golang.org/protobuf/types/known/wrapperspb" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) // IsComponentEnabledInSpec reports whether the given component is enabled in the given spec. diff --git a/operator/pkg/translate/translate_common_test.go b/operator/pkg/translate/translate_common_test.go index 738720b..bf3af7a 100644 --- a/operator/pkg/translate/translate_common_test.go +++ b/operator/pkg/translate/translate_common_test.go @@ -18,8 +18,8 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) func TestGetEnabledComponents(t *testing.T) { diff --git a/operator/pkg/translate/translate_test.go b/operator/pkg/translate/translate_test.go index a5c999c..a0f7412 100644 --- a/operator/pkg/translate/translate_test.go +++ b/operator/pkg/translate/translate_test.go @@ -19,10 +19,10 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/object" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/object" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/test/util/assert" ) diff --git a/operator/pkg/translate/translate_value.go b/operator/pkg/translate/translate_value.go index 355485a..85a9eb5 100644 --- a/operator/pkg/translate/translate_value.go +++ b/operator/pkg/translate/translate_value.go @@ -22,12 +22,12 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/metrics" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" - "github.com/jehawley/istio/operator/pkg/version" - oversion "github.com/jehawley/istio/operator/version" + "github.com/solo.io/istio-operator-legacy/operator/pkg/metrics" + "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/version" + oversion "github.com/solo.io/istio-operator-legacy/operator/version" ) // ReverseTranslator is a set of mappings to translate between values.yaml and API paths, charts, k8s paths. diff --git a/operator/pkg/translate/translate_value_test.go b/operator/pkg/translate/translate_value_test.go index fea97c9..81a49d2 100644 --- a/operator/pkg/translate/translate_value_test.go +++ b/operator/pkg/translate/translate_value_test.go @@ -19,9 +19,9 @@ import ( "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/apis/istio" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/util/protomarshal" ) diff --git a/operator/pkg/translate/yaml_tree.go b/operator/pkg/translate/yaml_tree.go index 566a7ea..751232b 100644 --- a/operator/pkg/translate/yaml_tree.go +++ b/operator/pkg/translate/yaml_tree.go @@ -17,8 +17,8 @@ package translate import ( "gopkg.in/yaml.v2" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) // YAMLTree takes an input tree inTreeStr, a partially constructed output tree outTreeStr, and a map of diff --git a/operator/pkg/util/k8s.go b/operator/pkg/util/k8s.go index 2d6db27..edb66a3 100644 --- a/operator/pkg/util/k8s.go +++ b/operator/pkg/util/k8s.go @@ -27,7 +27,7 @@ import ( "k8s.io/client-go/kubernetes" "istio.io/api/label" - iopv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + iopv1alpha1 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" "istio.io/istio/pkg/config/constants" "istio.io/istio/pkg/kube" ) diff --git a/operator/pkg/util/k8s_test.go b/operator/pkg/util/k8s_test.go index 7259c01..bf29540 100644 --- a/operator/pkg/util/k8s_test.go +++ b/operator/pkg/util/k8s_test.go @@ -22,7 +22,7 @@ import ( fakediscovery "k8s.io/client-go/discovery/fake" "sigs.k8s.io/yaml" - pkgAPI "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + pkgAPI "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" "istio.io/istio/pkg/kube" "istio.io/istio/pkg/test/util/assert" ) diff --git a/operator/pkg/util/merge_iop.go b/operator/pkg/util/merge_iop.go index bfabddf..7d74c94 100644 --- a/operator/pkg/util/merge_iop.go +++ b/operator/pkg/util/merge_iop.go @@ -27,7 +27,7 @@ import ( v1alpha13 "istio.io/api/mesh/v1alpha1" "istio.io/api/networking/v1alpha3" "istio.io/api/123/operator/v1alpha1" - v1alpha12 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + v1alpha12 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" ) // Partially mirrored from istio/api and operator/pkg/api (for values). diff --git a/operator/pkg/util/merge_iop_test.go b/operator/pkg/util/merge_iop_test.go index 74ec635..d34b9d9 100644 --- a/operator/pkg/util/merge_iop_test.go +++ b/operator/pkg/util/merge_iop_test.go @@ -23,7 +23,7 @@ import ( meshconfig "istio.io/api/mesh/v1alpha1" v1alpha12 "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" "istio.io/istio/pkg/config/mesh" "istio.io/istio/pkg/test/env" "istio.io/istio/pkg/util/protomarshal" diff --git a/operator/pkg/util/progress/progress.go b/operator/pkg/util/progress/progress.go index eb81e29..7fc8b71 100644 --- a/operator/pkg/util/progress/progress.go +++ b/operator/pkg/util/progress/progress.go @@ -23,7 +23,7 @@ import ( "github.com/cheggaaa/pb/v3" - "github.com/jehawley/istio/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/name" ) type InstallState int diff --git a/operator/pkg/util/progress/progress_test.go b/operator/pkg/util/progress/progress_test.go index 984d5c9..d880de7 100644 --- a/operator/pkg/util/progress/progress_test.go +++ b/operator/pkg/util/progress/progress_test.go @@ -19,7 +19,7 @@ import ( "io" "testing" - "github.com/jehawley/istio/operator/pkg/name" + "github.com/solo.io/istio-operator-legacy/operator/pkg/name" ) func TestProgressLog(t *testing.T) { diff --git a/operator/pkg/validate/common.go b/operator/pkg/validate/common.go index a18f6fd..364e94a 100644 --- a/operator/pkg/validate/common.go +++ b/operator/pkg/validate/common.go @@ -26,8 +26,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/validate/validate.go b/operator/pkg/validate/validate.go index 5d5fac7..cbebd52 100644 --- a/operator/pkg/validate/validate.go +++ b/operator/pkg/validate/validate.go @@ -22,10 +22,10 @@ import ( "google.golang.org/protobuf/types/known/structpb" "istio.io/api/123/operator/v1alpha1" - operator_v1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/metrics" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + operator_v1alpha1 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/metrics" + "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/config/labels" "istio.io/istio/pkg/config/mesh" "istio.io/istio/pkg/util/protomarshal" diff --git a/operator/pkg/validate/validate_test.go b/operator/pkg/validate/validate_test.go index a6cc0a0..b6e5463 100644 --- a/operator/pkg/validate/validate_test.go +++ b/operator/pkg/validate/validate_test.go @@ -18,7 +18,7 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) func TestValidate(t *testing.T) { diff --git a/operator/pkg/validate/validate_values.go b/operator/pkg/validate/validate_values.go index 28d411f..505f73f 100644 --- a/operator/pkg/validate/validate_values.go +++ b/operator/pkg/validate/validate_values.go @@ -19,8 +19,8 @@ import ( "google.golang.org/protobuf/types/known/structpb" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" ) // DefaultValuesValidations maps a data path to a validation function. diff --git a/operator/pkg/validate/validate_values_test.go b/operator/pkg/validate/validate_values_test.go index d8d369f..f08b460 100644 --- a/operator/pkg/validate/validate_values_test.go +++ b/operator/pkg/validate/validate_values_test.go @@ -22,9 +22,9 @@ import ( "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/helm" - "github.com/jehawley/istio/operator/pkg/object" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" + "github.com/solo.io/istio-operator-legacy/operator/pkg/object" + "github.com/solo.io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/test/env" ) diff --git a/operator/version/version.go b/operator/version/version.go index 62a1ce8..975d972 100644 --- a/operator/version/version.go +++ b/operator/version/version.go @@ -17,7 +17,7 @@ package version import ( "time" - pkgversion "github.com/jehawley/istio/operator/pkg/version" + pkgversion "github.com/solo.io/istio-operator-legacy/operator/pkg/version" buildversion "istio.io/istio/pkg/version" ) From b1a31b7b86829201046938c5d290b820c0f5aaa0 Mon Sep 17 00:00:00 2001 From: John Hawley Date: Fri, 8 Nov 2024 11:42:41 -0500 Subject: [PATCH 3/4] Revert "update module name" This reverts commit fa6091a21a824ba34509bcf96f84bec6c1627b97. --- go.mod | 2 +- .../pkg/apis/addtoscheme_istio_v1alpha1.go | 2 +- operator/pkg/apis/istio/common.go | 6 ++--- .../pkg/apis/istio/v1alpha1/deepcopy_test.go | 4 ++-- .../istio/v1alpha1/validation/validation.go | 6 ++--- .../v1alpha1/validation/validation_test.go | 12 +++++----- .../apis/istio/v1alpha1/values_types.proto | 2 +- operator/pkg/component/component.go | 12 +++++----- operator/pkg/controlplane/control_plane.go | 10 ++++---- .../pkg/controlplane/control_plane_test.go | 8 +++---- operator/pkg/helm/helm.go | 2 +- operator/pkg/helm/renderer.go | 2 +- operator/pkg/manifest/shared.go | 24 +++++++++---------- operator/pkg/manifest/shared_test.go | 2 +- operator/pkg/metrics/resource_counts.go | 2 +- operator/pkg/metrics/utils.go | 2 +- operator/pkg/name/name.go | 6 ++--- operator/pkg/name/name_test.go | 4 ++-- operator/pkg/object/objects.go | 10 ++++---- operator/pkg/object/objects_test.go | 2 +- operator/pkg/patch/patch.go | 10 ++++---- operator/pkg/patch/patch_test.go | 2 +- operator/pkg/tpath/struct.go | 2 +- operator/pkg/tpath/struct_test.go | 2 +- operator/pkg/tpath/tree.go | 2 +- operator/pkg/tpath/tree_test.go | 2 +- operator/pkg/tpath/util.go | 2 +- operator/pkg/translate/translate.go | 16 ++++++------- operator/pkg/translate/translate_common.go | 6 ++--- .../pkg/translate/translate_common_test.go | 4 ++-- operator/pkg/translate/translate_test.go | 8 +++---- operator/pkg/translate/translate_value.go | 12 +++++----- .../pkg/translate/translate_value_test.go | 6 ++--- operator/pkg/translate/yaml_tree.go | 4 ++-- operator/pkg/util/k8s.go | 2 +- operator/pkg/util/k8s_test.go | 2 +- operator/pkg/util/merge_iop.go | 2 +- operator/pkg/util/merge_iop_test.go | 2 +- operator/pkg/util/progress/progress.go | 2 +- operator/pkg/util/progress/progress_test.go | 2 +- operator/pkg/validate/common.go | 4 ++-- operator/pkg/validate/validate.go | 8 +++---- operator/pkg/validate/validate_test.go | 2 +- operator/pkg/validate/validate_values.go | 4 ++-- operator/pkg/validate/validate_values_test.go | 6 ++--- operator/version/version.go | 2 +- 46 files changed, 118 insertions(+), 118 deletions(-) diff --git a/go.mod b/go.mod index d84db63..7671d9a 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/solo.io/istio-operator-legacy +module github.com/jehawley/istio go 1.22.0 diff --git a/operator/pkg/apis/addtoscheme_istio_v1alpha1.go b/operator/pkg/apis/addtoscheme_istio_v1alpha1.go index 77d2bfc..4e51515 100644 --- a/operator/pkg/apis/addtoscheme_istio_v1alpha1.go +++ b/operator/pkg/apis/addtoscheme_istio_v1alpha1.go @@ -15,7 +15,7 @@ package apis import ( - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" ) func init() { diff --git a/operator/pkg/apis/istio/common.go b/operator/pkg/apis/istio/common.go index 1b1cd70..388e1c0 100644 --- a/operator/pkg/apis/istio/common.go +++ b/operator/pkg/apis/istio/common.go @@ -20,9 +20,9 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - operator_v1alpha1 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" - "github.com/solo.io/istio-operator-legacy/operator/pkg/validate" + operator_v1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/validate" ) // UnmarshalAndValidateIOPS unmarshals a string containing IstioOperator YAML, validates it, and returns a struct diff --git a/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go b/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go index 3919a57..a8c7839 100644 --- a/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go +++ b/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go @@ -23,8 +23,8 @@ import ( "sigs.k8s.io/yaml" v1alpha12 "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio" - install "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/apis/istio" + install "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" ) // This is to verify that certain proto types handle marshal and unmarshal properly diff --git a/operator/pkg/apis/istio/v1alpha1/validation/validation.go b/operator/pkg/apis/istio/v1alpha1/validation/validation.go index a22f1ff..5acfd35 100644 --- a/operator/pkg/apis/istio/v1alpha1/validation/validation.go +++ b/operator/pkg/apis/istio/v1alpha1/validation/validation.go @@ -25,9 +25,9 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "istio.io/api/123/operator/v1alpha1" - valuesv1alpha1 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + valuesv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" ) const ( diff --git a/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go b/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go index 4847b08..baf116a 100644 --- a/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go +++ b/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go @@ -24,12 +24,12 @@ import ( wrappers "google.golang.org/protobuf/types/known/wrapperspb" v1alpha12 "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1/validation" - "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" - "github.com/solo.io/istio-operator-legacy/operator/pkg/manifest" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util/clog" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1/validation" + "github.com/jehawley/istio/operator/pkg/helm" + "github.com/jehawley/istio/operator/pkg/manifest" + "github.com/jehawley/istio/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util/clog" "istio.io/istio/pkg/test/env" ) diff --git a/operator/pkg/apis/istio/v1alpha1/values_types.proto b/operator/pkg/apis/istio/v1alpha1/values_types.proto index c8735e4..f8eb9ea 100644 --- a/operator/pkg/apis/istio/v1alpha1/values_types.proto +++ b/operator/pkg/apis/istio/v1alpha1/values_types.proto @@ -24,7 +24,7 @@ import "k8s.io/api/core/v1/generated.proto"; import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto"; // Package-wide variables from generator "generated". -option go_package = "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1"; +option go_package = "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1"; // ArchConfig specifies the pod scheduling target architecture(amd64, ppc64le, s390x, arm64) // for all the Istio control plane components. diff --git a/operator/pkg/component/component.go b/operator/pkg/component/component.go index 4fad8c6..804e397 100644 --- a/operator/pkg/component/component.go +++ b/operator/pkg/component/component.go @@ -26,12 +26,12 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" - "github.com/solo.io/istio-operator-legacy/operator/pkg/metrics" - "github.com/solo.io/istio-operator-legacy/operator/pkg/name" - "github.com/solo.io/istio-operator-legacy/operator/pkg/patch" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" - "github.com/solo.io/istio-operator-legacy/operator/pkg/translate" + "github.com/jehawley/istio/operator/pkg/helm" + "github.com/jehawley/istio/operator/pkg/metrics" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/patch" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/translate" "istio.io/istio/pkg/log" "istio.io/istio/pkg/util/sets" ) diff --git a/operator/pkg/controlplane/control_plane.go b/operator/pkg/controlplane/control_plane.go index ac94620..378a725 100644 --- a/operator/pkg/controlplane/control_plane.go +++ b/operator/pkg/controlplane/control_plane.go @@ -21,11 +21,11 @@ import ( "k8s.io/apimachinery/pkg/version" "istio.io/api/123/operator/v1alpha1" - iop "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/component" - "github.com/solo.io/istio-operator-legacy/operator/pkg/name" - "github.com/solo.io/istio-operator-legacy/operator/pkg/translate" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + iop "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/component" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/translate" + "github.com/jehawley/istio/operator/pkg/util" "istio.io/istio/pkg/util/sets" ) diff --git a/operator/pkg/controlplane/control_plane_test.go b/operator/pkg/controlplane/control_plane_test.go index 96613f5..cf3bc51 100644 --- a/operator/pkg/controlplane/control_plane_test.go +++ b/operator/pkg/controlplane/control_plane_test.go @@ -20,10 +20,10 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/component" - "github.com/solo.io/istio-operator-legacy/operator/pkg/name" - "github.com/solo.io/istio-operator-legacy/operator/pkg/translate" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/component" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/translate" + "github.com/jehawley/istio/operator/pkg/util" ) func TestOrderedKeys(t *testing.T) { diff --git a/operator/pkg/helm/helm.go b/operator/pkg/helm/helm.go index c614916..b877689 100644 --- a/operator/pkg/helm/helm.go +++ b/operator/pkg/helm/helm.go @@ -30,7 +30,7 @@ import ( "istio.io/istio/istioctl/pkg/install/k8sversion" "istio.io/istio/manifests" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/helm/renderer.go b/operator/pkg/helm/renderer.go index a099a35..2e986c4 100644 --- a/operator/pkg/helm/renderer.go +++ b/operator/pkg/helm/renderer.go @@ -26,7 +26,7 @@ import ( "k8s.io/apimachinery/pkg/version" "istio.io/istio/manifests" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util" ) const ( diff --git a/operator/pkg/manifest/shared.go b/operator/pkg/manifest/shared.go index bebc52a..3fa66c2 100644 --- a/operator/pkg/manifest/shared.go +++ b/operator/pkg/manifest/shared.go @@ -26,18 +26,18 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio" - iopv1alpha1 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1/validation" - "github.com/solo.io/istio-operator-legacy/operator/pkg/controlplane" - "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" - "github.com/solo.io/istio-operator-legacy/operator/pkg/name" - "github.com/solo.io/istio-operator-legacy/operator/pkg/object" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" - "github.com/solo.io/istio-operator-legacy/operator/pkg/translate" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util/clog" - "github.com/solo.io/istio-operator-legacy/operator/pkg/validate" + "github.com/jehawley/istio/operator/pkg/apis/istio" + iopv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1/validation" + "github.com/jehawley/istio/operator/pkg/controlplane" + "github.com/jehawley/istio/operator/pkg/helm" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/object" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/translate" + "github.com/jehawley/istio/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util/clog" + "github.com/jehawley/istio/operator/pkg/validate" "istio.io/istio/pkg/kube" "istio.io/istio/pkg/log" "istio.io/istio/pkg/util/sets" diff --git a/operator/pkg/manifest/shared_test.go b/operator/pkg/manifest/shared_test.go index c7c8ace..89f620b 100644 --- a/operator/pkg/manifest/shared_test.go +++ b/operator/pkg/manifest/shared_test.go @@ -21,7 +21,7 @@ import ( "path/filepath" "testing" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util" "istio.io/istio/pkg/test/env" ) diff --git a/operator/pkg/metrics/resource_counts.go b/operator/pkg/metrics/resource_counts.go index 32f6600..31c1d25 100644 --- a/operator/pkg/metrics/resource_counts.go +++ b/operator/pkg/metrics/resource_counts.go @@ -19,7 +19,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util" ) // resourceCounts keeps track of the number of resources owned by each diff --git a/operator/pkg/metrics/utils.go b/operator/pkg/metrics/utils.go index 0cb264e..1473708 100644 --- a/operator/pkg/metrics/utils.go +++ b/operator/pkg/metrics/utils.go @@ -17,7 +17,7 @@ package metrics import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/name" ) // CountCRMergeFail increments the count of CR merge failure diff --git a/operator/pkg/name/name.go b/operator/pkg/name/name.go index 5afcdce..bb8c9b2 100644 --- a/operator/pkg/name/name.go +++ b/operator/pkg/name/name.go @@ -19,9 +19,9 @@ import ( "strings" "istio.io/api/123/operator/v1alpha1" - iop "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" + iop "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/helm" + "github.com/jehawley/istio/operator/pkg/tpath" ) // Kubernetes Kind strings. diff --git a/operator/pkg/name/name_test.go b/operator/pkg/name/name_test.go index 25ef8a4..ac22541 100644 --- a/operator/pkg/name/name_test.go +++ b/operator/pkg/name/name_test.go @@ -21,8 +21,8 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" ) func TestGetFromTreePath(t *testing.T) { diff --git a/operator/pkg/object/objects.go b/operator/pkg/object/objects.go index 8454e74..954c277 100644 --- a/operator/pkg/object/objects.go +++ b/operator/pkg/object/objects.go @@ -32,11 +32,11 @@ import ( k8syaml "k8s.io/apimachinery/pkg/util/yaml" "sigs.k8s.io/yaml" - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" - names "github.com/solo.io/istio-operator-legacy/operator/pkg/name" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/helm" + names "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/object/objects_test.go b/operator/pkg/object/objects_test.go index 380aa33..fb7fd7f 100644 --- a/operator/pkg/object/objects_test.go +++ b/operator/pkg/object/objects_test.go @@ -25,7 +25,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" k8syaml "k8s.io/apimachinery/pkg/util/yaml" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util" "istio.io/istio/pkg/test/util/assert" ) diff --git a/operator/pkg/patch/patch.go b/operator/pkg/patch/patch.go index f9d872a..f3ec151 100644 --- a/operator/pkg/patch/patch.go +++ b/operator/pkg/patch/patch.go @@ -102,11 +102,11 @@ import ( yaml2 "gopkg.in/yaml.v2" "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" - "github.com/solo.io/istio-operator-legacy/operator/pkg/metrics" - "github.com/solo.io/istio-operator-legacy/operator/pkg/object" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/helm" + "github.com/jehawley/istio/operator/pkg/metrics" + "github.com/jehawley/istio/operator/pkg/object" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/patch/patch_test.go b/operator/pkg/patch/patch_test.go index 1cd9a9d..779d199 100644 --- a/operator/pkg/patch/patch_test.go +++ b/operator/pkg/patch/patch_test.go @@ -19,7 +19,7 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util" ) func TestPatchYAMLManifestSuccess(t *testing.T) { diff --git a/operator/pkg/tpath/struct.go b/operator/pkg/tpath/struct.go index 238a7dc..9add224 100644 --- a/operator/pkg/tpath/struct.go +++ b/operator/pkg/tpath/struct.go @@ -24,7 +24,7 @@ import ( "google.golang.org/protobuf/types/known/structpb" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util" ) // GetFromStructPath returns the value at path from the given node, or false if the path does not exist. diff --git a/operator/pkg/tpath/struct_test.go b/operator/pkg/tpath/struct_test.go index d77b1e0..7c06107 100644 --- a/operator/pkg/tpath/struct_test.go +++ b/operator/pkg/tpath/struct_test.go @@ -19,7 +19,7 @@ import ( "sigs.k8s.io/yaml" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util" ) func TestGetFromStructPath(t *testing.T) { diff --git a/operator/pkg/tpath/tree.go b/operator/pkg/tpath/tree.go index 9f68483..0f8f3b4 100644 --- a/operator/pkg/tpath/tree.go +++ b/operator/pkg/tpath/tree.go @@ -33,7 +33,7 @@ import ( "gopkg.in/yaml.v2" yaml2 "sigs.k8s.io/yaml" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/tpath/tree_test.go b/operator/pkg/tpath/tree_test.go index 465d531..c9ae9cb 100644 --- a/operator/pkg/tpath/tree_test.go +++ b/operator/pkg/tpath/tree_test.go @@ -19,7 +19,7 @@ import ( "sigs.k8s.io/yaml" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util" ) func TestWritePathContext(t *testing.T) { diff --git a/operator/pkg/tpath/util.go b/operator/pkg/tpath/util.go index 51893de..de45b16 100644 --- a/operator/pkg/tpath/util.go +++ b/operator/pkg/tpath/util.go @@ -22,7 +22,7 @@ import ( "gopkg.in/yaml.v2" yaml2 "sigs.k8s.io/yaml" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util" ) // AddSpecRoot adds a root node called "spec" to the given tree and returns the resulting tree. diff --git a/operator/pkg/translate/translate.go b/operator/pkg/translate/translate.go index b89a59c..2d9dc3e 100644 --- a/operator/pkg/translate/translate.go +++ b/operator/pkg/translate/translate.go @@ -31,14 +31,14 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio" - iopv1alpha1 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/name" - "github.com/solo.io/istio-operator-legacy/operator/pkg/object" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" - "github.com/solo.io/istio-operator-legacy/operator/pkg/version" - oversion "github.com/solo.io/istio-operator-legacy/operator/version" + "github.com/jehawley/istio/operator/pkg/apis/istio" + iopv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/object" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/version" + oversion "github.com/jehawley/istio/operator/version" "istio.io/istio/pkg/log" "istio.io/istio/pkg/util/sets" ) diff --git a/operator/pkg/translate/translate_common.go b/operator/pkg/translate/translate_common.go index 57c4985..2f9ffe2 100644 --- a/operator/pkg/translate/translate_common.go +++ b/operator/pkg/translate/translate_common.go @@ -20,9 +20,9 @@ import ( "google.golang.org/protobuf/types/known/wrapperspb" "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/name" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" ) // IsComponentEnabledInSpec reports whether the given component is enabled in the given spec. diff --git a/operator/pkg/translate/translate_common_test.go b/operator/pkg/translate/translate_common_test.go index bf3af7a..738720b 100644 --- a/operator/pkg/translate/translate_common_test.go +++ b/operator/pkg/translate/translate_common_test.go @@ -18,8 +18,8 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/name" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/util" ) func TestGetEnabledComponents(t *testing.T) { diff --git a/operator/pkg/translate/translate_test.go b/operator/pkg/translate/translate_test.go index a0f7412..a5c999c 100644 --- a/operator/pkg/translate/translate_test.go +++ b/operator/pkg/translate/translate_test.go @@ -19,10 +19,10 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/name" - "github.com/solo.io/istio-operator-legacy/operator/pkg/object" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/object" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" "istio.io/istio/pkg/test/util/assert" ) diff --git a/operator/pkg/translate/translate_value.go b/operator/pkg/translate/translate_value.go index 85a9eb5..355485a 100644 --- a/operator/pkg/translate/translate_value.go +++ b/operator/pkg/translate/translate_value.go @@ -22,12 +22,12 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/metrics" - "github.com/solo.io/istio-operator-legacy/operator/pkg/name" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" - "github.com/solo.io/istio-operator-legacy/operator/pkg/version" - oversion "github.com/solo.io/istio-operator-legacy/operator/version" + "github.com/jehawley/istio/operator/pkg/metrics" + "github.com/jehawley/istio/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/version" + oversion "github.com/jehawley/istio/operator/version" ) // ReverseTranslator is a set of mappings to translate between values.yaml and API paths, charts, k8s paths. diff --git a/operator/pkg/translate/translate_value_test.go b/operator/pkg/translate/translate_value_test.go index 81a49d2..fea97c9 100644 --- a/operator/pkg/translate/translate_value_test.go +++ b/operator/pkg/translate/translate_value_test.go @@ -19,9 +19,9 @@ import ( "sigs.k8s.io/yaml" - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio" - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/apis/istio" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/util" "istio.io/istio/pkg/util/protomarshal" ) diff --git a/operator/pkg/translate/yaml_tree.go b/operator/pkg/translate/yaml_tree.go index 751232b..566a7ea 100644 --- a/operator/pkg/translate/yaml_tree.go +++ b/operator/pkg/translate/yaml_tree.go @@ -17,8 +17,8 @@ package translate import ( "gopkg.in/yaml.v2" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" ) // YAMLTree takes an input tree inTreeStr, a partially constructed output tree outTreeStr, and a map of diff --git a/operator/pkg/util/k8s.go b/operator/pkg/util/k8s.go index edb66a3..2d6db27 100644 --- a/operator/pkg/util/k8s.go +++ b/operator/pkg/util/k8s.go @@ -27,7 +27,7 @@ import ( "k8s.io/client-go/kubernetes" "istio.io/api/label" - iopv1alpha1 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + iopv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" "istio.io/istio/pkg/config/constants" "istio.io/istio/pkg/kube" ) diff --git a/operator/pkg/util/k8s_test.go b/operator/pkg/util/k8s_test.go index bf29540..7259c01 100644 --- a/operator/pkg/util/k8s_test.go +++ b/operator/pkg/util/k8s_test.go @@ -22,7 +22,7 @@ import ( fakediscovery "k8s.io/client-go/discovery/fake" "sigs.k8s.io/yaml" - pkgAPI "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + pkgAPI "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" "istio.io/istio/pkg/kube" "istio.io/istio/pkg/test/util/assert" ) diff --git a/operator/pkg/util/merge_iop.go b/operator/pkg/util/merge_iop.go index 7d74c94..bfabddf 100644 --- a/operator/pkg/util/merge_iop.go +++ b/operator/pkg/util/merge_iop.go @@ -27,7 +27,7 @@ import ( v1alpha13 "istio.io/api/mesh/v1alpha1" "istio.io/api/networking/v1alpha3" "istio.io/api/123/operator/v1alpha1" - v1alpha12 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + v1alpha12 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" ) // Partially mirrored from istio/api and operator/pkg/api (for values). diff --git a/operator/pkg/util/merge_iop_test.go b/operator/pkg/util/merge_iop_test.go index d34b9d9..74ec635 100644 --- a/operator/pkg/util/merge_iop_test.go +++ b/operator/pkg/util/merge_iop_test.go @@ -23,7 +23,7 @@ import ( meshconfig "istio.io/api/mesh/v1alpha1" v1alpha12 "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" "istio.io/istio/pkg/config/mesh" "istio.io/istio/pkg/test/env" "istio.io/istio/pkg/util/protomarshal" diff --git a/operator/pkg/util/progress/progress.go b/operator/pkg/util/progress/progress.go index 7fc8b71..eb81e29 100644 --- a/operator/pkg/util/progress/progress.go +++ b/operator/pkg/util/progress/progress.go @@ -23,7 +23,7 @@ import ( "github.com/cheggaaa/pb/v3" - "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/name" ) type InstallState int diff --git a/operator/pkg/util/progress/progress_test.go b/operator/pkg/util/progress/progress_test.go index d880de7..984d5c9 100644 --- a/operator/pkg/util/progress/progress_test.go +++ b/operator/pkg/util/progress/progress_test.go @@ -19,7 +19,7 @@ import ( "io" "testing" - "github.com/solo.io/istio-operator-legacy/operator/pkg/name" + "github.com/jehawley/istio/operator/pkg/name" ) func TestProgressLog(t *testing.T) { diff --git a/operator/pkg/validate/common.go b/operator/pkg/validate/common.go index 364e94a..a18f6fd 100644 --- a/operator/pkg/validate/common.go +++ b/operator/pkg/validate/common.go @@ -26,8 +26,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/yaml" - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/validate/validate.go b/operator/pkg/validate/validate.go index cbebd52..5d5fac7 100644 --- a/operator/pkg/validate/validate.go +++ b/operator/pkg/validate/validate.go @@ -22,10 +22,10 @@ import ( "google.golang.org/protobuf/types/known/structpb" "istio.io/api/123/operator/v1alpha1" - operator_v1alpha1 "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/metrics" - "github.com/solo.io/istio-operator-legacy/operator/pkg/tpath" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + operator_v1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/metrics" + "github.com/jehawley/istio/operator/pkg/tpath" + "github.com/jehawley/istio/operator/pkg/util" "istio.io/istio/pkg/config/labels" "istio.io/istio/pkg/config/mesh" "istio.io/istio/pkg/util/protomarshal" diff --git a/operator/pkg/validate/validate_test.go b/operator/pkg/validate/validate_test.go index b6e5463..a6cc0a0 100644 --- a/operator/pkg/validate/validate_test.go +++ b/operator/pkg/validate/validate_test.go @@ -18,7 +18,7 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/util" ) func TestValidate(t *testing.T) { diff --git a/operator/pkg/validate/validate_values.go b/operator/pkg/validate/validate_values.go index 505f73f..28d411f 100644 --- a/operator/pkg/validate/validate_values.go +++ b/operator/pkg/validate/validate_values.go @@ -19,8 +19,8 @@ import ( "google.golang.org/protobuf/types/known/structpb" - "github.com/solo.io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/jehawley/istio/operator/pkg/util" ) // DefaultValuesValidations maps a data path to a validation function. diff --git a/operator/pkg/validate/validate_values_test.go b/operator/pkg/validate/validate_values_test.go index f08b460..d8d369f 100644 --- a/operator/pkg/validate/validate_values_test.go +++ b/operator/pkg/validate/validate_values_test.go @@ -22,9 +22,9 @@ import ( "sigs.k8s.io/yaml" - "github.com/solo.io/istio-operator-legacy/operator/pkg/helm" - "github.com/solo.io/istio-operator-legacy/operator/pkg/object" - "github.com/solo.io/istio-operator-legacy/operator/pkg/util" + "github.com/jehawley/istio/operator/pkg/helm" + "github.com/jehawley/istio/operator/pkg/object" + "github.com/jehawley/istio/operator/pkg/util" "istio.io/istio/pkg/test/env" ) diff --git a/operator/version/version.go b/operator/version/version.go index 975d972..62a1ce8 100644 --- a/operator/version/version.go +++ b/operator/version/version.go @@ -17,7 +17,7 @@ package version import ( "time" - pkgversion "github.com/solo.io/istio-operator-legacy/operator/pkg/version" + pkgversion "github.com/jehawley/istio/operator/pkg/version" buildversion "istio.io/istio/pkg/version" ) From f85fe304b807c66b238b76bae6479a88a596cef2 Mon Sep 17 00:00:00 2001 From: John Hawley Date: Fri, 8 Nov 2024 11:43:00 -0500 Subject: [PATCH 4/4] update module name --- go.mod | 2 +- .../pkg/apis/addtoscheme_istio_v1alpha1.go | 2 +- operator/pkg/apis/istio/common.go | 6 ++--- .../pkg/apis/istio/v1alpha1/deepcopy_test.go | 4 ++-- .../istio/v1alpha1/validation/validation.go | 6 ++--- .../v1alpha1/validation/validation_test.go | 12 +++++----- .../apis/istio/v1alpha1/values_types.proto | 2 +- operator/pkg/component/component.go | 12 +++++----- operator/pkg/controlplane/control_plane.go | 10 ++++---- .../pkg/controlplane/control_plane_test.go | 8 +++---- operator/pkg/helm/helm.go | 2 +- operator/pkg/helm/renderer.go | 2 +- operator/pkg/manifest/shared.go | 24 +++++++++---------- operator/pkg/manifest/shared_test.go | 2 +- operator/pkg/metrics/resource_counts.go | 2 +- operator/pkg/metrics/utils.go | 2 +- operator/pkg/name/name.go | 6 ++--- operator/pkg/name/name_test.go | 4 ++-- operator/pkg/object/objects.go | 10 ++++---- operator/pkg/object/objects_test.go | 2 +- operator/pkg/patch/patch.go | 10 ++++---- operator/pkg/patch/patch_test.go | 2 +- operator/pkg/tpath/struct.go | 2 +- operator/pkg/tpath/struct_test.go | 2 +- operator/pkg/tpath/tree.go | 2 +- operator/pkg/tpath/tree_test.go | 2 +- operator/pkg/tpath/util.go | 2 +- operator/pkg/translate/translate.go | 16 ++++++------- operator/pkg/translate/translate_common.go | 6 ++--- .../pkg/translate/translate_common_test.go | 4 ++-- operator/pkg/translate/translate_test.go | 8 +++---- operator/pkg/translate/translate_value.go | 12 +++++----- .../pkg/translate/translate_value_test.go | 6 ++--- operator/pkg/translate/yaml_tree.go | 4 ++-- operator/pkg/util/k8s.go | 2 +- operator/pkg/util/k8s_test.go | 2 +- operator/pkg/util/merge_iop.go | 2 +- operator/pkg/util/merge_iop_test.go | 2 +- operator/pkg/util/progress/progress.go | 2 +- operator/pkg/util/progress/progress_test.go | 2 +- operator/pkg/validate/common.go | 4 ++-- operator/pkg/validate/validate.go | 8 +++---- operator/pkg/validate/validate_test.go | 2 +- operator/pkg/validate/validate_values.go | 4 ++-- operator/pkg/validate/validate_values_test.go | 6 ++--- operator/version/version.go | 2 +- 46 files changed, 118 insertions(+), 118 deletions(-) diff --git a/go.mod b/go.mod index 7671d9a..87eb494 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/jehawley/istio +module github.com/solo-io/istio-operator-legacy go 1.22.0 diff --git a/operator/pkg/apis/addtoscheme_istio_v1alpha1.go b/operator/pkg/apis/addtoscheme_istio_v1alpha1.go index 4e51515..e54560e 100644 --- a/operator/pkg/apis/addtoscheme_istio_v1alpha1.go +++ b/operator/pkg/apis/addtoscheme_istio_v1alpha1.go @@ -15,7 +15,7 @@ package apis import ( - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" ) func init() { diff --git a/operator/pkg/apis/istio/common.go b/operator/pkg/apis/istio/common.go index 388e1c0..6410262 100644 --- a/operator/pkg/apis/istio/common.go +++ b/operator/pkg/apis/istio/common.go @@ -20,9 +20,9 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - operator_v1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/util" - "github.com/jehawley/istio/operator/pkg/validate" + operator_v1alpha1 "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/validate" ) // UnmarshalAndValidateIOPS unmarshals a string containing IstioOperator YAML, validates it, and returns a struct diff --git a/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go b/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go index a8c7839..453e0c8 100644 --- a/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go +++ b/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go @@ -23,8 +23,8 @@ import ( "sigs.k8s.io/yaml" v1alpha12 "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio" - install "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio" + install "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" ) // This is to verify that certain proto types handle marshal and unmarshal properly diff --git a/operator/pkg/apis/istio/v1alpha1/validation/validation.go b/operator/pkg/apis/istio/v1alpha1/validation/validation.go index 5acfd35..f4c6bbe 100644 --- a/operator/pkg/apis/istio/v1alpha1/validation/validation.go +++ b/operator/pkg/apis/istio/v1alpha1/validation/validation.go @@ -25,9 +25,9 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "istio.io/api/123/operator/v1alpha1" - valuesv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + valuesv1alpha1 "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) const ( diff --git a/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go b/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go index baf116a..6996ea9 100644 --- a/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go +++ b/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go @@ -24,12 +24,12 @@ import ( wrappers "google.golang.org/protobuf/types/known/wrapperspb" v1alpha12 "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1/validation" - "github.com/jehawley/istio/operator/pkg/helm" - "github.com/jehawley/istio/operator/pkg/manifest" - "github.com/jehawley/istio/operator/pkg/util" - "github.com/jehawley/istio/operator/pkg/util/clog" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1/validation" + "github.com/solo-io/istio-operator-legacy/operator/pkg/helm" + "github.com/solo-io/istio-operator-legacy/operator/pkg/manifest" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util/clog" "istio.io/istio/pkg/test/env" ) diff --git a/operator/pkg/apis/istio/v1alpha1/values_types.proto b/operator/pkg/apis/istio/v1alpha1/values_types.proto index f8eb9ea..3a5ad25 100644 --- a/operator/pkg/apis/istio/v1alpha1/values_types.proto +++ b/operator/pkg/apis/istio/v1alpha1/values_types.proto @@ -24,7 +24,7 @@ import "k8s.io/api/core/v1/generated.proto"; import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto"; // Package-wide variables from generator "generated". -option go_package = "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1"; +option go_package = "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1"; // ArchConfig specifies the pod scheduling target architecture(amd64, ppc64le, s390x, arm64) // for all the Istio control plane components. diff --git a/operator/pkg/component/component.go b/operator/pkg/component/component.go index 804e397..0b83f0f 100644 --- a/operator/pkg/component/component.go +++ b/operator/pkg/component/component.go @@ -26,12 +26,12 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/helm" - "github.com/jehawley/istio/operator/pkg/metrics" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/patch" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/translate" + "github.com/solo-io/istio-operator-legacy/operator/pkg/helm" + "github.com/solo-io/istio-operator-legacy/operator/pkg/metrics" + "github.com/solo-io/istio-operator-legacy/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/patch" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo-io/istio-operator-legacy/operator/pkg/translate" "istio.io/istio/pkg/log" "istio.io/istio/pkg/util/sets" ) diff --git a/operator/pkg/controlplane/control_plane.go b/operator/pkg/controlplane/control_plane.go index 378a725..27ef04f 100644 --- a/operator/pkg/controlplane/control_plane.go +++ b/operator/pkg/controlplane/control_plane.go @@ -21,11 +21,11 @@ import ( "k8s.io/apimachinery/pkg/version" "istio.io/api/123/operator/v1alpha1" - iop "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/component" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/translate" - "github.com/jehawley/istio/operator/pkg/util" + iop "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/component" + "github.com/solo-io/istio-operator-legacy/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/translate" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/util/sets" ) diff --git a/operator/pkg/controlplane/control_plane_test.go b/operator/pkg/controlplane/control_plane_test.go index cf3bc51..46b0dc0 100644 --- a/operator/pkg/controlplane/control_plane_test.go +++ b/operator/pkg/controlplane/control_plane_test.go @@ -20,10 +20,10 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/component" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/translate" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/component" + "github.com/solo-io/istio-operator-legacy/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/translate" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) func TestOrderedKeys(t *testing.T) { diff --git a/operator/pkg/helm/helm.go b/operator/pkg/helm/helm.go index b877689..bda2bcf 100644 --- a/operator/pkg/helm/helm.go +++ b/operator/pkg/helm/helm.go @@ -30,7 +30,7 @@ import ( "istio.io/istio/istioctl/pkg/install/k8sversion" "istio.io/istio/manifests" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/helm/renderer.go b/operator/pkg/helm/renderer.go index 2e986c4..bc1aa66 100644 --- a/operator/pkg/helm/renderer.go +++ b/operator/pkg/helm/renderer.go @@ -26,7 +26,7 @@ import ( "k8s.io/apimachinery/pkg/version" "istio.io/istio/manifests" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) const ( diff --git a/operator/pkg/manifest/shared.go b/operator/pkg/manifest/shared.go index 3fa66c2..1c00e8f 100644 --- a/operator/pkg/manifest/shared.go +++ b/operator/pkg/manifest/shared.go @@ -26,18 +26,18 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio" - iopv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1/validation" - "github.com/jehawley/istio/operator/pkg/controlplane" - "github.com/jehawley/istio/operator/pkg/helm" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/object" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/translate" - "github.com/jehawley/istio/operator/pkg/util" - "github.com/jehawley/istio/operator/pkg/util/clog" - "github.com/jehawley/istio/operator/pkg/validate" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio" + iopv1alpha1 "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1/validation" + "github.com/solo-io/istio-operator-legacy/operator/pkg/controlplane" + "github.com/solo-io/istio-operator-legacy/operator/pkg/helm" + "github.com/solo-io/istio-operator-legacy/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/object" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo-io/istio-operator-legacy/operator/pkg/translate" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util/clog" + "github.com/solo-io/istio-operator-legacy/operator/pkg/validate" "istio.io/istio/pkg/kube" "istio.io/istio/pkg/log" "istio.io/istio/pkg/util/sets" diff --git a/operator/pkg/manifest/shared_test.go b/operator/pkg/manifest/shared_test.go index 89f620b..6f839e0 100644 --- a/operator/pkg/manifest/shared_test.go +++ b/operator/pkg/manifest/shared_test.go @@ -21,7 +21,7 @@ import ( "path/filepath" "testing" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/test/env" ) diff --git a/operator/pkg/metrics/resource_counts.go b/operator/pkg/metrics/resource_counts.go index 31c1d25..42cc882 100644 --- a/operator/pkg/metrics/resource_counts.go +++ b/operator/pkg/metrics/resource_counts.go @@ -19,7 +19,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) // resourceCounts keeps track of the number of resources owned by each diff --git a/operator/pkg/metrics/utils.go b/operator/pkg/metrics/utils.go index 1473708..f07ea36 100644 --- a/operator/pkg/metrics/utils.go +++ b/operator/pkg/metrics/utils.go @@ -17,7 +17,7 @@ package metrics import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/jehawley/istio/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/name" ) // CountCRMergeFail increments the count of CR merge failure diff --git a/operator/pkg/name/name.go b/operator/pkg/name/name.go index bb8c9b2..2f5694e 100644 --- a/operator/pkg/name/name.go +++ b/operator/pkg/name/name.go @@ -19,9 +19,9 @@ import ( "strings" "istio.io/api/123/operator/v1alpha1" - iop "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/helm" - "github.com/jehawley/istio/operator/pkg/tpath" + iop "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/helm" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" ) // Kubernetes Kind strings. diff --git a/operator/pkg/name/name_test.go b/operator/pkg/name/name_test.go index ac22541..acd48c4 100644 --- a/operator/pkg/name/name_test.go +++ b/operator/pkg/name/name_test.go @@ -21,8 +21,8 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) func TestGetFromTreePath(t *testing.T) { diff --git a/operator/pkg/object/objects.go b/operator/pkg/object/objects.go index 954c277..e098dda 100644 --- a/operator/pkg/object/objects.go +++ b/operator/pkg/object/objects.go @@ -32,11 +32,11 @@ import ( k8syaml "k8s.io/apimachinery/pkg/util/yaml" "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/helm" - names "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/helm" + names "github.com/solo-io/istio-operator-legacy/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/object/objects_test.go b/operator/pkg/object/objects_test.go index fb7fd7f..afaf5a0 100644 --- a/operator/pkg/object/objects_test.go +++ b/operator/pkg/object/objects_test.go @@ -25,7 +25,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" k8syaml "k8s.io/apimachinery/pkg/util/yaml" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/test/util/assert" ) diff --git a/operator/pkg/patch/patch.go b/operator/pkg/patch/patch.go index f3ec151..c09c6c7 100644 --- a/operator/pkg/patch/patch.go +++ b/operator/pkg/patch/patch.go @@ -102,11 +102,11 @@ import ( yaml2 "gopkg.in/yaml.v2" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/helm" - "github.com/jehawley/istio/operator/pkg/metrics" - "github.com/jehawley/istio/operator/pkg/object" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/helm" + "github.com/solo-io/istio-operator-legacy/operator/pkg/metrics" + "github.com/solo-io/istio-operator-legacy/operator/pkg/object" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/patch/patch_test.go b/operator/pkg/patch/patch_test.go index 779d199..c5ffccb 100644 --- a/operator/pkg/patch/patch_test.go +++ b/operator/pkg/patch/patch_test.go @@ -19,7 +19,7 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) func TestPatchYAMLManifestSuccess(t *testing.T) { diff --git a/operator/pkg/tpath/struct.go b/operator/pkg/tpath/struct.go index 9add224..7ea7d33 100644 --- a/operator/pkg/tpath/struct.go +++ b/operator/pkg/tpath/struct.go @@ -24,7 +24,7 @@ import ( "google.golang.org/protobuf/types/known/structpb" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) // GetFromStructPath returns the value at path from the given node, or false if the path does not exist. diff --git a/operator/pkg/tpath/struct_test.go b/operator/pkg/tpath/struct_test.go index 7c06107..6fae822 100644 --- a/operator/pkg/tpath/struct_test.go +++ b/operator/pkg/tpath/struct_test.go @@ -19,7 +19,7 @@ import ( "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) func TestGetFromStructPath(t *testing.T) { diff --git a/operator/pkg/tpath/tree.go b/operator/pkg/tpath/tree.go index 0f8f3b4..da3167c 100644 --- a/operator/pkg/tpath/tree.go +++ b/operator/pkg/tpath/tree.go @@ -33,7 +33,7 @@ import ( "gopkg.in/yaml.v2" yaml2 "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/tpath/tree_test.go b/operator/pkg/tpath/tree_test.go index c9ae9cb..8e7ac91 100644 --- a/operator/pkg/tpath/tree_test.go +++ b/operator/pkg/tpath/tree_test.go @@ -19,7 +19,7 @@ import ( "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) func TestWritePathContext(t *testing.T) { diff --git a/operator/pkg/tpath/util.go b/operator/pkg/tpath/util.go index de45b16..992a57d 100644 --- a/operator/pkg/tpath/util.go +++ b/operator/pkg/tpath/util.go @@ -22,7 +22,7 @@ import ( "gopkg.in/yaml.v2" yaml2 "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) // AddSpecRoot adds a root node called "spec" to the given tree and returns the resulting tree. diff --git a/operator/pkg/translate/translate.go b/operator/pkg/translate/translate.go index 2d9dc3e..ad8db59 100644 --- a/operator/pkg/translate/translate.go +++ b/operator/pkg/translate/translate.go @@ -31,14 +31,14 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio" - iopv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/object" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" - "github.com/jehawley/istio/operator/pkg/version" - oversion "github.com/jehawley/istio/operator/version" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio" + iopv1alpha1 "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/object" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/version" + oversion "github.com/solo-io/istio-operator-legacy/operator/version" "istio.io/istio/pkg/log" "istio.io/istio/pkg/util/sets" ) diff --git a/operator/pkg/translate/translate_common.go b/operator/pkg/translate/translate_common.go index 2f9ffe2..6ff2868 100644 --- a/operator/pkg/translate/translate_common.go +++ b/operator/pkg/translate/translate_common.go @@ -20,9 +20,9 @@ import ( "google.golang.org/protobuf/types/known/wrapperspb" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) // IsComponentEnabledInSpec reports whether the given component is enabled in the given spec. diff --git a/operator/pkg/translate/translate_common_test.go b/operator/pkg/translate/translate_common_test.go index 738720b..7718ee4 100644 --- a/operator/pkg/translate/translate_common_test.go +++ b/operator/pkg/translate/translate_common_test.go @@ -18,8 +18,8 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) func TestGetEnabledComponents(t *testing.T) { diff --git a/operator/pkg/translate/translate_test.go b/operator/pkg/translate/translate_test.go index a5c999c..aa74e6e 100644 --- a/operator/pkg/translate/translate_test.go +++ b/operator/pkg/translate/translate_test.go @@ -19,10 +19,10 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/object" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/object" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/test/util/assert" ) diff --git a/operator/pkg/translate/translate_value.go b/operator/pkg/translate/translate_value.go index 355485a..32c8c5c 100644 --- a/operator/pkg/translate/translate_value.go +++ b/operator/pkg/translate/translate_value.go @@ -22,12 +22,12 @@ import ( "sigs.k8s.io/yaml" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/metrics" - "github.com/jehawley/istio/operator/pkg/name" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" - "github.com/jehawley/istio/operator/pkg/version" - oversion "github.com/jehawley/istio/operator/version" + "github.com/solo-io/istio-operator-legacy/operator/pkg/metrics" + "github.com/solo-io/istio-operator-legacy/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/version" + oversion "github.com/solo-io/istio-operator-legacy/operator/version" ) // ReverseTranslator is a set of mappings to translate between values.yaml and API paths, charts, k8s paths. diff --git a/operator/pkg/translate/translate_value_test.go b/operator/pkg/translate/translate_value_test.go index fea97c9..c5779dc 100644 --- a/operator/pkg/translate/translate_value_test.go +++ b/operator/pkg/translate/translate_value_test.go @@ -19,9 +19,9 @@ import ( "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/apis/istio" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/util/protomarshal" ) diff --git a/operator/pkg/translate/yaml_tree.go b/operator/pkg/translate/yaml_tree.go index 566a7ea..b7c704a 100644 --- a/operator/pkg/translate/yaml_tree.go +++ b/operator/pkg/translate/yaml_tree.go @@ -17,8 +17,8 @@ package translate import ( "gopkg.in/yaml.v2" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) // YAMLTree takes an input tree inTreeStr, a partially constructed output tree outTreeStr, and a map of diff --git a/operator/pkg/util/k8s.go b/operator/pkg/util/k8s.go index 2d6db27..d8c1c9b 100644 --- a/operator/pkg/util/k8s.go +++ b/operator/pkg/util/k8s.go @@ -27,7 +27,7 @@ import ( "k8s.io/client-go/kubernetes" "istio.io/api/label" - iopv1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + iopv1alpha1 "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" "istio.io/istio/pkg/config/constants" "istio.io/istio/pkg/kube" ) diff --git a/operator/pkg/util/k8s_test.go b/operator/pkg/util/k8s_test.go index 7259c01..b0c0e5d 100644 --- a/operator/pkg/util/k8s_test.go +++ b/operator/pkg/util/k8s_test.go @@ -22,7 +22,7 @@ import ( fakediscovery "k8s.io/client-go/discovery/fake" "sigs.k8s.io/yaml" - pkgAPI "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + pkgAPI "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" "istio.io/istio/pkg/kube" "istio.io/istio/pkg/test/util/assert" ) diff --git a/operator/pkg/util/merge_iop.go b/operator/pkg/util/merge_iop.go index bfabddf..f46bd01 100644 --- a/operator/pkg/util/merge_iop.go +++ b/operator/pkg/util/merge_iop.go @@ -27,7 +27,7 @@ import ( v1alpha13 "istio.io/api/mesh/v1alpha1" "istio.io/api/networking/v1alpha3" "istio.io/api/123/operator/v1alpha1" - v1alpha12 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + v1alpha12 "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" ) // Partially mirrored from istio/api and operator/pkg/api (for values). diff --git a/operator/pkg/util/merge_iop_test.go b/operator/pkg/util/merge_iop_test.go index 74ec635..21f3d0f 100644 --- a/operator/pkg/util/merge_iop_test.go +++ b/operator/pkg/util/merge_iop_test.go @@ -23,7 +23,7 @@ import ( meshconfig "istio.io/api/mesh/v1alpha1" v1alpha12 "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" "istio.io/istio/pkg/config/mesh" "istio.io/istio/pkg/test/env" "istio.io/istio/pkg/util/protomarshal" diff --git a/operator/pkg/util/progress/progress.go b/operator/pkg/util/progress/progress.go index eb81e29..3e54825 100644 --- a/operator/pkg/util/progress/progress.go +++ b/operator/pkg/util/progress/progress.go @@ -23,7 +23,7 @@ import ( "github.com/cheggaaa/pb/v3" - "github.com/jehawley/istio/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/name" ) type InstallState int diff --git a/operator/pkg/util/progress/progress_test.go b/operator/pkg/util/progress/progress_test.go index 984d5c9..03040f3 100644 --- a/operator/pkg/util/progress/progress_test.go +++ b/operator/pkg/util/progress/progress_test.go @@ -19,7 +19,7 @@ import ( "io" "testing" - "github.com/jehawley/istio/operator/pkg/name" + "github.com/solo-io/istio-operator-legacy/operator/pkg/name" ) func TestProgressLog(t *testing.T) { diff --git a/operator/pkg/validate/common.go b/operator/pkg/validate/common.go index a18f6fd..b5cd404 100644 --- a/operator/pkg/validate/common.go +++ b/operator/pkg/validate/common.go @@ -26,8 +26,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/log" ) diff --git a/operator/pkg/validate/validate.go b/operator/pkg/validate/validate.go index 5d5fac7..461cb82 100644 --- a/operator/pkg/validate/validate.go +++ b/operator/pkg/validate/validate.go @@ -22,10 +22,10 @@ import ( "google.golang.org/protobuf/types/known/structpb" "istio.io/api/123/operator/v1alpha1" - operator_v1alpha1 "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/metrics" - "github.com/jehawley/istio/operator/pkg/tpath" - "github.com/jehawley/istio/operator/pkg/util" + operator_v1alpha1 "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/metrics" + "github.com/solo-io/istio-operator-legacy/operator/pkg/tpath" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/config/labels" "istio.io/istio/pkg/config/mesh" "istio.io/istio/pkg/util/protomarshal" diff --git a/operator/pkg/validate/validate_test.go b/operator/pkg/validate/validate_test.go index a6cc0a0..1adcb7c 100644 --- a/operator/pkg/validate/validate_test.go +++ b/operator/pkg/validate/validate_test.go @@ -18,7 +18,7 @@ import ( "testing" "istio.io/api/123/operator/v1alpha1" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) func TestValidate(t *testing.T) { diff --git a/operator/pkg/validate/validate_values.go b/operator/pkg/validate/validate_values.go index 28d411f..34b72a3 100644 --- a/operator/pkg/validate/validate_values.go +++ b/operator/pkg/validate/validate_values.go @@ -19,8 +19,8 @@ import ( "google.golang.org/protobuf/types/known/structpb" - "github.com/jehawley/istio/operator/pkg/apis/istio/v1alpha1" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/apis/istio/v1alpha1" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" ) // DefaultValuesValidations maps a data path to a validation function. diff --git a/operator/pkg/validate/validate_values_test.go b/operator/pkg/validate/validate_values_test.go index d8d369f..ddbdffc 100644 --- a/operator/pkg/validate/validate_values_test.go +++ b/operator/pkg/validate/validate_values_test.go @@ -22,9 +22,9 @@ import ( "sigs.k8s.io/yaml" - "github.com/jehawley/istio/operator/pkg/helm" - "github.com/jehawley/istio/operator/pkg/object" - "github.com/jehawley/istio/operator/pkg/util" + "github.com/solo-io/istio-operator-legacy/operator/pkg/helm" + "github.com/solo-io/istio-operator-legacy/operator/pkg/object" + "github.com/solo-io/istio-operator-legacy/operator/pkg/util" "istio.io/istio/pkg/test/env" ) diff --git a/operator/version/version.go b/operator/version/version.go index 62a1ce8..1fccf0a 100644 --- a/operator/version/version.go +++ b/operator/version/version.go @@ -17,7 +17,7 @@ package version import ( "time" - pkgversion "github.com/jehawley/istio/operator/pkg/version" + pkgversion "github.com/solo-io/istio-operator-legacy/operator/pkg/version" buildversion "istio.io/istio/pkg/version" )