Implement SMI v1alpha3 router

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
This commit is contained in:
Stefan Prodan 2021-04-28 19:17:01 +03:00
parent fcac992e71
commit 593f450093
No known key found for this signature in database
GPG Key ID: 3299AEB0E4085BAF
27 changed files with 1405 additions and 13 deletions

View File

@ -82,6 +82,7 @@ metadata:
spec:
# service mesh provider (optional)
# can be: kubernetes, istio, linkerd, appmesh, nginx, skipper, contour, gloo, supergloo, traefik
# for SMI TrafficSplit can be: smi:v1alpha1, smi:v1alpha2, smi:v1alpha3
provider: istio
# deployment reference
targetRef:
@ -192,17 +193,21 @@ For more details on how the canary analysis and promotion works please [read the
**Service Mesh**
| Feature | App Mesh | Istio | Linkerd | Kubernetes CNI |
| ------------------------------------------ | ------------------ | ------------------ | ------------------ | ----------------- |
| Canary deployments (weighted traffic) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
| A/B testing (headers and cookies routing) | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: |
| Blue/Green deployments (traffic switch) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Blue/Green deployments (traffic mirroring) | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: |
| Webhooks (acceptance/load testing) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Manual gating (approve/pause/resume) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Request success rate check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
| Request duration check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
| Custom metric checks | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Feature | App Mesh | Istio | Linkerd | SMI | Kubernetes CNI |
| ------------------------------------------ | ------------------ | ------------------ | ------------------ | ----------------- | ----------------- |
| Canary deployments (weighted traffic) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
| A/B testing (headers and cookies routing) | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: |
| Blue/Green deployments (traffic switch) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Blue/Green deployments (traffic mirroring) | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: |
| Webhooks (acceptance/load testing) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Manual gating (approve/pause/resume) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Request success rate check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: |
| Request duration check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: |
| Custom metric checks | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
For SMI compatible service mesh solutions like Open Service Mesh, Consul Connect or Nginx Service Mesh,
[Prometheus MetricTemplates](https://docs.flagger.app/usage/metrics#prometheus) can be used to implement
the request success rate and request duration checks.
**Ingress**
@ -229,7 +234,6 @@ For more details on how the canary analysis and promotion works please [read the
#### Integrations
* Add support for Kubernetes [Ingress v2](https://github.com/kubernetes-sigs/service-apis)
* Add support for SMI compatible service mesh solutions like Open Service Mesh and Consul Connect
* Add support for ingress controllers like HAProxy and ALB
* Add support for metrics providers like InfluxDB, Stackdriver, SignalFX

View File

@ -30,7 +30,7 @@ chmod +x ${CODEGEN_PKG}/generate-groups.sh
${CODEGEN_PKG}/generate-groups.sh all \
github.com/fluxcd/flagger/pkg/client github.com/fluxcd/flagger/pkg/apis \
"flagger:v1beta1 appmesh:v1beta2 appmesh:v1beta1 istio:v1alpha3 smi:v1alpha1 smi:v1alpha2 gloo:v1 projectcontour:v1 traefik:v1alpha1" \
"flagger:v1beta1 appmesh:v1beta2 appmesh:v1beta1 istio:v1alpha3 smi:v1alpha1 smi:v1alpha2 smi:v1alpha3 gloo:v1 projectcontour:v1 traefik:v1alpha1" \
--output-base "${TEMP_DIR}" \
--go-header-file ${SCRIPT_ROOT}/hack/boilerplate.go.txt

View File

@ -940,6 +940,10 @@ spec:
- post-rollout
- event
- rollback
- confirm-traffic-increase
muteAlert:
description: Mute all alerts for the webhook
type: boolean
url:
description: URL address of this webhook
type: string

View File

@ -0,0 +1,4 @@
// +k8s:deepcopy-gen=package
// +groupName=split.smi-spec.io
package v1alpha3

View File

@ -0,0 +1,48 @@
package v1alpha3
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
ts "github.com/fluxcd/flagger/pkg/apis/smi"
)
// SchemeGroupVersion is the identifier for the API which includes
// the name of the group and the version of the API
var SchemeGroupVersion = schema.GroupVersion{
Group: ts.GroupName,
Version: "v1alpha3",
}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
// SchemeBuilder collects functions that add things to a scheme. It's to allow
// code to compile without explicitly referencing generated types. You should
// declare one in each package that will have generated deep copy or conversion
// functions.
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme applies all the stored functions to the scheme. A non-nil error
// indicates that one function failed and the attempt was abandoned.
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&TrafficSplit{},
&TrafficSplitList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,60 @@
package v1alpha3
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// TrafficSplit allows users to incrementally direct percentages of traffic
// between various services. It will be used by clients such as ingress
// controllers or service mesh sidecars to split the outgoing traffic to
// different destinations.
type TrafficSplit struct {
metav1.TypeMeta `json:",inline"`
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Specification of the desired behavior of the traffic split.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// +optional
Spec TrafficSplitSpec `json:"spec,omitempty"`
}
// TrafficSplitSpec is the specification for a TrafficSplit
type TrafficSplitSpec struct {
// Service represents the apex service
Service string `json:"service"`
// Backends defines a list of Kubernetes services
// used as the traffic split destination
Backends []TrafficSplitBackend `json:"backends"`
// Matches allows defining a list of HTTP route groups
// that this traffic split object should match
// +optional
Matches []corev1.TypedLocalObjectReference `json:"matches,omitempty"`
}
// TrafficSplitBackend defines a backend
type TrafficSplitBackend struct {
// Service is the name of a Kubernetes service
Service string `json:"service"`
// Weight defines the traffic split percentage
Weight int `json:"weight"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type TrafficSplitList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []TrafficSplit `json:"items"`
}

View File

@ -0,0 +1,130 @@
// +build !ignore_autogenerated
/*
Copyright 2020 The Flux 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 deepcopy-gen. DO NOT EDIT.
package v1alpha3
import (
v1 "k8s.io/api/core/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TrafficSplit) DeepCopyInto(out *TrafficSplit) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplit.
func (in *TrafficSplit) DeepCopy() *TrafficSplit {
if in == nil {
return nil
}
out := new(TrafficSplit)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TrafficSplit) 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 *TrafficSplitBackend) DeepCopyInto(out *TrafficSplitBackend) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitBackend.
func (in *TrafficSplitBackend) DeepCopy() *TrafficSplitBackend {
if in == nil {
return nil
}
out := new(TrafficSplitBackend)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TrafficSplitList) DeepCopyInto(out *TrafficSplitList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]TrafficSplit, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitList.
func (in *TrafficSplitList) DeepCopy() *TrafficSplitList {
if in == nil {
return nil
}
out := new(TrafficSplitList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TrafficSplitList) 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 *TrafficSplitSpec) DeepCopyInto(out *TrafficSplitSpec) {
*out = *in
if in.Backends != nil {
in, out := &in.Backends, &out.Backends
*out = make([]TrafficSplitBackend, len(*in))
copy(*out, *in)
}
if in.Matches != nil {
in, out := &in.Matches, &out.Matches
*out = make([]v1.TypedLocalObjectReference, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitSpec.
func (in *TrafficSplitSpec) DeepCopy() *TrafficSplitSpec {
if in == nil {
return nil
}
out := new(TrafficSplitSpec)
in.DeepCopyInto(out)
return out
}

View File

@ -29,6 +29,7 @@ import (
projectcontourv1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/projectcontour/v1"
splitv1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha1"
splitv1alpha2 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha2"
splitv1alpha3 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha3"
traefikv1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/traefik/v1alpha1"
discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest"
@ -45,6 +46,7 @@ type Interface interface {
ProjectcontourV1() projectcontourv1.ProjectcontourV1Interface
SplitV1alpha1() splitv1alpha1.SplitV1alpha1Interface
SplitV1alpha2() splitv1alpha2.SplitV1alpha2Interface
SplitV1alpha3() splitv1alpha3.SplitV1alpha3Interface
TraefikV1alpha1() traefikv1alpha1.TraefikV1alpha1Interface
}
@ -60,6 +62,7 @@ type Clientset struct {
projectcontourV1 *projectcontourv1.ProjectcontourV1Client
splitV1alpha1 *splitv1alpha1.SplitV1alpha1Client
splitV1alpha2 *splitv1alpha2.SplitV1alpha2Client
splitV1alpha3 *splitv1alpha3.SplitV1alpha3Client
traefikV1alpha1 *traefikv1alpha1.TraefikV1alpha1Client
}
@ -103,6 +106,11 @@ func (c *Clientset) SplitV1alpha2() splitv1alpha2.SplitV1alpha2Interface {
return c.splitV1alpha2
}
// SplitV1alpha3 retrieves the SplitV1alpha3Client
func (c *Clientset) SplitV1alpha3() splitv1alpha3.SplitV1alpha3Interface {
return c.splitV1alpha3
}
// TraefikV1alpha1 retrieves the TraefikV1alpha1Client
func (c *Clientset) TraefikV1alpha1() traefikv1alpha1.TraefikV1alpha1Interface {
return c.traefikV1alpha1
@ -161,6 +169,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
if err != nil {
return nil, err
}
cs.splitV1alpha3, err = splitv1alpha3.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
cs.traefikV1alpha1, err = traefikv1alpha1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
@ -185,6 +197,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset {
cs.projectcontourV1 = projectcontourv1.NewForConfigOrDie(c)
cs.splitV1alpha1 = splitv1alpha1.NewForConfigOrDie(c)
cs.splitV1alpha2 = splitv1alpha2.NewForConfigOrDie(c)
cs.splitV1alpha3 = splitv1alpha3.NewForConfigOrDie(c)
cs.traefikV1alpha1 = traefikv1alpha1.NewForConfigOrDie(c)
cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
@ -202,6 +215,7 @@ func New(c rest.Interface) *Clientset {
cs.projectcontourV1 = projectcontourv1.New(c)
cs.splitV1alpha1 = splitv1alpha1.New(c)
cs.splitV1alpha2 = splitv1alpha2.New(c)
cs.splitV1alpha3 = splitv1alpha3.New(c)
cs.traefikV1alpha1 = traefikv1alpha1.New(c)
cs.DiscoveryClient = discovery.NewDiscoveryClient(c)

View File

@ -36,6 +36,8 @@ import (
fakesplitv1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha1/fake"
splitv1alpha2 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha2"
fakesplitv1alpha2 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha2/fake"
splitv1alpha3 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha3"
fakesplitv1alpha3 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha3/fake"
traefikv1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/traefik/v1alpha1"
faketraefikv1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/traefik/v1alpha1/fake"
"k8s.io/apimachinery/pkg/runtime"
@ -132,6 +134,11 @@ func (c *Clientset) SplitV1alpha2() splitv1alpha2.SplitV1alpha2Interface {
return &fakesplitv1alpha2.FakeSplitV1alpha2{Fake: &c.Fake}
}
// SplitV1alpha3 retrieves the SplitV1alpha3Client
func (c *Clientset) SplitV1alpha3() splitv1alpha3.SplitV1alpha3Interface {
return &fakesplitv1alpha3.FakeSplitV1alpha3{Fake: &c.Fake}
}
// TraefikV1alpha1 retrieves the TraefikV1alpha1Client
func (c *Clientset) TraefikV1alpha1() traefikv1alpha1.TraefikV1alpha1Interface {
return &faketraefikv1alpha1.FakeTraefikV1alpha1{Fake: &c.Fake}

View File

@ -27,6 +27,7 @@ import (
projectcontourv1 "github.com/fluxcd/flagger/pkg/apis/projectcontour/v1"
splitv1alpha1 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha1"
splitv1alpha2 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha2"
splitv1alpha3 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha3"
traefikv1alpha1 "github.com/fluxcd/flagger/pkg/apis/traefik/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
@ -47,6 +48,7 @@ var localSchemeBuilder = runtime.SchemeBuilder{
projectcontourv1.AddToScheme,
splitv1alpha1.AddToScheme,
splitv1alpha2.AddToScheme,
splitv1alpha3.AddToScheme,
traefikv1alpha1.AddToScheme,
}

View File

@ -27,6 +27,7 @@ import (
projectcontourv1 "github.com/fluxcd/flagger/pkg/apis/projectcontour/v1"
splitv1alpha1 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha1"
splitv1alpha2 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha2"
splitv1alpha3 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha3"
traefikv1alpha1 "github.com/fluxcd/flagger/pkg/apis/traefik/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
@ -47,6 +48,7 @@ var localSchemeBuilder = runtime.SchemeBuilder{
projectcontourv1.AddToScheme,
splitv1alpha1.AddToScheme,
splitv1alpha2.AddToScheme,
splitv1alpha3.AddToScheme,
traefikv1alpha1.AddToScheme,
}

View File

@ -0,0 +1,20 @@
/*
Copyright 2020 The Flux 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.
// This package has the automatically generated typed clients.
package v1alpha3

View File

@ -0,0 +1,20 @@
/*
Copyright 2020 The Flux 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 has the automatically generated clients.
package fake

View File

@ -0,0 +1,40 @@
/*
Copyright 2020 The Flux 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 (
v1alpha3 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha3"
rest "k8s.io/client-go/rest"
testing "k8s.io/client-go/testing"
)
type FakeSplitV1alpha3 struct {
*testing.Fake
}
func (c *FakeSplitV1alpha3) TrafficSplits(namespace string) v1alpha3.TrafficSplitInterface {
return &FakeTrafficSplits{c, namespace}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeSplitV1alpha3) RESTClient() rest.Interface {
var ret *rest.RESTClient
return ret
}

View File

@ -0,0 +1,130 @@
/*
Copyright 2020 The Flux 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 (
"context"
v1alpha3 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha3"
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"
)
// FakeTrafficSplits implements TrafficSplitInterface
type FakeTrafficSplits struct {
Fake *FakeSplitV1alpha3
ns string
}
var trafficsplitsResource = schema.GroupVersionResource{Group: "split.smi-spec.io", Version: "v1alpha3", Resource: "trafficsplits"}
var trafficsplitsKind = schema.GroupVersionKind{Group: "split.smi-spec.io", Version: "v1alpha3", Kind: "TrafficSplit"}
// Get takes name of the trafficSplit, and returns the corresponding trafficSplit object, and an error if there is any.
func (c *FakeTrafficSplits) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha3.TrafficSplit, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(trafficsplitsResource, c.ns, name), &v1alpha3.TrafficSplit{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha3.TrafficSplit), err
}
// List takes label and field selectors, and returns the list of TrafficSplits that match those selectors.
func (c *FakeTrafficSplits) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha3.TrafficSplitList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(trafficsplitsResource, trafficsplitsKind, c.ns, opts), &v1alpha3.TrafficSplitList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha3.TrafficSplitList{ListMeta: obj.(*v1alpha3.TrafficSplitList).ListMeta}
for _, item := range obj.(*v1alpha3.TrafficSplitList).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 trafficSplits.
func (c *FakeTrafficSplits) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(trafficsplitsResource, c.ns, opts))
}
// Create takes the representation of a trafficSplit and creates it. Returns the server's representation of the trafficSplit, and an error, if there is any.
func (c *FakeTrafficSplits) Create(ctx context.Context, trafficSplit *v1alpha3.TrafficSplit, opts v1.CreateOptions) (result *v1alpha3.TrafficSplit, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(trafficsplitsResource, c.ns, trafficSplit), &v1alpha3.TrafficSplit{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha3.TrafficSplit), err
}
// Update takes the representation of a trafficSplit and updates it. Returns the server's representation of the trafficSplit, and an error, if there is any.
func (c *FakeTrafficSplits) Update(ctx context.Context, trafficSplit *v1alpha3.TrafficSplit, opts v1.UpdateOptions) (result *v1alpha3.TrafficSplit, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(trafficsplitsResource, c.ns, trafficSplit), &v1alpha3.TrafficSplit{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha3.TrafficSplit), err
}
// Delete takes name of the trafficSplit and deletes it. Returns an error if one occurs.
func (c *FakeTrafficSplits) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(trafficsplitsResource, c.ns, name), &v1alpha3.TrafficSplit{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeTrafficSplits) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(trafficsplitsResource, c.ns, listOpts)
_, err := c.Fake.Invokes(action, &v1alpha3.TrafficSplitList{})
return err
}
// Patch applies the patch and returns the patched trafficSplit.
func (c *FakeTrafficSplits) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha3.TrafficSplit, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(trafficsplitsResource, c.ns, name, pt, data, subresources...), &v1alpha3.TrafficSplit{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha3.TrafficSplit), err
}

View File

@ -0,0 +1,21 @@
/*
Copyright 2020 The Flux 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 v1alpha3
type TrafficSplitExpansion interface{}

View File

@ -0,0 +1,89 @@
/*
Copyright 2020 The Flux 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 v1alpha3
import (
v1alpha3 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha3"
"github.com/fluxcd/flagger/pkg/client/clientset/versioned/scheme"
rest "k8s.io/client-go/rest"
)
type SplitV1alpha3Interface interface {
RESTClient() rest.Interface
TrafficSplitsGetter
}
// SplitV1alpha3Client is used to interact with features provided by the split.smi-spec.io group.
type SplitV1alpha3Client struct {
restClient rest.Interface
}
func (c *SplitV1alpha3Client) TrafficSplits(namespace string) TrafficSplitInterface {
return newTrafficSplits(c, namespace)
}
// NewForConfig creates a new SplitV1alpha3Client for the given config.
func NewForConfig(c *rest.Config) (*SplitV1alpha3Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &SplitV1alpha3Client{client}, nil
}
// NewForConfigOrDie creates a new SplitV1alpha3Client for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *SplitV1alpha3Client {
client, err := NewForConfig(c)
if err != nil {
panic(err)
}
return client
}
// New creates a new SplitV1alpha3Client for the given RESTClient.
func New(c rest.Interface) *SplitV1alpha3Client {
return &SplitV1alpha3Client{c}
}
func setConfigDefaults(config *rest.Config) error {
gv := v1alpha3.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return nil
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *SplitV1alpha3Client) RESTClient() rest.Interface {
if c == nil {
return nil
}
return c.restClient
}

View File

@ -0,0 +1,178 @@
/*
Copyright 2020 The Flux 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 v1alpha3
import (
"context"
"time"
v1alpha3 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha3"
scheme "github.com/fluxcd/flagger/pkg/client/clientset/versioned/scheme"
v1 "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"
)
// TrafficSplitsGetter has a method to return a TrafficSplitInterface.
// A group's client should implement this interface.
type TrafficSplitsGetter interface {
TrafficSplits(namespace string) TrafficSplitInterface
}
// TrafficSplitInterface has methods to work with TrafficSplit resources.
type TrafficSplitInterface interface {
Create(ctx context.Context, trafficSplit *v1alpha3.TrafficSplit, opts v1.CreateOptions) (*v1alpha3.TrafficSplit, error)
Update(ctx context.Context, trafficSplit *v1alpha3.TrafficSplit, opts v1.UpdateOptions) (*v1alpha3.TrafficSplit, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha3.TrafficSplit, error)
List(ctx context.Context, opts v1.ListOptions) (*v1alpha3.TrafficSplitList, error)
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha3.TrafficSplit, err error)
TrafficSplitExpansion
}
// trafficSplits implements TrafficSplitInterface
type trafficSplits struct {
client rest.Interface
ns string
}
// newTrafficSplits returns a TrafficSplits
func newTrafficSplits(c *SplitV1alpha3Client, namespace string) *trafficSplits {
return &trafficSplits{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the trafficSplit, and returns the corresponding trafficSplit object, and an error if there is any.
func (c *trafficSplits) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha3.TrafficSplit, err error) {
result = &v1alpha3.TrafficSplit{}
err = c.client.Get().
Namespace(c.ns).
Resource("trafficsplits").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do(ctx).
Into(result)
return
}
// List takes label and field selectors, and returns the list of TrafficSplits that match those selectors.
func (c *trafficSplits) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha3.TrafficSplitList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1alpha3.TrafficSplitList{}
err = c.client.Get().
Namespace(c.ns).
Resource("trafficsplits").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do(ctx).
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested trafficSplits.
func (c *trafficSplits) Watch(ctx context.Context, opts v1.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("trafficsplits").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch(ctx)
}
// Create takes the representation of a trafficSplit and creates it. Returns the server's representation of the trafficSplit, and an error, if there is any.
func (c *trafficSplits) Create(ctx context.Context, trafficSplit *v1alpha3.TrafficSplit, opts v1.CreateOptions) (result *v1alpha3.TrafficSplit, err error) {
result = &v1alpha3.TrafficSplit{}
err = c.client.Post().
Namespace(c.ns).
Resource("trafficsplits").
VersionedParams(&opts, scheme.ParameterCodec).
Body(trafficSplit).
Do(ctx).
Into(result)
return
}
// Update takes the representation of a trafficSplit and updates it. Returns the server's representation of the trafficSplit, and an error, if there is any.
func (c *trafficSplits) Update(ctx context.Context, trafficSplit *v1alpha3.TrafficSplit, opts v1.UpdateOptions) (result *v1alpha3.TrafficSplit, err error) {
result = &v1alpha3.TrafficSplit{}
err = c.client.Put().
Namespace(c.ns).
Resource("trafficsplits").
Name(trafficSplit.Name).
VersionedParams(&opts, scheme.ParameterCodec).
Body(trafficSplit).
Do(ctx).
Into(result)
return
}
// Delete takes name of the trafficSplit and deletes it. Returns an error if one occurs.
func (c *trafficSplits) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("trafficsplits").
Name(name).
Body(&opts).
Do(ctx).
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *trafficSplits) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
var timeout time.Duration
if listOpts.TimeoutSeconds != nil {
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("trafficsplits").
VersionedParams(&listOpts, scheme.ParameterCodec).
Timeout(timeout).
Body(&opts).
Do(ctx).
Error()
}
// Patch applies the patch and returns the patched trafficSplit.
func (c *trafficSplits) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha3.TrafficSplit, err error) {
result = &v1alpha3.TrafficSplit{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("trafficsplits").
Name(name).
SubResource(subresources...).
VersionedParams(&opts, scheme.ParameterCodec).
Body(data).
Do(ctx).
Into(result)
return
}

View File

@ -29,6 +29,7 @@ import (
projectcontourv1 "github.com/fluxcd/flagger/pkg/apis/projectcontour/v1"
v1alpha1 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha1"
v1alpha2 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha2"
smiv1alpha3 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha3"
traefikv1alpha1 "github.com/fluxcd/flagger/pkg/apis/traefik/v1alpha1"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
@ -106,6 +107,10 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
case v1alpha2.SchemeGroupVersion.WithResource("trafficsplits"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Split().V1alpha2().TrafficSplits().Informer()}, nil
// Group=split.smi-spec.io, Version=v1alpha3
case smiv1alpha3.SchemeGroupVersion.WithResource("trafficsplits"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Split().V1alpha3().TrafficSplits().Informer()}, nil
// Group=traefik.containo.us, Version=v1alpha1
case traefikv1alpha1.SchemeGroupVersion.WithResource("traefikservices"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Traefik().V1alpha1().TraefikServices().Informer()}, nil

View File

@ -22,6 +22,7 @@ import (
internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces"
v1alpha1 "github.com/fluxcd/flagger/pkg/client/informers/externalversions/smi/v1alpha1"
v1alpha2 "github.com/fluxcd/flagger/pkg/client/informers/externalversions/smi/v1alpha2"
v1alpha3 "github.com/fluxcd/flagger/pkg/client/informers/externalversions/smi/v1alpha3"
)
// Interface provides access to each of this group's versions.
@ -30,6 +31,8 @@ type Interface interface {
V1alpha1() v1alpha1.Interface
// V1alpha2 provides access to shared informers for resources in V1alpha2.
V1alpha2() v1alpha2.Interface
// V1alpha3 provides access to shared informers for resources in V1alpha3.
V1alpha3() v1alpha3.Interface
}
type group struct {
@ -52,3 +55,8 @@ func (g *group) V1alpha1() v1alpha1.Interface {
func (g *group) V1alpha2() v1alpha2.Interface {
return v1alpha2.New(g.factory, g.namespace, g.tweakListOptions)
}
// V1alpha3 returns a new v1alpha3.Interface.
func (g *group) V1alpha3() v1alpha3.Interface {
return v1alpha3.New(g.factory, g.namespace, g.tweakListOptions)
}

View File

@ -0,0 +1,45 @@
/*
Copyright 2020 The Flux 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 v1alpha3
import (
internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to all the informers in this group version.
type Interface interface {
// TrafficSplits returns a TrafficSplitInformer.
TrafficSplits() TrafficSplitInformer
}
type version struct {
factory internalinterfaces.SharedInformerFactory
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// TrafficSplits returns a TrafficSplitInformer.
func (v *version) TrafficSplits() TrafficSplitInformer {
return &trafficSplitInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}

View File

@ -0,0 +1,90 @@
/*
Copyright 2020 The Flux 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 v1alpha3
import (
"context"
time "time"
smiv1alpha3 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha3"
versioned "github.com/fluxcd/flagger/pkg/client/clientset/versioned"
internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces"
v1alpha3 "github.com/fluxcd/flagger/pkg/client/listers/smi/v1alpha3"
v1 "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"
)
// TrafficSplitInformer provides access to a shared informer and lister for
// TrafficSplits.
type TrafficSplitInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha3.TrafficSplitLister
}
type trafficSplitInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewTrafficSplitInformer constructs a new informer for TrafficSplit 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 NewTrafficSplitInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredTrafficSplitInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredTrafficSplitInformer constructs a new informer for TrafficSplit 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 NewFilteredTrafficSplitInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.SplitV1alpha3().TrafficSplits(namespace).List(context.TODO(), options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.SplitV1alpha3().TrafficSplits(namespace).Watch(context.TODO(), options)
},
},
&smiv1alpha3.TrafficSplit{},
resyncPeriod,
indexers,
)
}
func (f *trafficSplitInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredTrafficSplitInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *trafficSplitInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&smiv1alpha3.TrafficSplit{}, f.defaultInformer)
}
func (f *trafficSplitInformer) Lister() v1alpha3.TrafficSplitLister {
return v1alpha3.NewTrafficSplitLister(f.Informer().GetIndexer())
}

View File

@ -0,0 +1,27 @@
/*
Copyright 2020 The Flux 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 v1alpha3
// TrafficSplitListerExpansion allows custom methods to be added to
// TrafficSplitLister.
type TrafficSplitListerExpansion interface{}
// TrafficSplitNamespaceListerExpansion allows custom methods to be added to
// TrafficSplitNamespaceLister.
type TrafficSplitNamespaceListerExpansion interface{}

View File

@ -0,0 +1,99 @@
/*
Copyright 2020 The Flux 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 v1alpha3
import (
v1alpha3 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha3"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// TrafficSplitLister helps list TrafficSplits.
// All objects returned here must be treated as read-only.
type TrafficSplitLister interface {
// List lists all TrafficSplits in the indexer.
// Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1alpha3.TrafficSplit, err error)
// TrafficSplits returns an object that can list and get TrafficSplits.
TrafficSplits(namespace string) TrafficSplitNamespaceLister
TrafficSplitListerExpansion
}
// trafficSplitLister implements the TrafficSplitLister interface.
type trafficSplitLister struct {
indexer cache.Indexer
}
// NewTrafficSplitLister returns a new TrafficSplitLister.
func NewTrafficSplitLister(indexer cache.Indexer) TrafficSplitLister {
return &trafficSplitLister{indexer: indexer}
}
// List lists all TrafficSplits in the indexer.
func (s *trafficSplitLister) List(selector labels.Selector) (ret []*v1alpha3.TrafficSplit, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha3.TrafficSplit))
})
return ret, err
}
// TrafficSplits returns an object that can list and get TrafficSplits.
func (s *trafficSplitLister) TrafficSplits(namespace string) TrafficSplitNamespaceLister {
return trafficSplitNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// TrafficSplitNamespaceLister helps list and get TrafficSplits.
// All objects returned here must be treated as read-only.
type TrafficSplitNamespaceLister interface {
// List lists all TrafficSplits in the indexer for a given namespace.
// Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1alpha3.TrafficSplit, err error)
// Get retrieves the TrafficSplit from the indexer for a given namespace and name.
// Objects returned here must be treated as read-only.
Get(name string) (*v1alpha3.TrafficSplit, error)
TrafficSplitNamespaceListerExpansion
}
// trafficSplitNamespaceLister implements the TrafficSplitNamespaceLister
// interface.
type trafficSplitNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all TrafficSplits in the indexer for a given namespace.
func (s trafficSplitNamespaceLister) List(selector labels.Selector) (ret []*v1alpha3.TrafficSplit, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha3.TrafficSplit))
})
return ret, err
}
// Get retrieves the TrafficSplit from the indexer for a given namespace and name.
func (s trafficSplitNamespaceLister) Get(name string) (*v1alpha3.TrafficSplit, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1alpha3.Resource("trafficsplit"), name)
}
return obj.(*v1alpha3.TrafficSplit), nil
}

View File

@ -122,6 +122,15 @@ func (factory *Factory) MeshRouter(provider string, labelSelector string) Interf
smiClient: factory.meshClient,
targetMesh: mesh,
}
case strings.HasPrefix(provider, flaggerv1.SMIProvider+":v1alpha3"):
mesh := strings.TrimPrefix(provider, flaggerv1.SMIProvider+":v1alpha3:")
return &Smiv1alpha3Router{
logger: factory.logger,
flaggerClient: factory.flaggerClient,
kubeClient: factory.kubeClient,
smiClient: factory.meshClient,
targetMesh: mesh,
}
case provider == flaggerv1.ContourProvider:
return &ContourRouter{
logger: factory.logger,

198
pkg/router/smi_v1alpha3.go Normal file
View File

@ -0,0 +1,198 @@
/*
Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"context"
"encoding/json"
"fmt"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes"
flaggerv1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1"
smiv1alpha3 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha3"
clientset "github.com/fluxcd/flagger/pkg/client/clientset/versioned"
)
type Smiv1alpha3Router struct {
kubeClient kubernetes.Interface
flaggerClient clientset.Interface
smiClient clientset.Interface
logger *zap.SugaredLogger
targetMesh string
}
// Reconcile creates or updates the SMI traffic split
func (sr *Smiv1alpha3Router) Reconcile(canary *flaggerv1.Canary) error {
apexName, primaryName, canaryName := canary.GetServiceNames()
var host string
if len(canary.Spec.Service.Hosts) > 0 {
host = canary.Spec.Service.Hosts[0]
} else {
host = apexName
}
tsSpec := smiv1alpha3.TrafficSplitSpec{
Service: host,
Backends: []smiv1alpha3.TrafficSplitBackend{
{
Service: canaryName,
Weight: 0,
},
{
Service: primaryName,
Weight: 100,
},
},
}
ts, err := sr.smiClient.SplitV1alpha3().TrafficSplits(canary.Namespace).Get(context.TODO(), apexName, metav1.GetOptions{})
// create traffic split
if errors.IsNotFound(err) {
t := &smiv1alpha3.TrafficSplit{
ObjectMeta: metav1.ObjectMeta{
Name: apexName,
Namespace: canary.Namespace,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(canary, schema.GroupVersionKind{
Group: flaggerv1.SchemeGroupVersion.Group,
Version: flaggerv1.SchemeGroupVersion.Version,
Kind: flaggerv1.CanaryKind,
}),
},
Annotations: sr.makeAnnotations(canary.Spec.Service.Gateways),
},
Spec: tsSpec,
}
_, err := sr.smiClient.SplitV1alpha3().TrafficSplits(canary.Namespace).Create(context.TODO(), t, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("TrafficSplit %s.%s create error: %w", apexName, canary.Namespace, err)
}
sr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
Infof("TrafficSplit %s.%s created", t.GetName(), canary.Namespace)
return nil
} else if err != nil {
return fmt.Errorf("TrafficSplit %s.%s get query error: %w", apexName, canary.Namespace, err)
}
// update traffic split
if diff := cmp.Diff(tsSpec, ts.Spec, cmpopts.IgnoreFields(smiv1alpha3.TrafficSplitBackend{}, "Weight")); diff != "" {
tsClone := ts.DeepCopy()
tsClone.Spec = tsSpec
_, err := sr.smiClient.SplitV1alpha3().TrafficSplits(canary.Namespace).Update(context.TODO(), tsClone, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("TrafficSplit %s.%s update error: %w", apexName, canary.Namespace, err)
}
sr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
Infof("TrafficSplit %s.%s updated", apexName, canary.Namespace)
return nil
}
return nil
}
// GetRoutes returns the destinations weight for primary and canary
func (sr *Smiv1alpha3Router) GetRoutes(canary *flaggerv1.Canary) (
primaryWeight int,
canaryWeight int,
mirrored bool,
err error,
) {
apexName, primaryName, canaryName := canary.GetServiceNames()
ts, err := sr.smiClient.SplitV1alpha3().TrafficSplits(canary.Namespace).Get(context.TODO(), apexName, metav1.GetOptions{})
if err != nil {
err = fmt.Errorf("TrafficSplit %s.%s get query error %v", apexName, canary.Namespace, err)
return
}
for _, r := range ts.Spec.Backends {
if r.Service == primaryName {
primaryWeight = r.Weight
}
if r.Service == canaryName {
canaryWeight = r.Weight
}
}
if primaryWeight == 0 && canaryWeight == 0 {
err = fmt.Errorf("TrafficSplit %s.%s does not contain routes for %s and %s",
apexName, canary.Namespace, primaryName, canaryName)
}
mirrored = false
return
}
// SetRoutes updates the destinations weight for primary and canary
func (sr *Smiv1alpha3Router) SetRoutes(
canary *flaggerv1.Canary,
primaryWeight int,
canaryWeight int,
_ bool,
) error {
apexName, primaryName, canaryName := canary.GetServiceNames()
ts, err := sr.smiClient.SplitV1alpha3().TrafficSplits(canary.Namespace).Get(context.TODO(), apexName, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("TrafficSplit %s.%s get query error %v", apexName, canary.Namespace, err)
}
backends := []smiv1alpha3.TrafficSplitBackend{
{
Service: canaryName,
Weight: canaryWeight,
},
{
Service: primaryName,
Weight: primaryWeight,
},
}
tsClone := ts.DeepCopy()
tsClone.Spec.Backends = backends
_, err = sr.smiClient.SplitV1alpha3().TrafficSplits(canary.Namespace).Update(context.TODO(), tsClone, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("TrafficSplit %s.%s update error %v", apexName, canary.Namespace, err)
}
return nil
}
func (sr *Smiv1alpha3Router) makeAnnotations(gateways []string) map[string]string {
res := make(map[string]string)
if sr.targetMesh == "istio" && len(gateways) > 0 {
g, _ := json.Marshal(gateways)
res["VirtualService.v1alpha3.networking.istio.io/spec.gateways"] = string(g)
}
return res
}
func (sr *Smiv1alpha3Router) Finalize(_ *flaggerv1.Canary) error {
return nil
}

View File

@ -0,0 +1,138 @@
/*
Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
smiv1 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha3"
)
func TestSmiv1alpha3Router_Sync(t *testing.T) {
canary := newTestSMICanary()
mocks := newFixture(canary)
router := &Smiv1alpha3Router{
logger: mocks.logger,
flaggerClient: mocks.flaggerClient,
smiClient: mocks.meshClient,
kubeClient: mocks.kubeClient,
}
err := router.Reconcile(canary)
require.NoError(t, err)
// test insert
ts, err := router.smiClient.SplitV1alpha3().TrafficSplits("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)
dests := ts.Spec.Backends
assert.Len(t, dests, 2)
apexName, primaryName, canaryName := canary.GetServiceNames()
assert.Equal(t, ts.Spec.Service, apexName)
var pRoute smiv1.TrafficSplitBackend
var cRoute smiv1.TrafficSplitBackend
for _, dest := range ts.Spec.Backends {
if dest.Service == primaryName {
pRoute = dest
}
if dest.Service == canaryName {
cRoute = dest
}
}
assert.Equal(t, 100, pRoute.Weight)
assert.Equal(t, 0, cRoute.Weight)
// test update
host := "test"
canary.Spec.Service.Name = host
err = router.Reconcile(canary)
require.NoError(t, err)
ts, err = router.smiClient.SplitV1alpha3().TrafficSplits("default").Get(context.TODO(), "test", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, host, ts.Spec.Service)
}
func TestSmiv1alpha3Router_SetRoutes(t *testing.T) {
canary := newTestSMICanary()
mocks := newFixture(canary)
router := &Smiv1alpha3Router{
logger: mocks.logger,
flaggerClient: mocks.flaggerClient,
smiClient: mocks.meshClient,
kubeClient: mocks.kubeClient,
}
err := router.Reconcile(mocks.canary)
require.NoError(t, err)
p, c, m, err := router.GetRoutes(mocks.canary)
require.NoError(t, err)
p = 50
c = 50
m = false
err = router.SetRoutes(mocks.canary, p, c, m)
require.NoError(t, err)
ts, err := router.smiClient.SplitV1alpha3().TrafficSplits("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)
var pRoute smiv1.TrafficSplitBackend
var cRoute smiv1.TrafficSplitBackend
_, primaryName, canaryName := canary.GetServiceNames()
for _, dest := range ts.Spec.Backends {
if dest.Service == primaryName {
pRoute = dest
}
if dest.Service == canaryName {
cRoute = dest
}
}
assert.Equal(t, p, pRoute.Weight)
assert.Equal(t, c, cRoute.Weight)
}
func TestSmiv1alpha3Router_GetRoutes(t *testing.T) {
mocks := newFixture(nil)
router := &Smiv1alpha3Router{
logger: mocks.logger,
flaggerClient: mocks.flaggerClient,
smiClient: mocks.meshClient,
kubeClient: mocks.kubeClient,
}
err := router.Reconcile(mocks.canary)
require.NoError(t, err)
p, c, m, err := router.GetRoutes(mocks.canary)
require.NoError(t, err)
assert.Equal(t, 100, p)
assert.Equal(t, 0, c)
assert.False(t, m)
}