From 4378d0a18e0a14ba54b75062be587dede20bb3ba Mon Sep 17 00:00:00 2001 From: vgonuguntla Date: Wed, 17 Aug 2022 08:00:08 -0700 Subject: [PATCH] Routing policy addition (#243) (#150) * Adding the routing policy Co-authored-by: psikka1 Co-authored-by: vgonuguntla --- Makefile | 8 +- admiral/cmd/admiral/cmd/root.go | 14 +- admiral/crd/routingPolicy.yaml | 14 ++ admiral/pkg/apis/admiral/model/doc.go | 1 + .../apis/admiral/model/routingpolicy.pb.go | 100 +++++++++ .../apis/admiral/model/routingpolicy.proto | 32 +++ .../admiral/model/zz_generated.deepcopy.go | 34 +++ admiral/pkg/apis/admiral/v1/register.go | 2 + admiral/pkg/apis/admiral/v1/type.go | 26 +++ .../apis/admiral/v1/zz_generated.deepcopy.go | 77 +++++++ .../typed/admiral/v1/admiral_client.go | 5 + .../admiral/v1/fake/fake_admiral_client.go | 4 + .../admiral/v1/fake/fake_routingpolicy.go | 140 ++++++++++++ .../typed/admiral/v1/generated_expansion.go | 2 + .../typed/admiral/v1/routingpolicy.go | 191 ++++++++++++++++ .../externalversions/admiral/v1/interface.go | 7 + .../admiral/v1/routingpolicy.go | 89 ++++++++ .../informers/externalversions/generic.go | 2 + .../listers/admiral/v1/expansion_generated.go | 8 + .../listers/admiral/v1/routingpolicy.go | 94 ++++++++ admiral/pkg/clusters/envoyfilter.go | 193 ++++++++++++++++ admiral/pkg/clusters/envoyfilter_test.go | 172 ++++++++++++++ admiral/pkg/clusters/handler.go | 2 +- admiral/pkg/clusters/registry.go | 54 ++++- admiral/pkg/clusters/registry_test.go | 31 ++- admiral/pkg/clusters/serviceentry.go | 9 +- admiral/pkg/clusters/serviceentry_test.go | 10 +- admiral/pkg/clusters/types.go | 211 +++++++++++++++++- admiral/pkg/clusters/types_test.go | 197 ++++++++++++++++ admiral/pkg/clusters/util.go | 15 ++ admiral/pkg/clusters/util_test.go | 56 ++++- .../pkg/controller/admiral/routingpolicy.go | 90 ++++++++ .../controller/admiral/routingpolicy_test.go | 97 ++++++++ admiral/pkg/controller/common/common.go | 58 ++++- admiral/pkg/controller/common/common_test.go | 55 +++++ admiral/pkg/controller/common/config.go | 16 ++ admiral/pkg/controller/common/types.go | 59 +++-- admiral/pkg/test/mock.go | 16 ++ hack/update-codegen.sh | 2 +- install/admiral/base/crds.yaml | 17 ++ install/admiral/base/deployments.yaml | 3 + install/admiral/base/role_bindings.yaml | 17 +- install/admiral/base/roles.yaml | 12 + .../demosinglecluster/envconfig_values.yaml | 5 +- install/admiralremote/base/remote.yaml | 2 +- install/sample/rp.yaml | 21 ++ tests/run.sh | 2 + tests/test6.sh | 41 ++++ 48 files changed, 2254 insertions(+), 59 deletions(-) create mode 100644 admiral/crd/routingPolicy.yaml create mode 100644 admiral/pkg/apis/admiral/model/routingpolicy.pb.go create mode 100644 admiral/pkg/apis/admiral/model/routingpolicy.proto create mode 100644 admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_routingpolicy.go create mode 100644 admiral/pkg/client/clientset/versioned/typed/admiral/v1/routingpolicy.go create mode 100644 admiral/pkg/client/informers/externalversions/admiral/v1/routingpolicy.go create mode 100644 admiral/pkg/client/listers/admiral/v1/routingpolicy.go create mode 100644 admiral/pkg/clusters/envoyfilter.go create mode 100644 admiral/pkg/clusters/envoyfilter_test.go create mode 100644 admiral/pkg/controller/admiral/routingpolicy.go create mode 100644 admiral/pkg/controller/admiral/routingpolicy_test.go create mode 100644 install/sample/rp.yaml create mode 100755 tests/test6.sh diff --git a/Makefile b/Makefile index 4b12b0bcc..6f90e4750 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,7 @@ CUSTOM_RESOURCE_VERSION=v1 MAIN_PATH_ADMIRAL=./admiral/cmd/admiral/main.go OPSYS:=$(shell $(GOCMD) env GOOS) +DEEPCOPYGEN=deepcopy-gen PATH:=$(GOBIN):$(PATH) @@ -56,8 +57,10 @@ dep: setup: $(GOGET) -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 +model-gen: + $(DEEPCOPYGEN) -i ./admiral/pkg/apis/admiral/model/ -O zz_generated.deepcopy -gen-all: api-gen crd-gen +gen-all: api-gen model-gen crd-gen install-protoc-mac: curl -OL https://github.com/google/protobuf/releases/download/v$(PROTOC_VER)/$(PROTOC_ZIP) @@ -167,4 +170,5 @@ gen-yaml: cp ./install/sample/gtp_topology.yaml ./out/yaml/gtp_topology.yaml cp ./install/sample/grpc-client.yaml ./out/yaml/grpc-client.yaml cp ./install/prometheus/prometheus.yaml ./out/yaml/prometheus.yaml - cp ./install/scripts/*.sh ./out/scripts/ \ No newline at end of file + cp ./install/sample/rp.yaml ./out/yaml/rp.yaml + cp ./install/scripts/*.sh ./out/scripts/ diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index dc211b589..3245b5fb0 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -133,11 +133,15 @@ func GetRootCmd(args []string) *cobra.Command { rootCmd.PersistentFlags().StringVar(¶ms.SecretResolverConfigPath, "secret_resolver_config_path", "/etc/config/resolver_config.yaml", "Path to the secret resolver config") rootCmd.PersistentFlags().BoolVar(¶ms.MetricsEnabled, "metrics", true, "Enable prometheus metrics collections") - rootCmd.PersistentFlags().StringVar(¶ms.AdmiralStateCheckerName,"admiral_state_checker_name","NoOPStateChecker","The value of the admiral_state_checker_name label to configure the DR Strategy for Admiral") - rootCmd.PersistentFlags().StringVar(¶ms.DRStateStoreConfigPath,"dr_state_store_config_path","","Location of config file which has details for data store. Ex:- Dynamo DB connection details") - rootCmd.PersistentFlags().StringVar(¶ms.ServiceEntryIPPrefix,"se_ip_prefix","240.0","IP prefix for the auto generated IPs for service entries. Only the first two octets: Eg- 240.0") - - + rootCmd.PersistentFlags().StringVar(¶ms.AdmiralStateCheckerName, "admiral_state_checker_name", "NoOPStateChecker", "The value of the admiral_state_checker_name label to configure the DR Strategy for Admiral") + rootCmd.PersistentFlags().StringVar(¶ms.DRStateStoreConfigPath, "dr_state_store_config_path", "", "Location of config file which has details for data store. Ex:- Dynamo DB connection details") + rootCmd.PersistentFlags().StringVar(¶ms.ServiceEntryIPPrefix, "se_ip_prefix", "240.0", "IP prefix for the auto generated IPs for service entries. Only the first two octets: Eg- 240.0") + rootCmd.PersistentFlags().StringVar(¶ms.EnvoyFilterVersion, "envoy_filter_version", "", + "The value of envoy filter version is used to match the proxy version for envoy filter created by routing policy") + rootCmd.PersistentFlags().StringVar(¶ms.EnvoyFilterAdditionalConfig, "envoy_filter_additional_config", "", + "The value of envoy filter is to add additional config to the filter config section") + rootCmd.PersistentFlags().BoolVar(¶ms.EnableRoutingPolicy, "enable_routing_policy", false, + "If Routing Policy feature needs to be enabled") return rootCmd } diff --git a/admiral/crd/routingPolicy.yaml b/admiral/crd/routingPolicy.yaml new file mode 100644 index 000000000..1b3926449 --- /dev/null +++ b/admiral/crd/routingPolicy.yaml @@ -0,0 +1,14 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: routingpolicies.admiral.io +spec: + group: admiral.io + version: v1alpha1 + names: + kind: RoutingPolicy + plural: routingpolicies + shortNames: + - rp + - rps + scope: Namespaced \ No newline at end of file diff --git a/admiral/pkg/apis/admiral/model/doc.go b/admiral/pkg/apis/admiral/model/doc.go index c4b1a2e8b..339d5bcb6 100644 --- a/admiral/pkg/apis/admiral/model/doc.go +++ b/admiral/pkg/apis/admiral/model/doc.go @@ -2,5 +2,6 @@ package model //go:generate protoc -I . dependency.proto --go_out=plugins=grpc:. //go:generate protoc -I . globalrouting.proto --go_out=plugins=grpc:. +//go:generate protoc -I . routingpolicy.proto --go_out=plugins=grpc:. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen=package,register diff --git a/admiral/pkg/apis/admiral/model/routingpolicy.pb.go b/admiral/pkg/apis/admiral/model/routingpolicy.pb.go new file mode 100644 index 000000000..13e7e1254 --- /dev/null +++ b/admiral/pkg/apis/admiral/model/routingpolicy.pb.go @@ -0,0 +1,100 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: routingpolicy.proto + +package model + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type RoutingPolicy struct { + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Hosts []string `protobuf:"bytes,2,rep,name=hosts,proto3" json:"hosts,omitempty"` + Config map[string]string `protobuf:"bytes,3,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RoutingPolicy) Reset() { *m = RoutingPolicy{} } +func (m *RoutingPolicy) String() string { return proto.CompactTextString(m) } +func (*RoutingPolicy) ProtoMessage() {} +func (*RoutingPolicy) Descriptor() ([]byte, []int) { + return fileDescriptor_e4d39db6b36beca3, []int{0} +} + +func (m *RoutingPolicy) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RoutingPolicy.Unmarshal(m, b) +} +func (m *RoutingPolicy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RoutingPolicy.Marshal(b, m, deterministic) +} +func (m *RoutingPolicy) XXX_Merge(src proto.Message) { + xxx_messageInfo_RoutingPolicy.Merge(m, src) +} +func (m *RoutingPolicy) XXX_Size() int { + return xxx_messageInfo_RoutingPolicy.Size(m) +} +func (m *RoutingPolicy) XXX_DiscardUnknown() { + xxx_messageInfo_RoutingPolicy.DiscardUnknown(m) +} + +var xxx_messageInfo_RoutingPolicy proto.InternalMessageInfo + +func (m *RoutingPolicy) GetPlugin() string { + if m != nil { + return m.Plugin + } + return "" +} + +func (m *RoutingPolicy) GetHosts() []string { + if m != nil { + return m.Hosts + } + return nil +} + +func (m *RoutingPolicy) GetConfig() map[string]string { + if m != nil { + return m.Config + } + return nil +} + +func init() { + proto.RegisterType((*RoutingPolicy)(nil), "admiral.global.v1alpha.RoutingPolicy") + proto.RegisterMapType((map[string]string)(nil), "admiral.global.v1alpha.RoutingPolicy.ConfigEntry") +} + +func init() { proto.RegisterFile("routingpolicy.proto", fileDescriptor_e4d39db6b36beca3) } + +var fileDescriptor_e4d39db6b36beca3 = []byte{ + // 202 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2e, 0xca, 0x2f, 0x2d, + 0xc9, 0xcc, 0x4b, 0x2f, 0xc8, 0xcf, 0xc9, 0x4c, 0xae, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, + 0x12, 0x4b, 0x4c, 0xc9, 0xcd, 0x2c, 0x4a, 0xcc, 0xd1, 0x4b, 0xcf, 0xc9, 0x4f, 0x4a, 0xcc, 0xd1, + 0x2b, 0x33, 0x4c, 0xcc, 0x29, 0xc8, 0x48, 0x54, 0x3a, 0xcc, 0xc8, 0xc5, 0x1b, 0x04, 0x51, 0x1f, + 0x00, 0x56, 0x2f, 0x24, 0xc6, 0xc5, 0x56, 0x90, 0x53, 0x9a, 0x9e, 0x99, 0x27, 0xc1, 0xa8, 0xc0, + 0xa8, 0xc1, 0x19, 0x04, 0xe5, 0x09, 0x89, 0x70, 0xb1, 0x66, 0xe4, 0x17, 0x97, 0x14, 0x4b, 0x30, + 0x29, 0x30, 0x6b, 0x70, 0x06, 0x41, 0x38, 0x42, 0x9e, 0x5c, 0x6c, 0xc9, 0xf9, 0x79, 0x69, 0x99, + 0xe9, 0x12, 0xcc, 0x0a, 0xcc, 0x1a, 0xdc, 0x46, 0x86, 0x7a, 0xd8, 0x2d, 0xd2, 0x43, 0xb1, 0x44, + 0xcf, 0x19, 0xac, 0xc7, 0x35, 0xaf, 0xa4, 0xa8, 0x32, 0x08, 0x6a, 0x80, 0x94, 0x25, 0x17, 0x37, + 0x92, 0xb0, 0x90, 0x00, 0x17, 0x73, 0x76, 0x6a, 0x25, 0xd4, 0x11, 0x20, 0x26, 0xc8, 0x05, 0x65, + 0x89, 0x39, 0xa5, 0xa9, 0x12, 0x4c, 0x60, 0x31, 0x08, 0xc7, 0x8a, 0xc9, 0x82, 0xd1, 0x89, 0x3d, + 0x8a, 0x35, 0x37, 0x3f, 0x25, 0x35, 0x27, 0x89, 0x0d, 0xec, 0x5b, 0x63, 0x40, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x62, 0x2c, 0xd5, 0xb8, 0x04, 0x01, 0x00, 0x00, +} diff --git a/admiral/pkg/apis/admiral/model/routingpolicy.proto b/admiral/pkg/apis/admiral/model/routingpolicy.proto new file mode 100644 index 000000000..f36166c06 --- /dev/null +++ b/admiral/pkg/apis/admiral/model/routingpolicy.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package admiral.global.v1alpha; + +option go_package = "model"; + +//``` +// apiVersion: admiral.io/v1alpha1 +// kind: RoutingPolicy +// metadata: +// name: greeting-routing-policy +// annotations: +// admiral.io/env: stage +// labels: +// identity: greeting +// spec: +// plugin: greeting +// hosts: +// - qal.greeting.mesh +// config: +// cachePrefix: cache-v1 +// cachettlSec: "86400" +// dispatcherUrl: qal.greeting.dispatcher.mesh +// algo: greetingDispatcher +// pathPrefix: "/wpcatalog,/consumercatalog,/v1/company/{id}/auth/hydrate,/consumercatalog" +//``` + +message RoutingPolicy { + string plugin = 1; + repeated string hosts = 2; + map config = 3; +} diff --git a/admiral/pkg/apis/admiral/model/zz_generated.deepcopy.go b/admiral/pkg/apis/admiral/model/zz_generated.deepcopy.go index 055ffda13..7ae7a90b6 100644 --- a/admiral/pkg/apis/admiral/model/zz_generated.deepcopy.go +++ b/admiral/pkg/apis/admiral/model/zz_generated.deepcopy.go @@ -87,6 +87,40 @@ func (in *GlobalTrafficPolicy) DeepCopy() *GlobalTrafficPolicy { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RoutingPolicy) DeepCopyInto(out *RoutingPolicy) { + *out = *in + if in.Hosts != nil { + in, out := &in.Hosts, &out.Hosts + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.XXX_NoUnkeyedLiteral = in.XXX_NoUnkeyedLiteral + if in.XXX_unrecognized != nil { + in, out := &in.XXX_unrecognized, &out.XXX_unrecognized + *out = make([]byte, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoutingPolicy. +func (in *RoutingPolicy) DeepCopy() *RoutingPolicy { + if in == nil { + return nil + } + out := new(RoutingPolicy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TrafficGroup) DeepCopyInto(out *TrafficGroup) { *out = *in diff --git a/admiral/pkg/apis/admiral/v1/register.go b/admiral/pkg/apis/admiral/v1/register.go index 401347a4b..6e2205a4a 100644 --- a/admiral/pkg/apis/admiral/v1/register.go +++ b/admiral/pkg/apis/admiral/v1/register.go @@ -52,6 +52,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &DependencyList{}, &GlobalTrafficPolicy{}, &GlobalTrafficPolicyList{}, + &RoutingPolicy{}, + &RoutingPolicyList{}, ) // register the type in the scheme diff --git a/admiral/pkg/apis/admiral/v1/type.go b/admiral/pkg/apis/admiral/v1/type.go index b10e8209a..259fb00e1 100644 --- a/admiral/pkg/apis/admiral/v1/type.go +++ b/admiral/pkg/apis/admiral/v1/type.go @@ -55,3 +55,29 @@ type GlobalTrafficPolicyList struct { Items []GlobalTrafficPolicy `json:"items"` } + +//generic cdr object to wrap the GlobalTrafficPolicy api +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type RoutingPolicy struct { + meta_v1.TypeMeta `json:",inline"` + meta_v1.ObjectMeta `json:"metadata"` + Spec model.RoutingPolicy `json:"spec"` + Status RoutingPolicyStatus `json:"status"` +} + +// FooStatus is the status for a Foo resource + +type RoutingPolicyStatus struct { + ClusterSynced int32 `json:"clustersSynced"` + State string `json:"state"` +} + +// FooList is a list of Foo resources +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type RoutingPolicyList struct { + meta_v1.TypeMeta `json:",inline"` + meta_v1.ListMeta `json:"metadata"` + + Items []RoutingPolicy `json:"items"` +} diff --git a/admiral/pkg/apis/admiral/v1/zz_generated.deepcopy.go b/admiral/pkg/apis/admiral/v1/zz_generated.deepcopy.go index c7b0780c2..034807e9a 100644 --- a/admiral/pkg/apis/admiral/v1/zz_generated.deepcopy.go +++ b/admiral/pkg/apis/admiral/v1/zz_generated.deepcopy.go @@ -177,3 +177,80 @@ func (in *GlobalTrafficPolicyStatus) DeepCopy() *GlobalTrafficPolicyStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RoutingPolicy) DeepCopyInto(out *RoutingPolicy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoutingPolicy. +func (in *RoutingPolicy) DeepCopy() *RoutingPolicy { + if in == nil { + return nil + } + out := new(RoutingPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RoutingPolicy) 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 *RoutingPolicyList) DeepCopyInto(out *RoutingPolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]RoutingPolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoutingPolicyList. +func (in *RoutingPolicyList) DeepCopy() *RoutingPolicyList { + if in == nil { + return nil + } + out := new(RoutingPolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RoutingPolicyList) 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 *RoutingPolicyStatus) DeepCopyInto(out *RoutingPolicyStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoutingPolicyStatus. +func (in *RoutingPolicyStatus) DeepCopy() *RoutingPolicyStatus { + if in == nil { + return nil + } + out := new(RoutingPolicyStatus) + in.DeepCopyInto(out) + return out +} diff --git a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/admiral_client.go b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/admiral_client.go index 465c4c334..a6d89ebae 100644 --- a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/admiral_client.go +++ b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/admiral_client.go @@ -28,6 +28,7 @@ type AdmiralV1Interface interface { RESTClient() rest.Interface DependenciesGetter GlobalTrafficPoliciesGetter + RoutingPoliciesGetter } // AdmiralV1Client is used to interact with features provided by the admiral.io group. @@ -43,6 +44,10 @@ func (c *AdmiralV1Client) GlobalTrafficPolicies(namespace string) GlobalTrafficP return newGlobalTrafficPolicies(c, namespace) } +func (c *AdmiralV1Client) RoutingPolicies(namespace string) RoutingPolicyInterface { + return newRoutingPolicies(c, namespace) +} + // NewForConfig creates a new AdmiralV1Client for the given config. func NewForConfig(c *rest.Config) (*AdmiralV1Client, error) { config := *c diff --git a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_admiral_client.go b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_admiral_client.go index 8bff9d7be..69aa7524a 100644 --- a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_admiral_client.go +++ b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_admiral_client.go @@ -36,6 +36,10 @@ func (c *FakeAdmiralV1) GlobalTrafficPolicies(namespace string) v1.GlobalTraffic return &FakeGlobalTrafficPolicies{c, namespace} } +func (c *FakeAdmiralV1) RoutingPolicies(namespace string) v1.RoutingPolicyInterface { + return &FakeRoutingPolicies{c, namespace} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeAdmiralV1) RESTClient() rest.Interface { diff --git a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_routingpolicy.go b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_routingpolicy.go new file mode 100644 index 000000000..8aaa8ad53 --- /dev/null +++ b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_routingpolicy.go @@ -0,0 +1,140 @@ +/* +Copyright The Kubernetes 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 client-gen. DO NOT EDIT. + +package fake + +import ( + admiralv1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeRoutingPolicies implements RoutingPolicyInterface +type FakeRoutingPolicies struct { + Fake *FakeAdmiralV1 + ns string +} + +var routingpoliciesResource = schema.GroupVersionResource{Group: "admiral.io", Version: "v1", Resource: "routingpolicies"} + +var routingpoliciesKind = schema.GroupVersionKind{Group: "admiral.io", Version: "v1", Kind: "RoutingPolicy"} + +// Get takes name of the routingPolicy, and returns the corresponding routingPolicy object, and an error if there is any. +func (c *FakeRoutingPolicies) Get(name string, options v1.GetOptions) (result *admiralv1.RoutingPolicy, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(routingpoliciesResource, c.ns, name), &admiralv1.RoutingPolicy{}) + + if obj == nil { + return nil, err + } + return obj.(*admiralv1.RoutingPolicy), err +} + +// List takes label and field selectors, and returns the list of RoutingPolicies that match those selectors. +func (c *FakeRoutingPolicies) List(opts v1.ListOptions) (result *admiralv1.RoutingPolicyList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(routingpoliciesResource, routingpoliciesKind, c.ns, opts), &admiralv1.RoutingPolicyList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &admiralv1.RoutingPolicyList{ListMeta: obj.(*admiralv1.RoutingPolicyList).ListMeta} + for _, item := range obj.(*admiralv1.RoutingPolicyList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested routingPolicies. +func (c *FakeRoutingPolicies) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(routingpoliciesResource, c.ns, opts)) + +} + +// Create takes the representation of a routingPolicy and creates it. Returns the server's representation of the routingPolicy, and an error, if there is any. +func (c *FakeRoutingPolicies) Create(routingPolicy *admiralv1.RoutingPolicy) (result *admiralv1.RoutingPolicy, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(routingpoliciesResource, c.ns, routingPolicy), &admiralv1.RoutingPolicy{}) + + if obj == nil { + return nil, err + } + return obj.(*admiralv1.RoutingPolicy), err +} + +// Update takes the representation of a routingPolicy and updates it. Returns the server's representation of the routingPolicy, and an error, if there is any. +func (c *FakeRoutingPolicies) Update(routingPolicy *admiralv1.RoutingPolicy) (result *admiralv1.RoutingPolicy, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(routingpoliciesResource, c.ns, routingPolicy), &admiralv1.RoutingPolicy{}) + + if obj == nil { + return nil, err + } + return obj.(*admiralv1.RoutingPolicy), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeRoutingPolicies) UpdateStatus(routingPolicy *admiralv1.RoutingPolicy) (*admiralv1.RoutingPolicy, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(routingpoliciesResource, "status", c.ns, routingPolicy), &admiralv1.RoutingPolicy{}) + + if obj == nil { + return nil, err + } + return obj.(*admiralv1.RoutingPolicy), err +} + +// Delete takes name of the routingPolicy and deletes it. Returns an error if one occurs. +func (c *FakeRoutingPolicies) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(routingpoliciesResource, c.ns, name), &admiralv1.RoutingPolicy{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeRoutingPolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(routingpoliciesResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &admiralv1.RoutingPolicyList{}) + return err +} + +// Patch applies the patch and returns the patched routingPolicy. +func (c *FakeRoutingPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *admiralv1.RoutingPolicy, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(routingpoliciesResource, c.ns, name, pt, data, subresources...), &admiralv1.RoutingPolicy{}) + + if obj == nil { + return nil, err + } + return obj.(*admiralv1.RoutingPolicy), err +} diff --git a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/generated_expansion.go b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/generated_expansion.go index c04c8724f..5fdd32779 100644 --- a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/generated_expansion.go +++ b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/generated_expansion.go @@ -21,3 +21,5 @@ package v1 type DependencyExpansion interface{} type GlobalTrafficPolicyExpansion interface{} + +type RoutingPolicyExpansion interface{} diff --git a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/routingpolicy.go b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/routingpolicy.go new file mode 100644 index 000000000..1db4ef8be --- /dev/null +++ b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/routingpolicy.go @@ -0,0 +1,191 @@ +/* +Copyright The Kubernetes 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 client-gen. DO NOT EDIT. + +package v1 + +import ( + "time" + + v1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + scheme "github.com/istio-ecosystem/admiral/admiral/pkg/client/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// RoutingPoliciesGetter has a method to return a RoutingPolicyInterface. +// A group's client should implement this interface. +type RoutingPoliciesGetter interface { + RoutingPolicies(namespace string) RoutingPolicyInterface +} + +// RoutingPolicyInterface has methods to work with RoutingPolicy resources. +type RoutingPolicyInterface interface { + Create(*v1.RoutingPolicy) (*v1.RoutingPolicy, error) + Update(*v1.RoutingPolicy) (*v1.RoutingPolicy, error) + UpdateStatus(*v1.RoutingPolicy) (*v1.RoutingPolicy, error) + Delete(name string, options *metav1.DeleteOptions) error + DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error + Get(name string, options metav1.GetOptions) (*v1.RoutingPolicy, error) + List(opts metav1.ListOptions) (*v1.RoutingPolicyList, error) + Watch(opts metav1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.RoutingPolicy, err error) + RoutingPolicyExpansion +} + +// routingPolicies implements RoutingPolicyInterface +type routingPolicies struct { + client rest.Interface + ns string +} + +// newRoutingPolicies returns a RoutingPolicies +func newRoutingPolicies(c *AdmiralV1Client, namespace string) *routingPolicies { + return &routingPolicies{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the routingPolicy, and returns the corresponding routingPolicy object, and an error if there is any. +func (c *routingPolicies) Get(name string, options metav1.GetOptions) (result *v1.RoutingPolicy, err error) { + result = &v1.RoutingPolicy{} + err = c.client.Get(). + Namespace(c.ns). + Resource("routingpolicies"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of RoutingPolicies that match those selectors. +func (c *routingPolicies) List(opts metav1.ListOptions) (result *v1.RoutingPolicyList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1.RoutingPolicyList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("routingpolicies"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return result, err +} + +// Watch returns a watch.Interface that watches the requested routingPolicies. +func (c *routingPolicies) Watch(opts metav1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("routingpolicies"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a routingPolicy and creates it. Returns the server's representation of the routingPolicy, and an error, if there is any. +func (c *routingPolicies) Create(routingPolicy *v1.RoutingPolicy) (result *v1.RoutingPolicy, err error) { + result = &v1.RoutingPolicy{} + err = c.client.Post(). + Namespace(c.ns). + Resource("routingpolicies"). + Body(routingPolicy). + Do(). + Into(result) + return result, err +} + +// Update takes the representation of a routingPolicy and updates it. Returns the server's representation of the routingPolicy, and an error, if there is any. +func (c *routingPolicies) Update(routingPolicy *v1.RoutingPolicy) (result *v1.RoutingPolicy, err error) { + result = &v1.RoutingPolicy{} + err = c.client.Put(). + Namespace(c.ns). + Resource("routingpolicies"). + Name(routingPolicy.Name). + Body(routingPolicy). + Do(). + Into(result) + return result, err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *routingPolicies) UpdateStatus(routingPolicy *v1.RoutingPolicy) (result *v1.RoutingPolicy, err error) { + result = &v1.RoutingPolicy{} + err = c.client.Put(). + Namespace(c.ns). + Resource("routingpolicies"). + Name(routingPolicy.Name). + SubResource("status"). + Body(routingPolicy). + Do(). + Into(result) + return result, err +} + +// Delete takes name of the routingPolicy and deletes it. Returns an error if one occurs. +func (c *routingPolicies) Delete(name string, options *metav1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("routingpolicies"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *routingPolicies) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("routingpolicies"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched routingPolicy. +func (c *routingPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.RoutingPolicy, err error) { + result = &v1.RoutingPolicy{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("routingpolicies"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return result, err +} diff --git a/admiral/pkg/client/informers/externalversions/admiral/v1/interface.go b/admiral/pkg/client/informers/externalversions/admiral/v1/interface.go index 774c162d2..074940d63 100644 --- a/admiral/pkg/client/informers/externalversions/admiral/v1/interface.go +++ b/admiral/pkg/client/informers/externalversions/admiral/v1/interface.go @@ -28,6 +28,8 @@ type Interface interface { Dependencies() DependencyInformer // GlobalTrafficPolicies returns a GlobalTrafficPolicyInformer. GlobalTrafficPolicies() GlobalTrafficPolicyInformer + // RoutingPolicies returns a RoutingPolicyInformer. + RoutingPolicies() RoutingPolicyInformer } type version struct { @@ -50,3 +52,8 @@ func (v *version) Dependencies() DependencyInformer { func (v *version) GlobalTrafficPolicies() GlobalTrafficPolicyInformer { return &globalTrafficPolicyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// RoutingPolicies returns a RoutingPolicyInformer. +func (v *version) RoutingPolicies() RoutingPolicyInformer { + return &routingPolicyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/admiral/pkg/client/informers/externalversions/admiral/v1/routingpolicy.go b/admiral/pkg/client/informers/externalversions/admiral/v1/routingpolicy.go new file mode 100644 index 000000000..0d54f5a98 --- /dev/null +++ b/admiral/pkg/client/informers/externalversions/admiral/v1/routingpolicy.go @@ -0,0 +1,89 @@ +/* +Copyright The Kubernetes 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 informer-gen. DO NOT EDIT. + +package v1 + +import ( + time "time" + + admiralv1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + versioned "github.com/istio-ecosystem/admiral/admiral/pkg/client/clientset/versioned" + internalinterfaces "github.com/istio-ecosystem/admiral/admiral/pkg/client/informers/externalversions/internalinterfaces" + v1 "github.com/istio-ecosystem/admiral/admiral/pkg/client/listers/admiral/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// RoutingPolicyInformer provides access to a shared informer and lister for +// RoutingPolicies. +type RoutingPolicyInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1.RoutingPolicyLister +} + +type routingPolicyInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewRoutingPolicyInformer constructs a new informer for RoutingPolicy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewRoutingPolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredRoutingPolicyInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredRoutingPolicyInformer constructs a new informer for RoutingPolicy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredRoutingPolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AdmiralV1().RoutingPolicies(namespace).List(options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AdmiralV1().RoutingPolicies(namespace).Watch(options) + }, + }, + &admiralv1.RoutingPolicy{}, + resyncPeriod, + indexers, + ) +} + +func (f *routingPolicyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredRoutingPolicyInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *routingPolicyInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&admiralv1.RoutingPolicy{}, f.defaultInformer) +} + +func (f *routingPolicyInformer) Lister() v1.RoutingPolicyLister { + return v1.NewRoutingPolicyLister(f.Informer().GetIndexer()) +} diff --git a/admiral/pkg/client/informers/externalversions/generic.go b/admiral/pkg/client/informers/externalversions/generic.go index e2e9373a3..5c3bd2d05 100644 --- a/admiral/pkg/client/informers/externalversions/generic.go +++ b/admiral/pkg/client/informers/externalversions/generic.go @@ -57,6 +57,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Admiral().V1().Dependencies().Informer()}, nil case v1.SchemeGroupVersion.WithResource("globaltrafficpolicies"): return &genericInformer{resource: resource.GroupResource(), informer: f.Admiral().V1().GlobalTrafficPolicies().Informer()}, nil + case v1.SchemeGroupVersion.WithResource("routingpolicies"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Admiral().V1().RoutingPolicies().Informer()}, nil } diff --git a/admiral/pkg/client/listers/admiral/v1/expansion_generated.go b/admiral/pkg/client/listers/admiral/v1/expansion_generated.go index a9b546df8..c5813b12f 100644 --- a/admiral/pkg/client/listers/admiral/v1/expansion_generated.go +++ b/admiral/pkg/client/listers/admiral/v1/expansion_generated.go @@ -33,3 +33,11 @@ type GlobalTrafficPolicyListerExpansion interface{} // GlobalTrafficPolicyNamespaceListerExpansion allows custom methods to be added to // GlobalTrafficPolicyNamespaceLister. type GlobalTrafficPolicyNamespaceListerExpansion interface{} + +// RoutingPolicyListerExpansion allows custom methods to be added to +// RoutingPolicyLister. +type RoutingPolicyListerExpansion interface{} + +// RoutingPolicyNamespaceListerExpansion allows custom methods to be added to +// RoutingPolicyNamespaceLister. +type RoutingPolicyNamespaceListerExpansion interface{} diff --git a/admiral/pkg/client/listers/admiral/v1/routingpolicy.go b/admiral/pkg/client/listers/admiral/v1/routingpolicy.go new file mode 100644 index 000000000..a986b99e8 --- /dev/null +++ b/admiral/pkg/client/listers/admiral/v1/routingpolicy.go @@ -0,0 +1,94 @@ +/* +Copyright The Kubernetes 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 lister-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// RoutingPolicyLister helps list RoutingPolicies. +type RoutingPolicyLister interface { + // List lists all RoutingPolicies in the indexer. + List(selector labels.Selector) (ret []*v1.RoutingPolicy, err error) + // RoutingPolicies returns an object that can list and get RoutingPolicies. + RoutingPolicies(namespace string) RoutingPolicyNamespaceLister + RoutingPolicyListerExpansion +} + +// routingPolicyLister implements the RoutingPolicyLister interface. +type routingPolicyLister struct { + indexer cache.Indexer +} + +// NewRoutingPolicyLister returns a new RoutingPolicyLister. +func NewRoutingPolicyLister(indexer cache.Indexer) RoutingPolicyLister { + return &routingPolicyLister{indexer: indexer} +} + +// List lists all RoutingPolicies in the indexer. +func (s *routingPolicyLister) List(selector labels.Selector) (ret []*v1.RoutingPolicy, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1.RoutingPolicy)) + }) + return ret, err +} + +// RoutingPolicies returns an object that can list and get RoutingPolicies. +func (s *routingPolicyLister) RoutingPolicies(namespace string) RoutingPolicyNamespaceLister { + return routingPolicyNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// RoutingPolicyNamespaceLister helps list and get RoutingPolicies. +type RoutingPolicyNamespaceLister interface { + // List lists all RoutingPolicies in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1.RoutingPolicy, err error) + // Get retrieves the RoutingPolicy from the indexer for a given namespace and name. + Get(name string) (*v1.RoutingPolicy, error) + RoutingPolicyNamespaceListerExpansion +} + +// routingPolicyNamespaceLister implements the RoutingPolicyNamespaceLister +// interface. +type routingPolicyNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all RoutingPolicies in the indexer for a given namespace. +func (s routingPolicyNamespaceLister) List(selector labels.Selector) (ret []*v1.RoutingPolicy, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1.RoutingPolicy)) + }) + return ret, err +} + +// Get retrieves the RoutingPolicy from the indexer for a given namespace and name. +func (s routingPolicyNamespaceLister) Get(name string) (*v1.RoutingPolicy, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1.Resource("routingpolicy"), name) + } + return obj.(*v1.RoutingPolicy), nil +} diff --git a/admiral/pkg/clusters/envoyfilter.go b/admiral/pkg/clusters/envoyfilter.go new file mode 100644 index 000000000..e9bd7eaf6 --- /dev/null +++ b/admiral/pkg/clusters/envoyfilter.go @@ -0,0 +1,193 @@ +package clusters + +import ( + "errors" + "fmt" + "github.com/gogo/protobuf/types" + v1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/admiral" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + log "github.com/sirupsen/logrus" + "istio.io/api/networking/v1alpha3" + networking "istio.io/client-go/pkg/apis/networking/v1alpha3" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "strings" +) + +var ( + getSha1 = common.GetSha1 +) +const hostsKey = "hosts: " +const pluginKey = "plugin: " + +func createOrUpdateEnvoyFilter( rc *RemoteController, routingPolicy *v1.RoutingPolicy, eventType admiral.EventType, workloadIdentityKey string, admiralCache *AdmiralCache, workloadSelectorMap map[string]string) (*networking.EnvoyFilter, error) { + + envoyfilterSpec := constructEnvoyFilterStruct(routingPolicy, workloadSelectorMap) + + selectorLabelsSha, err := getSha1(workloadIdentityKey+common.GetRoutingPolicyEnv(routingPolicy)) + if err != nil { + log.Error("error ocurred while computing workload labels sha1") + return nil, err + } + if len(common.GetEnvoyFilterVersion()) == 0 { + log.Error("envoy filter version not supplied") + return nil, errors.New("envoy filter version not supplied") + } + envoyFilterName := fmt.Sprintf("%s-dynamicrouting-%s-%s", strings.ToLower(routingPolicy.Spec.Plugin), selectorLabelsSha, common.GetEnvoyFilterVersion()) + envoyfilter := &networking.EnvoyFilter{ + TypeMeta: metaV1.TypeMeta{ + Kind: "EnvoyFilter", + APIVersion: "networking.istio.io/v1alpha3", + }, + ObjectMeta: metaV1.ObjectMeta{ + Name: envoyFilterName, + Namespace: common.NamespaceIstioSystem, + }, + Spec: *envoyfilterSpec, + } + + admiralCache.RoutingPolicyFilterCache.Put(workloadIdentityKey+common.GetRoutingPolicyEnv(routingPolicy), rc.ClusterID, envoyFilterName) + var filter *networking.EnvoyFilter + //get the envoyfilter if it exists. If it exists, update it. Otherwise create it. + if eventType == admiral.Add || eventType == admiral.Update { + // We query the API server instead of getting it from cache because there could be potential condition where the filter exists in the cache but not on the cluster. + filter, err = rc.RoutingPolicyController.IstioClient.NetworkingV1alpha3().EnvoyFilters(common.NamespaceIstioSystem).Get(envoyFilterName, metaV1.GetOptions{}) + if err != nil { + log.Infof("msg=%s filtername=%s clustername=%s", "creating the envoy filter", envoyFilterName, rc.ClusterID) + filter, err = rc.RoutingPolicyController.IstioClient.NetworkingV1alpha3().EnvoyFilters(common.NamespaceIstioSystem).Create(envoyfilter) + if err != nil { + log.Infof("error creating filter: %v", err) + } + } else { + log.Infof("msg=%s filtername=%s clustername=%s", "updating existing envoy filter", envoyFilterName, rc.ClusterID) + envoyfilter.ResourceVersion = filter.ResourceVersion + filter, err = rc.RoutingPolicyController.IstioClient.NetworkingV1alpha3().EnvoyFilters(common.NamespaceIstioSystem).Update(envoyfilter) + } + } + + + return filter, err +} + +func constructEnvoyFilterStruct(routingPolicy *v1.RoutingPolicy, workloadSelectorLabels map[string]string) *v1alpha3.EnvoyFilter { + var envoyFilterStringConfig string + var wasmPath string + for key, val := range routingPolicy.Spec.Config { + if key == common.WASMPath { + wasmPath = val + continue + } + envoyFilterStringConfig += fmt.Sprintf("%s: %s\n", key, val) + } + if len(common.GetEnvoyFilterAdditionalConfig()) !=0 { + envoyFilterStringConfig += common.GetEnvoyFilterAdditionalConfig()+"\n" + } + envoyFilterStringConfig += getHosts(routingPolicy) + "\n" + envoyFilterStringConfig += getPlugin(routingPolicy) + + log.Infof("msg=%s type=routingpolicy name=%s", "adding config", routingPolicy.Name) + + + configuration := types.Struct{ + Fields: map[string]*types.Value{ + "@type": {Kind: &types.Value_StringValue{StringValue: "type.googleapis.com/google.protobuf.StringValue"}}, + "value": {Kind: &types.Value_StringValue{StringValue: envoyFilterStringConfig}}, + }, + } + + + vmConfig := types.Struct{ + Fields: map[string]*types.Value{ + "runtime": {Kind: &types.Value_StringValue{StringValue: "envoy.wasm.runtime.v8"}}, + "code": {Kind: &types.Value_StructValue{StructValue: &types.Struct{Fields: map[string]*types.Value{ + "local": {Kind: &types.Value_StructValue{StructValue: &types.Struct{Fields: map[string]*types.Value{ + "filename": {Kind: &types.Value_StringValue{StringValue: wasmPath}}, + }}}}, + }}}}, + }, + } + + typedConfigValue := types.Struct{ + Fields: map[string]*types.Value{ + "config": { + Kind: &types.Value_StructValue{ + StructValue: &types.Struct{ + Fields: map[string]*types.Value{ + "configuration": {Kind: &types.Value_StructValue{StructValue: &configuration}}, + "vm_config": {Kind: &types.Value_StructValue{StructValue: &vmConfig}}, + }, + }, + }, + }, + }, + } + + typedConfig := types.Struct{ + Fields: map[string]*types.Value{ + "@type": {Kind: &types.Value_StringValue{StringValue: "type.googleapis.com/udpa.type.v1.TypedStruct"}}, + "type_url": {Kind: &types.Value_StringValue{StringValue: "type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm"}}, + "value": {Kind: &types.Value_StructValue{StructValue: &typedConfigValue}}, + }, + } + + envoyfilterSpec := getEnvoyFilterSpec(workloadSelectorLabels, typedConfig) + return envoyfilterSpec +} + +func getEnvoyFilterSpec(workloadSelectorLabels map[string]string, typedConfig types.Struct) *v1alpha3.EnvoyFilter { + return &v1alpha3.EnvoyFilter{ + WorkloadSelector: &v1alpha3.WorkloadSelector{Labels: workloadSelectorLabels}, + + ConfigPatches: []*v1alpha3.EnvoyFilter_EnvoyConfigObjectPatch{ + { + ApplyTo: v1alpha3.EnvoyFilter_HTTP_FILTER, + Match: &v1alpha3.EnvoyFilter_EnvoyConfigObjectMatch{ + Context: v1alpha3.EnvoyFilter_SIDECAR_OUTBOUND, + // TODO: Figure out the possibility of using this for istio version upgrades. Can we add multiple filters with different proxy version Match here? + Proxy: &v1alpha3.EnvoyFilter_ProxyMatch{ProxyVersion: "^"+strings.ReplaceAll(common.GetEnvoyFilterVersion(),".","\\.")+".*"}, + ObjectTypes: &v1alpha3.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ + Listener: &v1alpha3.EnvoyFilter_ListenerMatch{ + FilterChain: &v1alpha3.EnvoyFilter_ListenerMatch_FilterChainMatch{ + Filter: &v1alpha3.EnvoyFilter_ListenerMatch_FilterMatch{ + Name: "envoy.filters.network.http_connection_manager", + SubFilter: &v1alpha3.EnvoyFilter_ListenerMatch_SubFilterMatch{ + Name: "envoy.filters.http.router", + }, + }, + }, + }, + }, + }, + Patch: &v1alpha3.EnvoyFilter_Patch{ + Operation: v1alpha3.EnvoyFilter_Patch_INSERT_BEFORE, + //https://pkg.go.dev/github.com/gogo/protobuf/types#Value + Value: &types.Struct{ + Fields: map[string]*types.Value{ + "name": {Kind: &types.Value_StringValue{StringValue: "dynamicRoutingFilterPatch"}}, + "typed_config": { + Kind: &types.Value_StructValue{ + StructValue: &typedConfig, + }, + }, + }, + }, + }, + }, + }, + } +} + +func getHosts(routingPolicy *v1.RoutingPolicy) string { + hosts := "" + for _, host := range routingPolicy.Spec.Hosts { + hosts += host + "," + } + hosts = strings.TrimSuffix(hosts,",") + return hostsKey + hosts +} + +func getPlugin(routingPolicy *v1.RoutingPolicy) string { + plugin := routingPolicy.Spec.Plugin + return pluginKey + plugin +} + diff --git a/admiral/pkg/clusters/envoyfilter_test.go b/admiral/pkg/clusters/envoyfilter_test.go new file mode 100644 index 000000000..079140436 --- /dev/null +++ b/admiral/pkg/clusters/envoyfilter_test.go @@ -0,0 +1,172 @@ +package clusters + +import ( + "context" + "errors" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/model" + v1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/admiral" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + "github.com/stretchr/testify/assert" + istiofake "istio.io/client-go/pkg/clientset/versioned/fake" + "istio.io/client-go/pkg/clientset/versioned/typed/networking/v1alpha3/fake" + time2 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + testing2 "k8s.io/client-go/testing" + "sync" + "testing" + "time" +) + +func TestCreateOrUpdateEnvoyFilter(t *testing.T) { + p := common.AdmiralParams{ + KubeconfigPath: "testdata/fake.config", + LabelSet: &common.LabelSet{}, + EnableSAN: true, + SANPrefix: "prefix", + HostnameSuffix: "mesh", + SyncNamespace: "ns", + CacheRefreshDuration: time.Minute, + ClusterRegistriesNamespace: "default", + DependenciesNamespace: "default", + SecretResolver: "", + EnvoyFilterVersion: "1.13", + } + + p.LabelSet.WorkloadIdentityKey = "identity" + p.LabelSet.EnvKey = "admiral.io/env" + p.LabelSet.GlobalTrafficDeploymentLabel = "identity" + + registry, _ := InitAdmiral(context.Background(), p) + + handler := RoutingPolicyHandler{} + + rpFilterCache := &routingPolicyFilterCache{} + rpFilterCache.filterCache = make(map[string]map[string]map[string]string) + rpFilterCache.mutex = &sync.Mutex{} + + routingPolicyController := &admiral.RoutingPolicyController{IstioClient: istiofake.NewSimpleClientset()} + remoteController, _ := createMockRemoteController(func(i interface{}) { + + }) + + remoteController.RoutingPolicyController = routingPolicyController + + registry.remoteControllers = map[string]*RemoteController{"cluster-1": remoteController} + registry.AdmiralCache.RoutingPolicyFilterCache = rpFilterCache + + // foo is dependent upon bar and bar has a deployment in the same cluster. + registry.AdmiralCache.IdentityDependencyCache.Put("foo", "bar", "bar") + registry.AdmiralCache.IdentityClusterCache.Put("bar", remoteController.ClusterID, remoteController.ClusterID) + + + handler.RemoteRegistry = registry + + routingPolicyFoo := &v1.RoutingPolicy{ + TypeMeta: time2.TypeMeta{}, + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{ + "identity": "foo", + "admiral.io/env": "stage", + }, + }, + Spec: model.RoutingPolicy{ + Plugin: "test", + Hosts: []string{"e2e.testservice.mesh"}, + Config: map[string]string{ + "cachePrefix": "cache-v1", + "cachettlSec": "86400", + "routingServiceUrl": "e2e.test.routing.service.mesh", + "pathPrefix": "/sayhello,/v1/company/{id}/", + }, + }, + Status: v1.RoutingPolicyStatus{}, + } + + selectors := map[string]string{"one":"test1", "two":"test2"} + + getSha1 = getSha1Error + + envoyfilter, err := createOrUpdateEnvoyFilter(remoteController, routingPolicyFoo, admiral.Add, "barstage", registry.AdmiralCache, selectors) + + assert.NotNil(t, err) + assert.Nil(t, envoyfilter) + + getSha1 = common.GetSha1 + + envoyfilter, err = createOrUpdateEnvoyFilter(remoteController, routingPolicyFoo, admiral.Add, "bar", registry.AdmiralCache, selectors) + assert.Equal(t, "test1", envoyfilter.Spec.WorkloadSelector.GetLabels()["one"]) + assert.Equal(t, "test2", envoyfilter.Spec.WorkloadSelector.GetLabels()["two"]) + assert.Equal(t, "test-dynamicrouting-d0fdd-1.13", envoyfilter.Name) + + envoyfilter, err = createOrUpdateEnvoyFilter(remoteController, routingPolicyFoo, admiral.Update, "bar", registry.AdmiralCache, selectors) + assert.Nil(t, err) + + + remoteController.RoutingPolicyController.IstioClient.NetworkingV1alpha3().(*fake.FakeNetworkingV1alpha3).PrependReactor("create", "envoyfilters", + func(action testing2.Action) (handled bool, ret runtime.Object, err error) { + return true, nil, errors.New("error creating envoyfilter") + }, + ) + envoyfilter3, err := createOrUpdateEnvoyFilter(remoteController, routingPolicyFoo, admiral.Add, "bar2", registry.AdmiralCache, selectors) + assert.NotNil(t, err) + assert.Nil(t, envoyfilter3) + + +} + +func getSha1Error (key interface{}) (string, error) { + return "", errors.New("error occured while computing the sha") +} + +func TestGetHosts(t *testing.T) { + routingPolicyFoo := &v1.RoutingPolicy{ + TypeMeta: time2.TypeMeta{}, + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{ + "identity": "foo", + "admiral.io/env": "stage", + }, + }, + Spec: model.RoutingPolicy{ + Plugin: "test", + Hosts: []string{"e2e.testservice.mesh,e2e2.testservice.mesh"}, + Config: map[string]string{ + "cachePrefix": "cache-v1", + "cachettlSec": "86400", + "routingServiceUrl": "e2e.test.routing.service.mesh", + "pathPrefix": "/sayhello,/v1/company/{id}/", + }, + }, + Status: v1.RoutingPolicyStatus{}, + } + + hosts := getHosts(routingPolicyFoo) + assert.Equal(t, "hosts: e2e.testservice.mesh,e2e2.testservice.mesh",hosts) +} + +func TestGetPlugin(t *testing.T) { + routingPolicyFoo := &v1.RoutingPolicy{ + TypeMeta: time2.TypeMeta{}, + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{ + "identity": "foo", + "admiral.io/env": "stage", + }, + }, + Spec: model.RoutingPolicy{ + Plugin: "test", + Hosts: []string{"e2e.testservice.mesh,e2e2.testservice.mesh"}, + Config: map[string]string{ + "cachePrefix": "cache-v1", + "cachettlSec": "86400", + "routingServiceUrl": "e2e.test.routing.service.mesh", + "pathPrefix": "/sayhello,/v1/company/{id}/", + }, + }, + Status: v1.RoutingPolicyStatus{}, + } + + plugin := getPlugin(routingPolicyFoo) + assert.Equal(t, "plugin: test",plugin) +} diff --git a/admiral/pkg/clusters/handler.go b/admiral/pkg/clusters/handler.go index 88054814f..55710da35 100644 --- a/admiral/pkg/clusters/handler.go +++ b/admiral/pkg/clusters/handler.go @@ -55,7 +55,7 @@ type WeightedService struct { } func updateIdentityDependencyCache(sourceIdentity string, identityDependencyCache *common.MapOfMaps, dr *v1.Dependency) { - for _, dIdentity := range dr.Spec.Destinations { + for _, dIdentity := range dr.Spec.Destinations { identityDependencyCache.Put(dIdentity, sourceIdentity, sourceIdentity) } log.Infof(LogFormat, "Update", "dependency-cache", dr.Name, "", "Updated=true namespace="+dr.Namespace) diff --git a/admiral/pkg/clusters/registry.go b/admiral/pkg/clusters/registry.go index bd0a5431f..0fd706bec 100644 --- a/admiral/pkg/clusters/registry.go +++ b/admiral/pkg/clusters/registry.go @@ -41,7 +41,7 @@ func InitAdmiral(ctx context.Context, params common.AdmiralParams) (*RemoteRegis var err error wd.DepController, err = admiral.NewDependencyController(ctx.Done(), &wd, params.KubeconfigPath, params.DependenciesNamespace, params.CacheRefreshDuration) if err != nil { - return nil, fmt.Errorf(" Error with dependency controller init: %v", err) + return nil, fmt.Errorf("error with dependency controller init: %v", err) } if !params.ArgoRolloutsEnabled { @@ -50,14 +50,14 @@ func InitAdmiral(ctx context.Context, params common.AdmiralParams) (*RemoteRegis configMapController, err := admiral.NewConfigMapController(params.ServiceEntryIPPrefix) if err != nil { - return nil, fmt.Errorf(" Error with configmap controller init: %v", err) + return nil, fmt.Errorf("error with configmap controller init: %v", err) } w.AdmiralCache.ConfigMapController = configMapController loadServiceEntryCacheData(w.AdmiralCache.ConfigMapController, w.AdmiralCache) err = createSecretController(ctx, w) if err != nil { - return nil, fmt.Errorf(" Error with secret control init: %v", err) + return nil, fmt.Errorf("error with secret control init: %v", err) } go w.shutdown() @@ -188,6 +188,54 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste return fmt.Errorf("error with Rollout controller init: %v", err) } } + + log.Infof("starting Routing Policies controller for custerID: %v", clusterID) + rc.RoutingPolicyController, err = admiral.NewRoutingPoliciesController(stop, &RoutingPolicyHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, 1 * time.Minute) + + if err != nil { + return fmt.Errorf("error with virtualServiceController init: %v", err) + } + + log.Infof("starting node controller clusterID: %v", clusterID) + rc.NodeController, err = admiral.NewNodeController( stop, &NodeHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig) + + if err != nil { + return fmt.Errorf("error with NodeController controller init: %v", err) + } + + log.Infof("starting service controller clusterID: %v", clusterID) + rc.ServiceController, err = admiral.NewServiceController(stop, &ServiceHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, resyncPeriod) + + if err != nil { + return fmt.Errorf("error with ServiceController controller init: %v", err) + } + + log.Infof("starting service entry controller for custerID: %v", clusterID) + rc.ServiceEntryController, err = istio.NewServiceEntryController(stop, &ServiceEntryHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, resyncPeriod) + + if err != nil { + return fmt.Errorf("error with ServiceEntryController init: %v", err) + } + + log.Infof("starting destination rule controller for custerID: %v", clusterID) + rc.DestinationRuleController, err = istio.NewDestinationRuleController(stop, &DestinationRuleHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, resyncPeriod) + + if err != nil { + return fmt.Errorf("error with DestinationRuleController init: %v", err) + } + + log.Infof("starting virtual service controller for custerID: %v", clusterID) + rc.VirtualServiceController, err = istio.NewVirtualServiceController(stop, &VirtualServiceHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, resyncPeriod) + + if err != nil { + return fmt.Errorf("error with VirtualServiceController init: %v", err) + } + + rc.SidecarController, err = istio.NewSidecarController(stop, &SidecarHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, resyncPeriod) + + if err != nil { + return fmt.Errorf("error with DestinationRuleController init: %v", err) + } r.PutRemoteController(clusterID, &rc) diff --git a/admiral/pkg/clusters/registry_test.go b/admiral/pkg/clusters/registry_test.go index 80687e5d0..5e02a666f 100644 --- a/admiral/pkg/clusters/registry_test.go +++ b/admiral/pkg/clusters/registry_test.go @@ -36,10 +36,14 @@ func init() { SecretResolver: "", WorkloadSidecarUpdate: "enabled", WorkloadSidecarName: "default", + EnableRoutingPolicy: true, + EnvoyFilterVersion: "1.13", } p.LabelSet.WorkloadIdentityKey = "identity" p.LabelSet.GlobalTrafficDeploymentLabel = "identity" + p.LabelSet.EnvKey = "admiral.io/env" + common.InitializeConfig(p) } @@ -131,13 +135,25 @@ func createMockRemoteController(f func(interface{})) (*RemoteController, error) Host: "localhost", } stop := make(chan struct{}) - d, e := admiral.NewDeploymentController(stop, &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300)) - s, e := admiral.NewServiceController(stop, &test.MockServiceHandler{}, &config, time.Second*time.Duration(300)) - n, e := admiral.NewNodeController(stop, &test.MockNodeHandler{}, &config) - r, e := admiral.NewRolloutsController(stop, &test.MockRolloutHandler{}, &config, time.Second*time.Duration(300)) - - if e != nil { - return nil, e + d, err := admiral.NewDeploymentController(stop, &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300)) + if err != nil { + return nil, err + } + s, err := admiral.NewServiceController(stop, &test.MockServiceHandler{}, &config, time.Second*time.Duration(300)) + if err != nil { + return nil, err + } + n, err := admiral.NewNodeController( stop, &test.MockNodeHandler{}, &config) + if err != nil { + return nil, err + } + r, err := admiral.NewRolloutsController(stop, &test.MockRolloutHandler{}, &config, time.Second*time.Duration(300)) + if err != nil { + return nil, err + } + rpc, err := admiral.NewRoutingPoliciesController(stop, &test.MockRoutingPolicyHandler{}, &config, time.Second*time.Duration(300)) + if err != nil { + return nil, err } deployment := k8sAppsV1.Deployment{ @@ -177,6 +193,7 @@ func createMockRemoteController(f func(interface{})) (*RemoteController, error) NodeController: n, ClusterID: "test.cluster", RolloutController: r, + RoutingPolicyController: rpc, } return &rc, nil } diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index 0706fbb72..fd363faad 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -154,7 +154,14 @@ func modifyServiceEntryForNewServiceOrPod(event admiral.EventType, env string, s } else { log.Debugf("No GTPs found for identity=%s in env=%s namespace=%s with key=%s", sourceIdentity, env, namespace, gtpKey) } - + + remoteRegistry.AdmiralCache.IdentityClusterCache.Put(sourceIdentity, rc.ClusterID, rc.ClusterID) + // workload selector cache is needed for routingPolicy's envoyFilter to match the dependency and apply to the right POD + // using service labels + workloadSelectors := GetServiceSelector(rc.ClusterID, serviceInstance) + if workloadSelectors != nil { + remoteRegistry.AdmiralCache.WorkloadSelectorCache.PutMap(sourceIdentity+rc.ClusterID, workloadSelectors) + } remoteRegistry.AdmiralCache.CnameClusterCache.Put(cname, rc.ClusterID, rc.ClusterID) remoteRegistry.AdmiralCache.CnameIdentityCache.Store(cname, sourceIdentity) sourceServices[rc.ClusterID] = serviceInstance diff --git a/admiral/pkg/clusters/serviceentry_test.go b/admiral/pkg/clusters/serviceentry_test.go index fc9648163..baa67e343 100644 --- a/admiral/pkg/clusters/serviceentry_test.go +++ b/admiral/pkg/clusters/serviceentry_test.go @@ -887,8 +887,9 @@ func TestCreateServiceEntryForNewServiceOrPodRolloutsUsecase(t *testing.T) { GlobalTrafficCache: &globalTrafficCache{ mutex: &sync.Mutex{}, }, - DependencyNamespaceCache: common.NewSidecarEgressMap(), - SeClusterCache: common.NewMapOfMaps(), + DependencyNamespaceCache: common.NewSidecarEgressMap(), + SeClusterCache: common.NewMapOfMaps(), + WorkloadSelectorCache: common.NewMapOfMaps(), } rr.AdmiralCache = admiralCache @@ -1025,8 +1026,9 @@ func TestCreateServiceEntryForBlueGreenRolloutsUsecase(t *testing.T) { GlobalTrafficCache: &globalTrafficCache{ mutex: &sync.Mutex{}, }, - DependencyNamespaceCache: common.NewSidecarEgressMap(), - SeClusterCache: common.NewMapOfMaps(), + DependencyNamespaceCache: common.NewSidecarEgressMap(), + SeClusterCache: common.NewMapOfMaps(), + WorkloadSelectorCache: common.NewMapOfMaps(), } rr.AdmiralCache = admiralCache diff --git a/admiral/pkg/clusters/types.go b/admiral/pkg/clusters/types.go index a8b01c928..777cbdbb0 100644 --- a/admiral/pkg/clusters/types.go +++ b/admiral/pkg/clusters/types.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sync" "time" @@ -32,6 +33,7 @@ type RemoteController struct { VirtualServiceController *istio.VirtualServiceController SidecarController *istio.SidecarController RolloutController *admiral.RolloutController + RoutingPolicyController *admiral.RoutingPolicyController stop chan struct{} //listener for normal types } @@ -41,6 +43,7 @@ type AdmiralCache struct { CnameDependentClusterCache *common.MapOfMaps CnameIdentityCache *sync.Map IdentityClusterCache *common.MapOfMaps + WorkloadSelectorCache *common.MapOfMaps ClusterLocalityCache *common.MapOfMaps IdentityDependencyCache *common.MapOfMaps SubsetServiceEntryIdentityCache *sync.Map @@ -49,7 +52,8 @@ type AdmiralCache struct { GlobalTrafficCache GlobalTrafficCache //The cache needs to live in the handler because it needs access to deployments DependencyNamespaceCache *common.SidecarEgressMap SeClusterCache *common.MapOfMaps - + RoutingPolicyFilterCache *routingPolicyFilterCache + RoutingPolicyCache *routingPolicyCache argoRolloutsEnabled bool } @@ -67,13 +71,21 @@ func NewRemoteRegistry(ctx context.Context, params common.AdmiralParams) *Remote gtpCache := &globalTrafficCache{} gtpCache.identityCache = make(map[string]*v1.GlobalTrafficPolicy) gtpCache.mutex = &sync.Mutex{} - + rpFilterCache := &routingPolicyFilterCache{} + rpFilterCache.filterCache = make(map[string]map[string]map[string]string) + rpFilterCache.mutex = &sync.Mutex{} + rpCache := &routingPolicyCache{} + rpCache.identityCache = make(map[string]*v1.RoutingPolicy) + rpCache.mutex = &sync.Mutex{} admiralCache := &AdmiralCache{ IdentityClusterCache: common.NewMapOfMaps(), CnameClusterCache: common.NewMapOfMaps(), CnameDependentClusterCache: common.NewMapOfMaps(), ClusterLocalityCache: common.NewMapOfMaps(), IdentityDependencyCache: common.NewMapOfMaps(), + WorkloadSelectorCache: common.NewMapOfMaps(), + RoutingPolicyFilterCache: rpFilterCache, + RoutingPolicyCache: rpCache, DependencyNamespaceCache: common.NewSidecarEgressMap(), CnameIdentityCache: &sync.Map{}, SubsetServiceEntryIdentityCache: &sync.Map{}, @@ -187,7 +199,7 @@ func (g *globalTrafficCache) Put(gtp *v1.GlobalTrafficPolicy) error { var gtpIdentity = gtp.Labels[common.GetGlobalTrafficDeploymentLabel()] var gtpEnv = common.GetGtpEnv(gtp) - log.Infof("Adding GTP with name %v to GTP cache. LabelMatch=%v env=%v", gtp.Name, gtpIdentity, gtpEnv) + log.Infof("adding GTP with name %v to GTP cache. LabelMatch=%v env=%v", gtp.Name, gtpIdentity, gtpEnv) identity := gtp.Labels[common.GetGlobalTrafficDeploymentLabel()] key := common.ConstructGtpKey(gtpEnv, identity) g.identityCache[key] = gtp @@ -198,12 +210,203 @@ func (g *globalTrafficCache) Put(gtp *v1.GlobalTrafficPolicy) error { func (g *globalTrafficCache) Delete(identity string, environment string) error { key := common.ConstructGtpKey(environment, identity) if _, ok := g.identityCache[key]; ok { - log.Infof("Deleting gtp with key=%s from global GTP cache", key) + log.Infof("deleting gtp with key=%s from global GTP cache", key) delete(g.identityCache, key) } return fmt.Errorf("gtp with key %s not found in cache", key) } +type RoutingPolicyHandler struct { + RemoteRegistry *RemoteRegistry + ClusterID string +} + +type routingPolicyCache struct { + // map of routing policies key=environment.identity, value: RoutingPolicy object + // only one routing policy per identity + env is allowed + identityCache map[string]*v1.RoutingPolicy + mutex *sync.Mutex +} + + +func (r *routingPolicyCache) Delete(identity string, environment string) { + defer r.mutex.Unlock() + r.mutex.Lock() + key := common.ConstructRoutingPolicyKey(environment, identity) + if _, ok := r.identityCache[key]; ok { + log.Infof("deleting RoutingPolicy with key=%s from global RoutingPolicy cache", key) + delete(r.identityCache, key) + } +} + +func (r *routingPolicyCache ) GetFromIdentity(identity string, environment string) *v1.RoutingPolicy { + defer r.mutex.Unlock() + r.mutex.Lock() + return r.identityCache[common.ConstructRoutingPolicyKey(environment, identity)] +} + +func (r *routingPolicyCache) Put(rp *v1.RoutingPolicy) error { + if rp == nil || rp.Name == "" { + // no RoutingPolicy, throw error + return errors.New("cannot add an empty RoutingPolicy to the cache") + } + if rp.Labels == nil { + return errors.New("labels empty in RoutingPolicy") + } + defer r.mutex.Unlock() + r.mutex.Lock() + var rpIdentity = rp.Labels[common.GetRoutingPolicyLabel()] + var rpEnv = common.GetRoutingPolicyEnv(rp) + + log.Infof("Adding RoutingPolicy with name %v to RoutingPolicy cache. LabelMatch=%v env=%v", rp.Name, rpIdentity, rpEnv) + key := common.ConstructRoutingPolicyKey(rpEnv, rpIdentity) + r.identityCache[key] = rp + + return nil +} + + +type routingPolicyFilterCache struct { + // map of envoyFilters key=environment+identity of the routingPolicy, value is a map [clusterId -> map [filterName -> filterName]] + filterCache map[string]map[string]map[string]string + mutex *sync.Mutex +} + +func (r *routingPolicyFilterCache) Get(identityEnvKey string) (filters map[string]map[string]string) { + defer r.mutex.Unlock() + r.mutex.Lock() + return r.filterCache[identityEnvKey] +} + +func (r *routingPolicyFilterCache) Put(identityEnvKey string, clusterId string, filterName string) { + defer r.mutex.Unlock() + r.mutex.Lock() + if r.filterCache[identityEnvKey] == nil { + r.filterCache[identityEnvKey] = make(map[string]map[string]string) + } + + if r.filterCache[identityEnvKey][clusterId] == nil { + r.filterCache[identityEnvKey][clusterId] = make(map[string]string) + } + r.filterCache[identityEnvKey][clusterId][filterName] = filterName +} + +func (r *routingPolicyFilterCache) Delete(identityEnvKey string) { + if common.GetEnableRoutingPolicy() { + defer r.mutex.Unlock() + r.mutex.Lock() + // delete all envoyFilters for a given identity+env key + delete(r.filterCache, identityEnvKey) + }else { + log.Infof(LogFormat, admiral.Delete, "routingpolicy", identityEnvKey, "", "routingpolicy disabled") + } +} +func (r RoutingPolicyHandler) Added(obj *v1.RoutingPolicy) { + if common.GetEnableRoutingPolicy() { + if common.ShouldIgnoreResource(obj.ObjectMeta) { + log.Infof(LogFormat, "success", "routingpolicy", obj.Name, obj.ClusterName, "Ignored the RoutingPolicy because of the annotation") + return + } + dependents := getDependents(obj, r) + if len(dependents) == 0 { + log.Info("No dependents found for Routing Policy - ", obj.Name) + return + } + r.processroutingPolicy(dependents, obj, admiral.Add) + + log.Infof(LogFormat, admiral.Add, "routingpolicy", obj.Name, obj.ClusterName, "finished processing routing policy") + }else { + log.Infof(LogFormat, admiral.Add, "routingpolicy", obj.Name, obj.ClusterName, "routingpolicy disabled") + } +} + +func (r RoutingPolicyHandler) processroutingPolicy(dependents map[string]string, routingPolicy *v1.RoutingPolicy, eventType admiral.EventType ) { + for _, remoteController := range r.RemoteRegistry.remoteControllers { + for _, dependent := range dependents { + + // Check if the dependent exists in this remoteCluster. If so, we create an envoyFilter with dependent identity as workload selector + if _, ok := r.RemoteRegistry.AdmiralCache.IdentityClusterCache.Get(dependent).Copy()[remoteController.ClusterID]; ok { + selectors := r.RemoteRegistry.AdmiralCache.WorkloadSelectorCache.Get(dependent+remoteController.ClusterID).Copy() + if len(selectors) != 0 { + + filter, err := createOrUpdateEnvoyFilter(remoteController, routingPolicy, eventType, dependent, r.RemoteRegistry.AdmiralCache, selectors) + if err != nil { + // Best effort create + log.Errorf(LogErrFormat, eventType, "routingpolicy", routingPolicy.Name, remoteController.ClusterID, err) + } else { + log.Infof("msg=%s name=%s cluster=%s", "created envoyfilter", filter.Name, remoteController.ClusterID) + } + } + } + } + + } +} + +func (r RoutingPolicyHandler) Updated(obj *v1.RoutingPolicy) { + if common.GetEnableRoutingPolicy() { + if common.ShouldIgnoreResource(obj.ObjectMeta) { + log.Infof(LogFormat, admiral.Update, "routingpolicy", obj.Name, obj.ClusterName, "Ignored the RoutingPolicy because of the annotation") + // We need to process this as a delete event. + r.Deleted(obj) + return + } + dependents := getDependents(obj, r) + if len(dependents) == 0 { + return + } + r.processroutingPolicy(dependents, obj, admiral.Update) + + log.Infof(LogFormat, admiral.Update, "routingpolicy", obj.Name, obj.ClusterName, "updated routing policy") + }else { + log.Infof(LogFormat, admiral.Update, "routingpolicy", obj.Name, obj.ClusterName, "routingpolicy disabled") + } +} + +// getDependents - Returns the client dependents for the destination service with routing policy +// Returns a list of asset ID's of the client services or nil if no dependents are found +func getDependents(obj *v1.RoutingPolicy, r RoutingPolicyHandler) map[string]string { + sourceIdentity := common.GetRoutingPolicyIdentity(obj) + if len(sourceIdentity) == 0 { + err := errors.New("identity label is missing") + log.Warnf(LogErrFormat, "add", "RoutingPolicy", obj.Name, r.ClusterID, err) + return nil + } + + dependents := r.RemoteRegistry.AdmiralCache.IdentityDependencyCache.Get(sourceIdentity).Copy() + return dependents +} + +func (r RoutingPolicyHandler) Deleted(obj *v1.RoutingPolicy) { + dependents := getDependents(obj, r) + if len(dependents) != 0 { + r.deleteEnvoyFilters(dependents, obj, admiral.Delete) + log.Infof(LogFormat, admiral.Delete, "routingpolicy", obj.Name, obj.ClusterName, "deleted envoy filter for routing policy") + } +} + +func (r RoutingPolicyHandler) deleteEnvoyFilters(dependents map[string]string, obj *v1.RoutingPolicy, eventType admiral.EventType) { + for _, dependent := range dependents { + key := dependent + common.GetRoutingPolicyEnv(obj) + clusterIdFilterMap := r.RemoteRegistry.AdmiralCache.RoutingPolicyFilterCache.Get(key) + for _, rc := range r.RemoteRegistry.remoteControllers { + if filterMap, ok := clusterIdFilterMap[rc.ClusterID]; ok { + for _, filter := range filterMap { + log.Infof(LogFormat, eventType, "envoyfilter", filter, rc.ClusterID, "deleting") + err := rc.RoutingPolicyController.IstioClient.NetworkingV1alpha3().EnvoyFilters("istio-system").Delete(filter, &metaV1.DeleteOptions{}) + if err != nil { + // Best effort delete + log.Errorf(LogErrFormat, eventType, "envoyfilter", filter, rc.ClusterID, err) + } else { + log.Infof(LogFormat, eventType, "envoyfilter", filter, rc.ClusterID, "deleting from cache") + r.RemoteRegistry.AdmiralCache.RoutingPolicyFilterCache.Delete(key) + } + } + } + } + } +} + type DeploymentHandler struct { RemoteRegistry *RemoteRegistry ClusterID string diff --git a/admiral/pkg/clusters/types_test.go b/admiral/pkg/clusters/types_test.go index 53658f263..01331b1ea 100644 --- a/admiral/pkg/clusters/types_test.go +++ b/admiral/pkg/clusters/types_test.go @@ -2,6 +2,10 @@ package clusters import ( "context" + "fmt" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/model" + istiofake "istio.io/client-go/pkg/clientset/versioned/fake" + "strings" "sync" "testing" "time" @@ -32,9 +36,12 @@ func init() { ClusterRegistriesNamespace: "default", DependenciesNamespace: "default", SecretResolver: "", + EnableRoutingPolicy: true, + EnvoyFilterVersion: "1.13", } p.LabelSet.WorkloadIdentityKey = "identity" + p.LabelSet.EnvKey = "admiral.io/env" p.LabelSet.GlobalTrafficDeploymentLabel = "identity" common.InitializeConfig(p) @@ -257,5 +264,195 @@ func TestHandleEventForGlobalTrafficPolicy(t *testing.T) { assert.Equal(t, err != nil, c.doesError) }) } +} + + +func TestRoutingPolicyHandler(t *testing.T) { + p := common.AdmiralParams{ + KubeconfigPath: "testdata/fake.config", + LabelSet: &common.LabelSet{}, + EnableSAN: true, + SANPrefix: "prefix", + HostnameSuffix: "mesh", + SyncNamespace: "ns", + CacheRefreshDuration: time.Minute, + ClusterRegistriesNamespace: "default", + DependenciesNamespace: "default", + SecretResolver: "", + EnableRoutingPolicy: true, + EnvoyFilterVersion: "1.13", + } + + p.LabelSet.WorkloadIdentityKey = "identity" + p.LabelSet.EnvKey = "admiral.io/env" + p.LabelSet.GlobalTrafficDeploymentLabel = "identity" + + registry, _ := InitAdmiral(context.Background(), p) + + handler := RoutingPolicyHandler{} + + rpFilterCache := &routingPolicyFilterCache{} + rpFilterCache.filterCache = make(map[string]map[string]map[string]string) + rpFilterCache.mutex = &sync.Mutex{} + + + routingPolicyController := &admiral.RoutingPolicyController{IstioClient: istiofake.NewSimpleClientset()} + remoteController, _ := createMockRemoteController(func(i interface{}) { + + }) + remoteController.RoutingPolicyController = routingPolicyController + + registry.remoteControllers = map[string]*RemoteController{"cluster-1": remoteController} + registry.AdmiralCache.RoutingPolicyFilterCache = rpFilterCache + + // foo is dependent upon bar and bar has a deployment in the same cluster. + registry.AdmiralCache.IdentityDependencyCache.Put("foo", "bar", "bar") + registry.AdmiralCache.IdentityClusterCache.Put("bar", remoteController.ClusterID, remoteController.ClusterID) + + + // foo is also dependent upon bar2 but bar2 is in a different cluster, so this cluster should not have the envoyfilter created + registry.AdmiralCache.IdentityDependencyCache.Put("foo", "bar2", "bar2") + registry.AdmiralCache.IdentityClusterCache.Put("bar2", "differentCluster", "differentCluster") + + // foo1 is dependent upon bar 1 but bar1 does not have a deployment so it is missing from identityClusterCache + registry.AdmiralCache.IdentityDependencyCache.Put("foo1", "bar1", "bar1") + + var mp = common.NewMap() + mp.Put("k1","v1") + registry.AdmiralCache.WorkloadSelectorCache.PutMap("bar"+remoteController.ClusterID, mp) + registry.AdmiralCache.WorkloadSelectorCache.PutMap("bar2differentCluster", mp) + + handler.RemoteRegistry = registry + + routingPolicyFoo := &v1.RoutingPolicy{ + TypeMeta: time2.TypeMeta{}, + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{ + "identity": "foo", + "admiral.io/env": "stage", + }, + }, + Spec: model.RoutingPolicy{ + Plugin: "test", + Hosts: []string{"e2e.testservice.mesh"}, + Config: map[string]string{ + "cachePrefix": "cache-v1", + "cachettlSec": "86400", + "routingServiceUrl": "e2e.test.routing.service.mesh", + "pathPrefix": "/sayhello,/v1/company/{id}/", + }, + }, + Status: v1.RoutingPolicyStatus{}, + } + + + routingPolicyFoo1 := routingPolicyFoo.DeepCopy() + routingPolicyFoo1.Labels[common.GetWorkloadIdentifier()] = "foo1" + + + testCases := []struct { + name string + routingPolicy *v1.RoutingPolicy + expectedFilterCacheKey string + valueExpected bool + + } { + { + name: "If dependent deployment exists, should fetch filter from cache", + routingPolicy: routingPolicyFoo, + expectedFilterCacheKey: "barstage", + valueExpected: true, + }, + { + name: "If dependent deployment does not exist, the filter should not be created", + routingPolicy: routingPolicyFoo1, + expectedFilterCacheKey: "bar1stage", + valueExpected: false, + }, + { + name: "If dependent deployment exists in a different cluster, the filter should not be created", + routingPolicy: routingPolicyFoo, + expectedFilterCacheKey: "bar2stage", + valueExpected: false, + }, + + } + + time.Sleep(time.Second*30) + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + handler.Added(c.routingPolicy) + if c.valueExpected { + filterCacheValue := registry.AdmiralCache.RoutingPolicyFilterCache.Get(c.expectedFilterCacheKey) + assert.NotNil(t, filterCacheValue) + selectorLabelsSha, err := common.GetSha1("bar"+common.GetRoutingPolicyEnv(c.routingPolicy)) + if err != nil { + t.Error("Error ocurred while computing workload Labels sha1") + } + envoyFilterName := fmt.Sprintf("%s-dynamicrouting-%s-%s", strings.ToLower(c.routingPolicy.Spec.Plugin), selectorLabelsSha, "1.13") + filterMap := filterCacheValue[remoteController.ClusterID] + assert.NotNil(t, filterMap) + assert.NotNil(t, filterMap[envoyFilterName]) + + // once the routing policy is deleted, the corresponding filter should also be deleted + handler.Deleted(c.routingPolicy) + assert.Nil(t, registry.AdmiralCache.RoutingPolicyFilterCache.Get(c.expectedFilterCacheKey)) + } else { + assert.Nil(t, registry.AdmiralCache.RoutingPolicyFilterCache.Get(c.expectedFilterCacheKey)) + } + + }) + } + + + + // Test for multiple filters + registry.AdmiralCache.IdentityDependencyCache.Put("foo", "bar3", "bar3") + registry.AdmiralCache.IdentityClusterCache.Put("bar3", remoteController.ClusterID, remoteController.ClusterID) + registry.AdmiralCache.WorkloadSelectorCache.PutMap("bar3"+remoteController.ClusterID, mp) + handler.Added(routingPolicyFoo) + + selectorLabelsShaBar3, err := common.GetSha1("bar3"+common.GetRoutingPolicyEnv(routingPolicyFoo)) + if err != nil { + t.Error("Error ocurred while computing workload Labels sha1") + } + envoyFilterNameBar3 := fmt.Sprintf("%s-dynamicrouting-%s-%s", strings.ToLower(routingPolicyFoo.Spec.Plugin), selectorLabelsShaBar3, "1.13") + + filterCacheValue := registry.AdmiralCache.RoutingPolicyFilterCache.Get("bar3stage") + assert.NotNil(t, filterCacheValue) + filterMap := filterCacheValue[remoteController.ClusterID] + assert.NotNil(t, filterMap) + assert.NotNil(t, filterMap[envoyFilterNameBar3]) + + + registry.AdmiralCache.IdentityDependencyCache.Put("foo", "bar4", "bar4") + registry.AdmiralCache.IdentityClusterCache.Put("bar4", remoteController.ClusterID, remoteController.ClusterID) + registry.AdmiralCache.WorkloadSelectorCache.PutMap("bar4"+remoteController.ClusterID, mp) + handler.Updated(routingPolicyFoo) + + selectorLabelsShaBar4, err := common.GetSha1("bar4"+common.GetRoutingPolicyEnv(routingPolicyFoo)) + if err != nil { + t.Error("Error ocurred while computing workload Labels sha1") + } + envoyFilterNameBar4 := fmt.Sprintf("%s-dynamicrouting-%s-%s", strings.ToLower(routingPolicyFoo.Spec.Plugin), selectorLabelsShaBar4, "1.13") + + filterCacheValue = registry.AdmiralCache.RoutingPolicyFilterCache.Get("bar4stage") + assert.NotNil(t, filterCacheValue) + filterMap = filterCacheValue[remoteController.ClusterID] + assert.NotNil(t, filterMap) + assert.NotNil(t, filterMap[envoyFilterNameBar4]) + + // ignore the routing policy + annotations := routingPolicyFoo.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + annotations[common.AdmiralIgnoreAnnotation] = "true" + routingPolicyFoo.SetAnnotations(annotations) + + handler.Updated(routingPolicyFoo) + assert.Nil(t, registry.AdmiralCache.RoutingPolicyFilterCache.Get("bar4stage")) + assert.Nil(t, registry.AdmiralCache.RoutingPolicyFilterCache.Get("bar3stage")) } + diff --git a/admiral/pkg/clusters/util.go b/admiral/pkg/clusters/util.go index 90e3bad45..e21e30014 100644 --- a/admiral/pkg/clusters/util.go +++ b/admiral/pkg/clusters/util.go @@ -48,6 +48,21 @@ func GetMeshPortsForRollout(clusterName string, destService *k8sV1.Service, return ports } +// Get the service selector to add as workload selector for envoyFilter +func GetServiceSelector(clusterName string, destService *k8sV1.Service) *common.Map { + var selectors = destService.Spec.Selector + if len(selectors) == 0{ + log.Infof(LogFormat, "GetServiceLabels", "no selectors present", destService.Name, clusterName, selectors) + return nil + } + var tempMap = common.NewMap() + for key, value := range selectors { + tempMap.Put(key,value) + } + log.Infof(LogFormat, "GetServiceLabels", "selectors present", destService.Name, clusterName, selectors) + return tempMap +} + func getMeshPortsHelper(meshPorts string, destService *k8sV1.Service, clusterName string) map[string]uint32 { var ports = make(map[string]uint32) diff --git a/admiral/pkg/clusters/util_test.go b/admiral/pkg/clusters/util_test.go index 216a483b6..d418177f4 100644 --- a/admiral/pkg/clusters/util_test.go +++ b/admiral/pkg/clusters/util_test.go @@ -6,13 +6,12 @@ import ( "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" k8sAppsV1 "k8s.io/api/apps/v1" coreV1 "k8s.io/api/core/v1" + k8sV1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "reflect" "strconv" "testing" - - k8sV1 "k8s.io/api/core/v1" ) func TestGetMeshPorts(t *testing.T) { @@ -215,6 +214,59 @@ func TestValidateConfigmapBeforePutting(t *testing.T) { } +func TestGetServiceSelector(t *testing.T) { + + selector := map[string]string {"app":"test1"} + + testCases := []struct { + name string + clusterName string + service k8sV1.Service + expected map[string]string + }{ + { + name: "should return a selectors based on service", + clusterName: "test-cluster", + service: k8sV1.Service{ + ObjectMeta: v1.ObjectMeta{Name: "server", Labels: map[string]string{"asset": "Intuit.platform.mesh.server"}}, + Spec: k8sV1.ServiceSpec{Selector: selector}, + }, + expected: selector, + }, + { + name: "should return empty selectors", + clusterName: "test-cluster", + service: k8sV1.Service{ + ObjectMeta: v1.ObjectMeta{Name: "server", Labels: map[string]string{"asset": "Intuit.platform.mesh.server"}}, + Spec: k8sV1.ServiceSpec{Selector: map[string]string{}}, + }, + expected: nil, + }, + { + name: "should return nil", + clusterName: "test-cluster", + service: k8sV1.Service{ + ObjectMeta: v1.ObjectMeta{Name: "server", Labels: map[string]string{"asset": "Intuit.platform.mesh.server"}}, + Spec: k8sV1.ServiceSpec{Selector: nil}, + }, + expected: nil, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + selectors := GetServiceSelector(c.clusterName,&c.service) + if selectors == nil { + if c.expected != nil { + t.Errorf("Wanted selectors: %v, got: %v", c.expected, selectors) + } + }else if !reflect.DeepEqual(selectors.Copy(), c.expected) { + t.Errorf("Wanted selectors: %v, got: %v", c.expected, selectors) + } + }) + } +} + func TestGetMeshPortsForRollout(t *testing.T) { annotatedPort := 8090 diff --git a/admiral/pkg/controller/admiral/routingpolicy.go b/admiral/pkg/controller/admiral/routingpolicy.go new file mode 100644 index 000000000..c16f99a34 --- /dev/null +++ b/admiral/pkg/controller/admiral/routingpolicy.go @@ -0,0 +1,90 @@ + +package admiral + +import ( + "fmt" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + clientset "github.com/istio-ecosystem/admiral/admiral/pkg/client/clientset/versioned" + informerV1 "github.com/istio-ecosystem/admiral/admiral/pkg/client/informers/externalversions/admiral/v1" + "istio.io/client-go/pkg/clientset/versioned" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" + "time" +) + +// Handler interface contains the methods that are required +type RoutingPolicyHandler interface { + Added(obj *v1.RoutingPolicy) + Updated(obj *v1.RoutingPolicy) + Deleted(obj *v1.RoutingPolicy) +} + +type RoutingPolicyEntry struct { + Identity string + RoutingPolicy *v1.RoutingPolicy +} + +type RoutingPolicyClusterEntry struct { + Identity string + RoutingPolicies map[string]*v1.RoutingPolicy +} + +type RoutingPolicyController struct { + K8sClient kubernetes.Interface + CrdClient clientset.Interface + IstioClient versioned.Interface + RoutingPolicyHandler RoutingPolicyHandler + informer cache.SharedIndexInformer +} + + +func (r *RoutingPolicyController) Added(obj interface{}) { + routingPolicy := obj.(*v1.RoutingPolicy) + r.RoutingPolicyHandler.Added(routingPolicy) +} + +func (r *RoutingPolicyController) Updated(obj interface{}, oldObj interface{}) { + routingPolicy := obj.(*v1.RoutingPolicy) + r.RoutingPolicyHandler.Updated(routingPolicy) +} + +func (r *RoutingPolicyController) Deleted(obj interface{}) { + routingPolicy := obj.(*v1.RoutingPolicy) + r.RoutingPolicyHandler.Deleted(routingPolicy) +} + +func NewRoutingPoliciesController(stopCh <-chan struct{}, handler RoutingPolicyHandler, configPath *rest.Config, resyncPeriod time.Duration) (*RoutingPolicyController, error) { + + rpController := RoutingPolicyController{} + rpController.RoutingPolicyHandler = handler + + var err error + + rpController.K8sClient, err = K8sClientFromConfig(configPath) + if err != nil { + return nil, fmt.Errorf("failed to create routing policy controller k8s client: %v", err) + } + + rpController.CrdClient, err = AdmiralCrdClientFromConfig(configPath) + if err != nil { + return nil, fmt.Errorf("failed to create routing policy controller crd client: %v", err) + } + + rpController.IstioClient, err = versioned.NewForConfig(configPath) + if err != nil { + return nil, fmt.Errorf("failed to create destination rule controller k8s client: %v", err) + } + + rpController.informer = informerV1.NewRoutingPolicyInformer( + rpController.CrdClient, + meta_v1.NamespaceAll, + resyncPeriod, + cache.Indexers{}, + ) + + NewController("rp-ctrl-" + configPath.Host, stopCh, &rpController, rpController.informer) + return &rpController, nil + +} diff --git a/admiral/pkg/controller/admiral/routingpolicy_test.go b/admiral/pkg/controller/admiral/routingpolicy_test.go new file mode 100644 index 000000000..6219f953f --- /dev/null +++ b/admiral/pkg/controller/admiral/routingpolicy_test.go @@ -0,0 +1,97 @@ +package admiral + +import ( + "github.com/google/go-cmp/cmp" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/model" + v1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + "github.com/istio-ecosystem/admiral/admiral/pkg/test" + v12 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/clientcmd" + "testing" + "time" +) + + +func TestNewroutingPolicyController(t *testing.T) { + config, err := clientcmd.BuildConfigFromFlags("", "../../test/resources/admins@fake-cluster.k8s.local") + if err != nil { + t.Errorf("%v", err) + } + stop := make(chan struct{}) + handler := test.MockRoutingPolicyHandler{} + + routingPolicyController, err := NewRoutingPoliciesController(stop, &handler, config, time.Duration(1000)) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if routingPolicyController == nil { + t.Errorf("RoutingPolicy controller should never be nil without an error thrown") + } +} + + +func TestRoutingPolicyAddUpdateDelete(t *testing.T) { + config, err := clientcmd.BuildConfigFromFlags("", "../../test/resources/admins@fake-cluster.k8s.local") + if err != nil { + t.Errorf("%v", err) + } + stop := make(chan struct{}) + handler := test.MockRoutingPolicyHandler{} + routingPolicyController, err := NewRoutingPoliciesController(stop, &handler, config, time.Duration(1000)) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if routingPolicyController == nil { + t.Errorf("RoutingPolicy controller should never be nil without an error thrown") + } + + rpName := "greetingRoutingPolicy" + rp := model.RoutingPolicy{ + Config: map[string]string{"cacheTTL": "86400", "dispatcherUrl": "stage.greeting.router.mesh", "pathPrefix": "/hello,/hello/v2/"}, + Plugin: "greeting", + Hosts: []string{"stage.greeting.mesh"}, + } + + rpObj := makeK8sRoutingPolicyObj(rpName, "namespace1", rp) + routingPolicyController.Added(rpObj) + + if !cmp.Equal(handler.Obj.Spec, rpObj.Spec) { + t.Errorf("Add should call the handler with the object") + } + + updatedrp := model.RoutingPolicy{ + Config: map[string]string{"cacheTTL": "86400", "dispatcherUrl": "e2e.greeting.router.mesh", "pathPrefix": "/hello,/hello/v2/"}, + Plugin: "greeting", + Hosts: []string{"e2e.greeting.mesh"}, + } + + updatedRpObj := makeK8sRoutingPolicyObj(rpName, "namespace1", updatedrp) + + routingPolicyController.Updated(updatedRpObj, rpObj) + + if !cmp.Equal(handler.Obj.Spec, updatedRpObj.Spec) { + t.Errorf("Update should call the handler with the updated object") + } + + routingPolicyController.Deleted(updatedRpObj) + + if handler.Obj != nil { + t.Errorf("Delete should delete the routing policy") + } + +} + + +func makeK8sRoutingPolicyObj(name string, namespace string, rp model.RoutingPolicy) *v1.RoutingPolicy { + return &v1.RoutingPolicy{ + Spec: rp, + ObjectMeta: v12.ObjectMeta{Name: name, Namespace: namespace}, + TypeMeta: v12.TypeMeta{ + APIVersion: "admiral.io/v1", + Kind: "RoutingPolicy", + }} +} diff --git a/admiral/pkg/controller/common/common.go b/admiral/pkg/controller/common/common.go index d4160fbdf..e94d75115 100644 --- a/admiral/pkg/controller/common/common.go +++ b/admiral/pkg/controller/common/common.go @@ -1,6 +1,10 @@ package common import ( + "bytes" + "crypto/sha1" + "encoding/gob" + "encoding/hex" "fmt" v12 "k8s.io/apimachinery/pkg/apis/meta/v1" "strings" @@ -36,8 +40,9 @@ const ( AdmiralCnameCaseSensitive = "admiral.io/cname-case-sensitive" BlueGreenRolloutPreviewPrefix = "preview" RolloutPodHashLabel = "rollouts-pod-template-hash" - RolloutActiveServiceSuffix = "active-service" - RolloutStableServiceSuffix = "stable-service" + RolloutActiveServiceSuffix = "active-service" + RolloutStableServiceSuffix = "stable-service" + WASMPath = "wasmPath" ) type Event int @@ -188,6 +193,7 @@ func ShouldIgnoreResource(metadata v12.ObjectMeta) bool { return metadata.Annotations[AdmiralIgnoreAnnotation] == "true" || metadata.Labels[AdmiralIgnoreAnnotation] == "true" } + func IsServiceMatch(serviceSelector map[string]string, selector *v12.LabelSelector) bool { if selector == nil || len(selector.MatchLabels) == 0 || len(serviceSelector) == 0 { return false @@ -207,3 +213,51 @@ func IsServiceMatch(serviceSelector map[string]string, selector *v12.LabelSelect } return match } + +func GetRoutingPolicyEnv(rp *v1.RoutingPolicy) string { + var environment = rp.Annotations[GetEnvKey()] + if len(environment) == 0 { + environment = rp.Labels[GetEnvKey()] + } + if len(environment) == 0 { + environment = Default + } + return environment +} + +func GetRoutingPolicyIdentity(rp *v1.RoutingPolicy) string { + identity := rp.Labels[GetRoutingPolicyLabel()] + return identity +} + +func GetRoutingPolicyKey(rp *v1.RoutingPolicy) string { + return ConstructRoutingPolicyKey(GetRoutingPolicyEnv(rp), GetRoutingPolicyIdentity(rp)) +} +// this function is exactly same as ConstructGtpKey. +// Not reusing the same function to keep the methods associated with these two objects separate. +func ConstructRoutingPolicyKey(env, identity string) string { + return fmt.Sprintf("%s.%s", env, identity) +} + +func GetSha1 (key interface{}) (string, error) { + bv, err := GetBytes(key) + if err != nil { + return "", err + } + hasher := sha1.New() + hasher.Write(bv) + sha := hex.EncodeToString(hasher.Sum(nil)) + return sha[0:5], nil +} + + +func GetBytes(key interface{}) ([]byte, error) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(key) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + diff --git a/admiral/pkg/controller/common/common_test.go b/admiral/pkg/controller/common/common_test.go index d1daa8ffb..6eba18dad 100644 --- a/admiral/pkg/controller/common/common_test.go +++ b/admiral/pkg/controller/common/common_test.go @@ -30,6 +30,8 @@ func init() { WorkloadSidecarName: "default", WorkloadSidecarUpdate: "disabled", MetricsEnabled: true, + EnableRoutingPolicy: true, + EnvoyFilterVersion: "1.13", } p.LabelSet.WorkloadIdentityKey = "identity" @@ -356,4 +358,57 @@ func TestGetGtpEnv(t *testing.T) { }) } +} + +func TestGetRoutingPolicyEnv(t *testing.T) { + + envNewAnnotationRP := v12.RoutingPolicy{} + envNewAnnotationRP.CreationTimestamp = v1.Now() + envNewAnnotationRP.Labels = map[string]string{"identity": "app1", "admiral.io/env": "stage1"} + envNewAnnotationRP.Annotations = map[string]string{"identity": "app1", "admiral.io/env": "stage1"} + envNewAnnotationRP.Namespace = "namespace" + envNewAnnotationRP.Name = "myRP-new-annotation" + + envLabelRP := v12.RoutingPolicy{} + envLabelRP.CreationTimestamp = v1.Now() + envLabelRP.Labels = map[string]string{"admiral.io/env": "stage1", "env": "stage2"} + envLabelRP.Namespace = "namespace" + envLabelRP.Name = "myRP-label" + + noEnvRP := v12.RoutingPolicy{} + noEnvRP.CreationTimestamp = v1.Now() + noEnvRP.Namespace = "namespace" + noEnvRP.Name = "myRP-no-env" + + testCases := []struct { + name string + rp *v12.RoutingPolicy + expectedEnv string + }{ + { + name: "Should return env from new annotation", + rp: &envNewAnnotationRP, + expectedEnv: "stage1", + }, + { + name: "Should return env from new label", + rp: &envLabelRP, + expectedEnv: "stage1", + }, + { + name: "Should return default with no env specified", + rp: &noEnvRP, + expectedEnv: "default", + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + returned := GetRoutingPolicyEnv(c.rp) + if !cmp.Equal(returned, c.expectedEnv, ignoreUnexported) { + t.Fatalf("RP env mismatch. Diff: %v", cmp.Diff(returned, c.expectedEnv, ignoreUnexported)) + } + }) + } + } \ No newline at end of file diff --git a/admiral/pkg/controller/common/config.go b/admiral/pkg/controller/common/config.go index 5832fbaef..74c4b4a6d 100644 --- a/admiral/pkg/controller/common/config.go +++ b/admiral/pkg/controller/common/config.go @@ -83,10 +83,26 @@ func GetGlobalTrafficDeploymentLabel() string { return admiralParams.LabelSet.GlobalTrafficDeploymentLabel } +func GetRoutingPolicyLabel() string { + return admiralParams.LabelSet.WorkloadIdentityKey +} + func GetWorkloadSidecarUpdate() string { return admiralParams.WorkloadSidecarUpdate } +func GetEnvoyFilterVersion() string { + return admiralParams.EnvoyFilterVersion +} + +func GetEnvoyFilterAdditionalConfig() string { + return admiralParams.EnvoyFilterAdditionalConfig +} + +func GetEnableRoutingPolicy() bool { + return admiralParams.EnableRoutingPolicy +} + func GetWorkloadSidecarName() string { return admiralParams.WorkloadSidecarName } diff --git a/admiral/pkg/controller/common/types.go b/admiral/pkg/controller/common/types.go index 68f7a697b..9bc98134d 100644 --- a/admiral/pkg/controller/common/types.go +++ b/admiral/pkg/controller/common/types.go @@ -29,26 +29,29 @@ type SidecarEgressMap struct { } type AdmiralParams struct { - ArgoRolloutsEnabled bool - KubeconfigPath string - CacheRefreshDuration time.Duration - ClusterRegistriesNamespace string - DependenciesNamespace string - SyncNamespace string - EnableSAN bool - SANPrefix string - SecretResolver string - SecretResolverConfigPath string - LabelSet *LabelSet - LogLevel int - HostnameSuffix string - PreviewHostnamePrefix string - MetricsEnabled bool - WorkloadSidecarUpdate string - WorkloadSidecarName string - AdmiralStateCheckerName string - DRStateStoreConfigPath string - ServiceEntryIPPrefix string + ArgoRolloutsEnabled bool + KubeconfigPath string + CacheRefreshDuration time.Duration + ClusterRegistriesNamespace string + DependenciesNamespace string + SyncNamespace string + EnableSAN bool + SANPrefix string + SecretResolver string + SecretResolverConfigPath string + LabelSet *LabelSet + LogLevel int + HostnameSuffix string + PreviewHostnamePrefix string + MetricsEnabled bool + WorkloadSidecarUpdate string + WorkloadSidecarName string + AdmiralStateCheckerName string + DRStateStoreConfigPath string + ServiceEntryIPPrefix string + EnvoyFilterVersion string + EnvoyFilterAdditionalConfig string + EnableRoutingPolicy bool } func (b AdmiralParams) String() string { @@ -59,10 +62,12 @@ func (b AdmiralParams) String() string { fmt.Sprintf("EnableSAN=%v ", b.EnableSAN) + fmt.Sprintf("SANPrefix=%v ", b.SANPrefix) + fmt.Sprintf("LabelSet=%v ", b.LabelSet) + - fmt.Sprintf("SecretResolver=%v ", b.SecretResolver)+ - fmt.Sprintf("AdmiralStateCheckername=%v ", b.AdmiralStateCheckerName)+ - fmt.Sprintf("DRStateStoreConfigPath=%v ", b.DRStateStoreConfigPath)+ - fmt.Sprintf("ServiceEntryIPPrefix=%v ", b.ServiceEntryIPPrefix) + fmt.Sprintf("SecretResolver=%v ", b.SecretResolver) + + fmt.Sprintf("AdmiralStateCheckername=%v ", b.AdmiralStateCheckerName) + + fmt.Sprintf("DRStateStoreConfigPath=%v ", b.DRStateStoreConfigPath) + + fmt.Sprintf("ServiceEntryIPPrefix=%v ", b.ServiceEntryIPPrefix) + + fmt.Sprintf("EnvoyFilterVersion=%v ", b.EnvoyFilterVersion) + + fmt.Sprintf("EnableRoutingPolicy=%v ", b.EnableRoutingPolicy) } type LabelSet struct { @@ -149,6 +154,12 @@ func (s *MapOfMaps) Put(pkey string, key string, value string) { s.cache[pkey] = mapVal } +func (s *MapOfMaps) PutMap(pkey string, inputMap *Map) { + defer s.mutex.Unlock() + s.mutex.Lock() + s.cache[pkey] = inputMap +} + func (s *MapOfMaps) Get(key string) *Map { s.mutex.Lock() val := s.cache[key] diff --git a/admiral/pkg/test/mock.go b/admiral/pkg/test/mock.go index 14a777d99..1060ce547 100644 --- a/admiral/pkg/test/mock.go +++ b/admiral/pkg/test/mock.go @@ -183,3 +183,19 @@ func (m *MockSidecarHandler) Updated(obj *v1alpha32.Sidecar) { func (m *MockSidecarHandler) Deleted(obj *v1alpha32.Sidecar) { m.Obj = nil } + +type MockRoutingPolicyHandler struct { + Obj *v1.RoutingPolicy +} + +func (m *MockRoutingPolicyHandler) Added(obj *v1.RoutingPolicy) { + m.Obj = obj +} + +func (m *MockRoutingPolicyHandler) Deleted(obj *v1.RoutingPolicy) { + m.Obj = nil +} + +func (m *MockRoutingPolicyHandler) Updated(obj *v1.RoutingPolicy) { + m.Obj = obj +} \ No newline at end of file diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index 6a7df9943..62b4c200f 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -7,7 +7,7 @@ set -o pipefail SCRIPT_ROOT=$(realpath $(dirname ${BASH_SOURCE})/..) #brew install coreutils (mac) or sudo apt-get install realpath (linux) if you don't have it # Grab code-generator version from go.sum. -CODEGEN_VERSION=$(grep 'k8s.io/code-generator' go.mod | awk '{print $2}' | head -1) +CODEGEN_VERSION=$(grep 'k8s.io/code-generator' go.mod | awk '{print $5}' | head -1) CODEGEN_PKG=$(echo `go env GOPATH`"/pkg/mod/k8s.io/code-generator@${CODEGEN_VERSION}") echo ">> Using ${CODEGEN_PKG}" diff --git a/install/admiral/base/crds.yaml b/install/admiral/base/crds.yaml index ffc269677..f99b8c10a 100644 --- a/install/admiral/base/crds.yaml +++ b/install/admiral/base/crds.yaml @@ -17,3 +17,20 @@ spec: - deps scope: Namespaced +--- + +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: routingpolicies.admiral.io +spec: + group: admiral.io + version: v1alpha1 + names: + kind: RoutingPolicy + plural: routingpolicies + shortNames: + - rp + - rps + scope: Namespaced + diff --git a/install/admiral/base/deployments.yaml b/install/admiral/base/deployments.yaml index 93611663b..ce4d9abec 100644 --- a/install/admiral/base/deployments.yaml +++ b/install/admiral/base/deployments.yaml @@ -33,6 +33,9 @@ spec: - 20s - --gateway_app - istio-eastwestgateway + - --envoy_filter_version + - "1.13" + - --enable_routing_policy=true image: docker.io/admiralproj/admiral:latest imagePullPolicy: Always livenessProbe: diff --git a/install/admiral/base/role_bindings.yaml b/install/admiral/base/role_bindings.yaml index 0fcf34145..902cabb07 100644 --- a/install/admiral/base/role_bindings.yaml +++ b/install/admiral/base/role_bindings.yaml @@ -44,4 +44,19 @@ roleRef: subjects: - kind: ServiceAccount name: admiral - namespace: admiral \ No newline at end of file + namespace: admiral + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: admiral-cluster-binding + namespace: istio-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admiral-sync-write-envoyfilters +subjects: + - kind: ServiceAccount + name: admiral + namespace: admiral-sync \ No newline at end of file diff --git a/install/admiral/base/roles.yaml b/install/admiral/base/roles.yaml index f7f71c688..4d1be36e3 100644 --- a/install/admiral/base/roles.yaml +++ b/install/admiral/base/roles.yaml @@ -33,3 +33,15 @@ rules: - apiGroups: [""] resources: ["configmaps"] verbs: ["get", "update", "create"] + +--- + +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: admiral-sync-write-envoyfilters + namespace: admiral +rules: + - apiGroups: ["networking.istio.io"] + resources: ['envoyfilters'] + verbs: ["create", "update", "delete", "patch"] \ No newline at end of file diff --git a/install/admiral/overlays/demosinglecluster/envconfig_values.yaml b/install/admiral/overlays/demosinglecluster/envconfig_values.yaml index 88a6d7c39..f531d1338 100644 --- a/install/admiral/overlays/demosinglecluster/envconfig_values.yaml +++ b/install/admiral/overlays/demosinglecluster/envconfig_values.yaml @@ -20,4 +20,7 @@ spec: - --sync_period - 10s - --argo_rollouts=true - name: admiral \ No newline at end of file + - --envoy_filter_version + - "1.13" + - --enable_routing_policy=true + name: admiral diff --git a/install/admiralremote/base/remote.yaml b/install/admiralremote/base/remote.yaml index b5c1d304e..94a852238 100644 --- a/install/admiralremote/base/remote.yaml +++ b/install/admiralremote/base/remote.yaml @@ -24,7 +24,7 @@ rules: resources: ['virtualservices', 'destinationrules', 'serviceentries', 'envoyfilters' ,'gateways', 'sidecars'] verbs: [ "get", "list", "watch"] - apiGroups: ["admiral.io"] - resources: ['globaltrafficpolicies'] + resources: ['globaltrafficpolicies', 'routingpolicies'] verbs: [ "get", "list", "watch"] - apiGroups: ["argoproj.io"] resources: ['rollouts'] diff --git a/install/sample/rp.yaml b/install/sample/rp.yaml new file mode 100644 index 000000000..9bc81c2da --- /dev/null +++ b/install/sample/rp.yaml @@ -0,0 +1,21 @@ +apiVersion: admiral.io/v1alpha1 +kind: RoutingPolicy +metadata: + name: greeting-routing-policy + annotations: + admiral.io/env: stage + labels: + identity: greeting +spec: + plugin: greeting + hosts: + - qal.greeting.mesh + config: + cachePrefix: "prefix-v1-a" + cacheTtlSec: "1440" + dispatcherUrl: "http://test.does.not.matter.mesh/" + env: "qal" + algorithmName: "ShardedRouting" + wasmPath: "/etc/istio/extensions/dynamicrouter.wasm" + pathPrefix: "/wpcatalog,/consumercatalog,/v1/company/{id}/auth/hydrate,/consumercatalog" + diff --git a/tests/run.sh b/tests/run.sh index 620159f58..db1e9cdbb 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -39,5 +39,7 @@ if [[ $IS_LOCAL == "false" ]]; then fi ./test3.sh "grpc-client" "sample" "grpc-server" $install_dir ./test4.sh "webapp" "sample" +# Testing routing policy +./test6.sh "sample" "1.13" $install_dir ./cleanup.sh $istio_version \ No newline at end of file diff --git a/tests/test6.sh b/tests/test6.sh new file mode 100755 index 000000000..fd7734e20 --- /dev/null +++ b/tests/test6.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +[ $# -lt 3 ] && { echo "Usage: $0 " ; exit 1; } + +deploy_and_test() { + source_ns=$1 + envoy_version=$2 + install_dir=$3 + + #Install the routing policy + + kubectl apply -f $install_dir/yaml/rp.yaml -n $source_ns + + + sleep 15 + + #Test, expecting envoy filter with correct version to be present in istio-system namespace + output=($(kubectl get envoyfilters.networking.istio.io -n istio-system | grep dynamicrouting| grep $envoy_version)) + + + if [[ "$output" != "" ]]; then + echo "PASS" + return 0 + else + echo "FAIL" . $output + kubectl get envoyfilters -n istio-system + kubectl get pod --all-namespaces + kubectl get dependencies --all-namespaces + return 1 + fi + kubectl delete -f $install_dir/yaml/rp.yaml -n $source_ns +} + +export -f deploy_and_test +timeout 90s bash -c "until deploy_and_test $1 $2 $3; do sleep 2; done" +if [[ $? -eq 124 ]] +then + exit 1 +fi + +