diff --git a/README.md b/README.md index e39710e8..19be6714 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,10 @@ spec: x-envoy-upstream-rq-timeout-ms: "15000" x-envoy-max-retries: "10" x-envoy-retry-on: "gateway-error,connect-failure,refused-stream" + # cross-origin resource sharing policy (optional) + corsPolicy: + allowOrigin: + - example.com # promote the canary without analysing it (default false) skipAnalysis: false # define the canary analysis timing and KPIs diff --git a/docs/gitbook/how-it-works.md b/docs/gitbook/how-it-works.md index 5935a819..2fff5bf7 100644 --- a/docs/gitbook/how-it-works.md +++ b/docs/gitbook/how-it-works.md @@ -109,6 +109,7 @@ kind: Canary metadata: name: frontend namespace: test +spec: service: # container port port: 9898 @@ -132,6 +133,16 @@ metadata: x-envoy-upstream-rq-timeout-ms: "15000" x-envoy-max-retries: "10" x-envoy-retry-on: "gateway-error,connect-failure,refused-stream" + # cross-origin resource sharing policy (optional) + corsPolicy: + allowOrigin: + - example.com + allowMethods: + - GET + allowCredentials: false + allowHeaders: + - x-some-header + maxAge: 24h # retry policy when a HTTP request fails (optional) retries: attempts: 3 @@ -165,6 +176,14 @@ spec: x-envoy-max-retries: "10" x-envoy-retry-on: gateway-error,connect-failure,refused-stream x-envoy-upstream-rq-timeout-ms: "15000" + corsPolicy: + allowHeaders: + - x-some-header + allowMethods: + - GET + allowOrigin: + - example.com + maxAge: 24h match: - uri: prefix: / diff --git a/pkg/apis/flagger/v1alpha3/types.go b/pkg/apis/flagger/v1alpha3/types.go index dfbf51a6..05dece8c 100755 --- a/pkg/apis/flagger/v1alpha3/types.go +++ b/pkg/apis/flagger/v1alpha3/types.go @@ -109,14 +109,15 @@ type CanaryStatus struct { // CanaryService is used to create ClusterIP services // and Istio Virtual Service type CanaryService struct { - Port int32 `json:"port"` - Gateways []string `json:"gateways"` - Hosts []string `json:"hosts"` - Match []istiov1alpha3.HTTPMatchRequest `json:"match,omitempty"` - Rewrite *istiov1alpha3.HTTPRewrite `json:"rewrite,omitempty"` - Timeout string `json:"timeout,omitempty"` - Retries *istiov1alpha3.HTTPRetry `json:"retries,omitempty"` - Headers *istiov1alpha3.Headers `json:"headers,omitempty"` + Port int32 `json:"port"` + Gateways []string `json:"gateways"` + Hosts []string `json:"hosts"` + Match []istiov1alpha3.HTTPMatchRequest `json:"match,omitempty"` + Rewrite *istiov1alpha3.HTTPRewrite `json:"rewrite,omitempty"` + Timeout string `json:"timeout,omitempty"` + Retries *istiov1alpha3.HTTPRetry `json:"retries,omitempty"` + Headers *istiov1alpha3.Headers `json:"headers,omitempty"` + CorsPolicy *istiov1alpha3.CorsPolicy `json:"corsPolicy,omitempty"` } // CanaryAnalysis is used to describe how the analysis should be done diff --git a/pkg/apis/flagger/v1alpha3/zz_generated.deepcopy.go b/pkg/apis/flagger/v1alpha3/zz_generated.deepcopy.go index 8f28a1d9..638a9457 100644 --- a/pkg/apis/flagger/v1alpha3/zz_generated.deepcopy.go +++ b/pkg/apis/flagger/v1alpha3/zz_generated.deepcopy.go @@ -166,6 +166,11 @@ func (in *CanaryService) DeepCopyInto(out *CanaryService) { *out = new(istiov1alpha3.Headers) (*in).DeepCopyInto(*out) } + if in.CorsPolicy != nil { + in, out := &in.CorsPolicy, &out.CorsPolicy + *out = new(istiov1alpha3.CorsPolicy) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/apis/istio/v1alpha3/virtual_service.go b/pkg/apis/istio/v1alpha3/virtual_service.go index 8b4c12f0..893f531f 100644 --- a/pkg/apis/istio/v1alpha3/virtual_service.go +++ b/pkg/apis/istio/v1alpha3/virtual_service.go @@ -328,7 +328,7 @@ type HTTPRoute struct { // Cross-Origin Resource Sharing policy (CORS). Refer to // https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS // for further details about cross origin resource sharing. - CorsPolicy *CorsPolicy `json:"CorsPolicy,omitempty"` + CorsPolicy *CorsPolicy `json:"corsPolicy,omitempty"` // Additional HTTP headers to add before forwarding a request to the // destination service. diff --git a/pkg/router/istio.go b/pkg/router/istio.go index fd4c137d..feef64d1 100644 --- a/pkg/router/istio.go +++ b/pkg/router/istio.go @@ -85,6 +85,7 @@ func (ir *IstioRouter) Sync(canary *flaggerv1.Canary) error { Rewrite: canary.Spec.Service.Rewrite, Timeout: canary.Spec.Service.Timeout, Retries: canary.Spec.Service.Retries, + CorsPolicy: canary.Spec.Service.CorsPolicy, AppendHeaders: addHeaders(canary), Route: route, }, @@ -201,6 +202,7 @@ func (ir *IstioRouter) SetRoutes( Rewrite: canary.Spec.Service.Rewrite, Timeout: canary.Spec.Service.Timeout, Retries: canary.Spec.Service.Retries, + CorsPolicy: canary.Spec.Service.CorsPolicy, AppendHeaders: addHeaders(canary), Route: []istiov1alpha3.DestinationWeight{ { diff --git a/pkg/router/istio_test.go b/pkg/router/istio_test.go index 807f80f1..5bd83927 100644 --- a/pkg/router/istio_test.go +++ b/pkg/router/istio_test.go @@ -192,7 +192,6 @@ func TestIstioRouter_HTTPRequestHeaders(t *testing.T) { t.Fatal(err.Error()) } - // test insert vs, err := mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) if err != nil { t.Fatal(err.Error()) @@ -207,3 +206,36 @@ func TestIstioRouter_HTTPRequestHeaders(t *testing.T) { t.Errorf("Got timeout %v wanted %v", timeout, "15000") } } + +func TestIstioRouter_CORS(t *testing.T) { + mocks := setupfakeClients() + router := &IstioRouter{ + logger: mocks.logger, + flaggerClient: mocks.flaggerClient, + istioClient: mocks.istioClient, + kubeClient: mocks.kubeClient, + } + + err := router.Sync(mocks.canary) + if err != nil { + t.Fatal(err.Error()) + } + + vs, err := mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) + if err != nil { + t.Fatal(err.Error()) + } + + if len(vs.Spec.Http) != 1 { + t.Fatalf("Got HTTPRoute %v wanted %v", len(vs.Spec.Http), 1) + } + + if vs.Spec.Http[0].CorsPolicy == nil { + t.Fatal("Got not CORS policy") + } + + methods := vs.Spec.Http[0].CorsPolicy.AllowMethods + if len(methods) != 2 { + t.Fatalf("Got CORS allow methods %v wanted %v", len(methods), 2) + } +} diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index 5d12aa25..e3bc1460 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -65,6 +65,12 @@ func newMockCanary() *v1alpha3.Canary { }, }, }, + CorsPolicy: &istiov1alpha3.CorsPolicy{ + AllowMethods: []string{ + "GET", + "POST", + }, + }, }, CanaryAnalysis: v1alpha3.CanaryAnalysis{ Threshold: 10, StepWeight: 10, diff --git a/test/e2e-tests.sh b/test/e2e-tests.sh index f2ccce58..a3da691d 100755 --- a/test/e2e-tests.sh +++ b/test/e2e-tests.sh @@ -33,10 +33,12 @@ spec: progressDeadlineSeconds: 60 service: port: 9898 - appendHeaders: - x-envoy-upstream-rq-timeout-ms: "15000" - x-envoy-max-retries: "10" - x-envoy-retry-on: "gateway-error,connect-failure,refused-stream" + headers: + request: + add: + x-envoy-upstream-rq-timeout-ms: "15000" + x-envoy-max-retries: "10" + x-envoy-retry-on: "gateway-error,connect-failure,refused-stream" canaryAnalysis: interval: 15s threshold: 15