From 9934e14d67a30258448eed0e9219c6d2bd76be04 Mon Sep 17 00:00:00 2001 From: Daishan Date: Tue, 2 Jun 2020 10:44:18 -0700 Subject: [PATCH] istio support, remove cert-manager, update tekton --- cli/cmd/install/install.go | 3 +- cli/cmd/system/viewconfig.go | 4 + cli/pkg/clicontext/config.go | 3 +- cli/pkg/tables/builds.go | 2 +- main.go | 18 + modules/build/controllers/service/service.go | 97 +- modules/build/controllers/stack/stack.go | 59 +- modules/gloo/controller/service/handler.go | 1 - modules/gloo/pkg/vsfactory/acme.go | 88 +- modules/gloo/pkg/vsfactory/app.go | 41 +- modules/gloo/pkg/vsfactory/gloo.go | 154 ++ modules/gloo/pkg/vsfactory/ingress.go | 2 +- modules/gloo/pkg/vsfactory/istio.go | 296 ++++ modules/gloo/pkg/vsfactory/revision.go | 25 +- modules/gloo/pkg/vsfactory/router.go | 47 +- modules/gloo/pkg/vsfactory/target.go | 26 +- modules/gloo/pkg/vsfactory/vsfactory.go | 152 +- .../ingress/controllers/ingress/ingress.go | 1 + modules/ingress/features/feature.go | 9 +- modules/istio/controller/app/handler.go | 60 + modules/istio/controller/gateway/gateway.go | 231 +++ modules/istio/controller/ingress/ingress.go | 241 +++ modules/istio/controller/pkg/pkg.go | 26 + modules/istio/controller/routers/routers.go | 41 + modules/istio/controller/service/handler.go | 50 + modules/istio/features/features.go | 39 + modules/istio/module.go | 12 + .../controllers/account/handler.go | 129 ++ .../controllers/certificate/certificate.go | 151 ++ .../controllers/clusterdomain/controller.go | 46 +- .../letsencrypt/controllers/issuer/handler.go | 135 -- .../controllers/publicdomain/controller.go | 43 +- modules/letsencrypt/features/feature.go | 15 +- modules/letsencrypt/pkg/rdns.go | 148 ++ modules/letsencrypt/pkg/user.go | 78 + modules/linkerd/pkg/injector/injector.go | 16 +- modules/modules.go | 5 + modules/rdns/controllers/service/handler.go | 11 +- modules/smi/controllers/app/handler.go | 5 +- .../admin.rio.cattle.io/v1/certificate.go | 37 + .../v1/zz_generated_deepcopy.go | 104 ++ .../v1/zz_generated_list_types.go | 17 + .../v1/zz_generated_register.go | 3 + pkg/apis/pipeline/v1alpha1/doc.go | 21 + pkg/apis/rio.cattle.io/v1/router.go | 3 + .../rio.cattle.io/v1/zz_generated_deepcopy.go | 5 + pkg/codegen/main.go | 20 + pkg/config/config.go | 2 + pkg/constants/constants.go | 7 + pkg/constructors/constructors.go | 4 +- pkg/features/controller.go | 4 +- pkg/server/crds.go | 3 +- pkg/server/startup.go | 30 +- pkg/stack/systemstack.go | 26 +- pkg/webhook/webhook.go | 3 +- scripts/test | 2 +- stacks/bindata.go | 18 +- stacks/rio-bootstrap-stack.yaml | 26 + stacks/rio-controller-stack.yaml | 3 + stacks/smi-stack.yaml | 5 +- stacks/tekton-stack.yaml | 1345 +++++++++++++---- tests/integration/suite_test.go | 9 +- tests/testutil/service.go | 5 +- types/context.go | 36 +- 64 files changed, 3393 insertions(+), 855 deletions(-) create mode 100644 modules/gloo/pkg/vsfactory/gloo.go create mode 100644 modules/gloo/pkg/vsfactory/istio.go create mode 100644 modules/istio/controller/app/handler.go create mode 100644 modules/istio/controller/gateway/gateway.go create mode 100644 modules/istio/controller/ingress/ingress.go create mode 100644 modules/istio/controller/pkg/pkg.go create mode 100644 modules/istio/controller/routers/routers.go create mode 100644 modules/istio/controller/service/handler.go create mode 100644 modules/istio/features/features.go create mode 100644 modules/istio/module.go create mode 100644 modules/letsencrypt/controllers/account/handler.go create mode 100644 modules/letsencrypt/controllers/certificate/certificate.go delete mode 100644 modules/letsencrypt/controllers/issuer/handler.go create mode 100644 modules/letsencrypt/pkg/rdns.go create mode 100644 modules/letsencrypt/pkg/user.go create mode 100644 pkg/apis/admin.rio.cattle.io/v1/certificate.go create mode 100644 pkg/apis/pipeline/v1alpha1/doc.go diff --git a/cli/cmd/install/install.go b/cli/cmd/install/install.go index 1b86cc844..22e21b7f7 100644 --- a/cli/cmd/install/install.go +++ b/cli/cmd/install/install.go @@ -55,6 +55,7 @@ func (i *Install) Run(ctx *clicontext.CLIContext) error { "RIO_DEBUG": strconv.FormatBool(i.EnableDebug), "IMAGE": fmt.Sprintf("%s:%s", constants.ControllerImage, constants.ControllerImageTag), "RUN_API_VALIDATOR": "\"TRUE\"", + "MESH_MODE": "linkerd", } bootstrapStack.WithAnswer(answers) controllerStack.WithAnswer(answers) @@ -293,7 +294,7 @@ func (i *Install) configureNamespace(ctx *clicontext.CLIContext, systemStack *st var checkFeatures = map[string][]string{ "gloo": {"gateway", "gateway-proxy", "gloo"}, "build": {"buildkitd", "webhook", "tekton-pipelines/tekton-pipelines-webhook", "tekton-pipelines/tekton-pipelines-controller"}, - "letsencrypt": {"cert-manager"}, + "letsencrypt": {}, "autoscaling": {"autoscaler"}, "linkerd": {"linkerd/linkerd-identity", "linkerd/linkerd-tap", "linkerd/linkerd-sp-validator", "linkerd/linkerd-proxy-injector", "linkerd/linkerd-controller", "linkerd/linkerd-grafana", "linkerd/linkerd-web", "linkerd/linkerd-destination", "linkerd/linkerd-prometheus"}, } diff --git a/cli/cmd/system/viewconfig.go b/cli/cmd/system/viewconfig.go index afd7851e5..68971edd8 100644 --- a/cli/cmd/system/viewconfig.go +++ b/cli/cmd/system/viewconfig.go @@ -4,6 +4,7 @@ import ( "bufio" "encoding/json" "fmt" + "sort" "github.com/rancher/rio/cli/cmd/edit/edit" "github.com/rancher/rio/cli/pkg/builder" @@ -129,6 +130,9 @@ func (s *Feature) Run(ctx *clicontext.CLIContext) error { Description: feature.Description, }) } + sort.Slice(data, func(i, j int) bool { + return data[i].Name < data[j].Name + }) writer := tables.NewFeatures(ctx) defer writer.TableWriter().Close() for _, obj := range data { diff --git a/cli/pkg/clicontext/config.go b/cli/pkg/clicontext/config.go index 4714ad93d..307cbd9d9 100644 --- a/cli/pkg/clicontext/config.go +++ b/cli/pkg/clicontext/config.go @@ -5,12 +5,11 @@ import ( "io" "os" - v3 "github.com/rancher/rio/pkg/generated/clientset/versioned/typed/management.cattle.io/v3" - "github.com/pkg/errors" webhookv1 "github.com/rancher/gitwatcher/pkg/generated/clientset/versioned/typed/gitwatcher.cattle.io/v1" v1 "github.com/rancher/rio/pkg/apis/admin.rio.cattle.io/v1" projectv1 "github.com/rancher/rio/pkg/generated/clientset/versioned/typed/admin.rio.cattle.io/v1" + v3 "github.com/rancher/rio/pkg/generated/clientset/versioned/typed/management.cattle.io/v3" riov1 "github.com/rancher/rio/pkg/generated/clientset/versioned/typed/rio.cattle.io/v1" "github.com/rancher/wrangler/pkg/apply" "github.com/rancher/wrangler/pkg/kubeconfig" diff --git a/cli/pkg/tables/builds.go b/cli/pkg/tables/builds.go index bfefc993a..674a1413d 100644 --- a/cli/pkg/tables/builds.go +++ b/cli/pkg/tables/builds.go @@ -65,7 +65,7 @@ func findRevision(data interface{}) (string, error) { if !ok { return "", nil } - for _, param := range m.Spec.Inputs.Resources[0].ResourceSpec.Params { + for _, param := range m.Spec.Resources.Inputs[0].PipelineResourceBinding.ResourceSpec.Params { if param.Name == "revision" { return param.Value, nil } diff --git a/main.go b/main.go index 9bc290b67..8c005e85a 100644 --- a/main.go +++ b/main.go @@ -73,6 +73,24 @@ func main() { Destination: &config.ConfigController.Features, Value: "autoscaling,build", }, + cli.StringFlag{ + Name: "mesh-mode", + Usage: "Specify which mesh-mode to use(linkerd/istio)", + Destination: &config.ConfigController.MeshMode, + Value: "istio", + }, + cli.StringFlag{ + Name: "gateway-service-name", + Usage: "Specify which external gateway service name", + Destination: &config.ConfigController.Gateway.ServiceName, + Value: "", + }, + cli.StringFlag{ + Name: "gateway-service-namespace", + Usage: "Specify which external gateway service namespace", + Destination: &config.ConfigController.Gateway.ServiceNamespace, + Value: "", + }, } app.Flags = append(app.Flags, debug.Flags(&debugConfig)...) app.Action = run diff --git a/modules/build/controllers/service/service.go b/modules/build/controllers/service/service.go index d72d8c599..e4ce49057 100644 --- a/modules/build/controllers/service/service.go +++ b/modules/build/controllers/service/service.go @@ -21,7 +21,8 @@ import ( "github.com/rancher/wrangler/pkg/name" "github.com/rancher/wrangler/pkg/objectset" "github.com/rancher/wrangler/pkg/relatedresource" - tektonv1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + tektonv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + resource "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -168,8 +169,11 @@ func (p populator) populateBuild(buildKey, namespace string, build *riov1.ImageB } dir, fileName := filepath.Join(build.Context, filepath.Dir(build.Dockerfile)), filepath.Base(build.Dockerfile) - taskrun := constructors.NewTaskRun(namespace, trName, tektonv1alpha1.TaskRun{ + taskrun := constructors.NewTaskRun(namespace, trName, tektonv1beta1.TaskRun{ ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "sidecar.istio.io/inject": "false", + }, Labels: map[string]string{ constants.ContainerLabel: buildKey, constants.ServiceLabel: svc.Name, @@ -177,38 +181,44 @@ func (p populator) populateBuild(buildKey, namespace string, build *riov1.ImageB constants.LogTokenLabel: status.BuildLogToken, }, }, - Spec: tektonv1alpha1.TaskRunSpec{ + Spec: tektonv1beta1.TaskRunSpec{ ServiceAccountName: sa.Name, Timeout: timeout, - TaskSpec: &tektonv1alpha1.TaskSpec{ - Inputs: &tektonv1alpha1.Inputs{ - Params: []tektonv1alpha1.ParamSpec{ + TaskSpec: &tektonv1beta1.TaskSpec{ + Resources: &tektonv1beta1.TaskResources{ + Inputs: []tektonv1beta1.TaskResource{ { - Name: "image", - Type: tektonv1alpha1.ParamTypeString, - Description: "Where to publish the resulting image", + ResourceDeclaration: resource.ResourceDeclaration{ + Name: "source", + Type: resource.PipelineResourceTypeGit, + }, }, - { - Name: "docker-context", - Type: tektonv1alpha1.ParamTypeString, - Description: "The context of the build", + }, + }, + Params: []tektonv1beta1.ParamSpec{ + { + Name: "image", + Default: &tektonv1beta1.ArrayOrString{ + Type: tektonv1beta1.ParamTypeString, + StringVal: ImageName(namespace, buildKey, build), }, - { - Name: "insecure-registry", - Type: tektonv1alpha1.ParamTypeString, - Description: "Whether to use insecure registry", + }, + { + Name: "insecure-registry", + Default: &tektonv1beta1.ArrayOrString{ + Type: tektonv1beta1.ParamTypeString, + StringVal: strconv.FormatBool(build.PushRegistry == ""), }, }, - Resources: []tektonv1alpha1.TaskResource{ - { - ResourceDeclaration: tektonv1alpha1.ResourceDeclaration{ - Name: "source", - Type: tektonv1alpha1.PipelineResourceTypeGit, - }, + { + Name: "docker-context", + Default: &tektonv1beta1.ArrayOrString{ + Type: tektonv1beta1.ParamTypeString, + StringVal: build.Context, }, }, }, - Steps: []tektonv1alpha1.Step{ + Steps: []tektonv1beta1.Step{ { Container: corev1.Container{ Name: "build-and-push", @@ -224,45 +234,22 @@ func (p populator) populateBuild(buildKey, namespace string, build *riov1.ImageB "--progress=plain", "--frontend=dockerfile.v0", "--opt", fmt.Sprintf("filename=%s", fileName), - "--local", "context=$(inputs.params.docker-context)", + "--local", "context=$(params.docker-context)", "--local", fmt.Sprintf("dockerfile=%s", dir), - "--output", "type=image,name=$(inputs.params.image),push=true,registry.insecure=$(inputs.params.insecure-registry)", + "--output", "type=image,name=$(params.image),push=true,registry.insecure=$(params.insecure-registry)", }, }, }, }, }, - Inputs: tektonv1alpha1.TaskRunInputs{ - Params: []tektonv1alpha1.Param{ - { - Name: "image", - Value: tektonv1alpha1.ArrayOrString{ - Type: tektonv1alpha1.ParamTypeString, - StringVal: ImageName(namespace, buildKey, build), - }, - }, - { - Name: "insecure-registry", - Value: tektonv1alpha1.ArrayOrString{ - Type: tektonv1alpha1.ParamTypeString, - StringVal: strconv.FormatBool(build.PushRegistry == ""), - }, - }, - { - Name: "docker-context", - Value: tektonv1alpha1.ArrayOrString{ - Type: tektonv1alpha1.ParamTypeString, - StringVal: build.Context, - }, - }, - }, - Resources: []tektonv1alpha1.TaskResourceBinding{ + Resources: &tektonv1beta1.TaskRunResources{ + Inputs: []tektonv1beta1.TaskResourceBinding{ { - PipelineResourceBinding: tektonv1alpha1.PipelineResourceBinding{ + PipelineResourceBinding: tektonv1beta1.PipelineResourceBinding{ Name: "source", - ResourceSpec: &tektonv1alpha1.PipelineResourceSpec{ - Type: tektonv1alpha1.PipelineResourceTypeGit, - Params: []tektonv1alpha1.ResourceParam{ + ResourceSpec: &resource.PipelineResourceSpec{ + Type: tektonv1beta1.PipelineResourceTypeGit, + Params: []tektonv1beta1.ResourceParam{ { Name: "url", Value: build.Repo, diff --git a/modules/build/controllers/stack/stack.go b/modules/build/controllers/stack/stack.go index 3a0e67442..9d976a295 100644 --- a/modules/build/controllers/stack/stack.go +++ b/modules/build/controllers/stack/stack.go @@ -16,7 +16,8 @@ import ( "github.com/rancher/wrangler/pkg/apply" "github.com/rancher/wrangler/pkg/name" "github.com/rancher/wrangler/pkg/objectset" - tektonv1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + tektonv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + resource "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -44,7 +45,7 @@ func Register(ctx context.Context, rContext *types.Context) error { rContext.RBAC.Rbac().V1().Role(), rContext.RBAC.Rbac().V1().RoleBinding(), ), - "BuildDeployed", + "", "stack-service-build", p.populate, nil) @@ -142,27 +143,39 @@ func (p populator) populateBuild(stack *riov1.Stack, systemNamespace string, os rbacs := populateRbac(stack, sa.Name, p.systemNamespace, pod.Name) os.Add(rbacs...) - build := constructors.NewTaskRun(stack.Namespace, trName, tektonv1alpha1.TaskRun{ + build := constructors.NewTaskRun(stack.Namespace, trName, tektonv1beta1.TaskRun{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ "stack-name": stack.Name, "stack-namespace": stack.Namespace, }, }, - Spec: tektonv1alpha1.TaskRunSpec{ + Spec: tektonv1beta1.TaskRunSpec{ ServiceAccountName: sa.Name, - TaskSpec: &tektonv1alpha1.TaskSpec{ - Inputs: &tektonv1alpha1.Inputs{ - Resources: []tektonv1alpha1.TaskResource{ - { - ResourceDeclaration: tektonv1alpha1.ResourceDeclaration{ - Name: "source", - Type: tektonv1alpha1.PipelineResourceTypeGit, + Resources: &tektonv1beta1.TaskRunResources{ + Inputs: []tektonv1beta1.TaskResourceBinding{ + { + PipelineResourceBinding: tektonv1beta1.PipelineResourceBinding{ + Name: "source", + ResourceSpec: &resource.PipelineResourceSpec{ + Type: tektonv1beta1.PipelineResourceTypeGit, + Params: []tektonv1beta1.ResourceParam{ + { + Name: "url", + Value: stack.Spec.Build.Repo, + }, + { + Name: "revision", + Value: rev, + }, + }, }, }, }, }, - Steps: []tektonv1alpha1.Step{ + }, + TaskSpec: &tektonv1beta1.TaskSpec{ + Steps: []tektonv1beta1.Step{ { Container: corev1.Container{ Name: "rio-up", @@ -177,28 +190,6 @@ func (p populator) populateBuild(stack *riov1.Stack, systemNamespace string, os }, }, }, - Inputs: tektonv1alpha1.TaskRunInputs{ - Resources: []tektonv1alpha1.TaskResourceBinding{ - { - PipelineResourceBinding: tektonv1alpha1.PipelineResourceBinding{ - Name: "source", - ResourceSpec: &tektonv1alpha1.PipelineResourceSpec{ - Type: tektonv1alpha1.PipelineResourceTypeGit, - Params: []tektonv1alpha1.ResourceParam{ - { - Name: "url", - Value: stack.Spec.Build.Repo, - }, - { - Name: "revision", - Value: rev, - }, - }, - }, - }, - }, - }, - }, }, }) os.Add(build) diff --git a/modules/gloo/controller/service/handler.go b/modules/gloo/controller/service/handler.go index 353144798..7f6c4313a 100644 --- a/modules/gloo/controller/service/handler.go +++ b/modules/gloo/controller/service/handler.go @@ -4,7 +4,6 @@ import ( "context" "github.com/rancher/rio/modules/gloo/pkg/vsfactory" - riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" rioadminv1controller "github.com/rancher/rio/pkg/generated/controllers/admin.rio.cattle.io/v1" riov1controller "github.com/rancher/rio/pkg/generated/controllers/rio.cattle.io/v1" diff --git a/modules/gloo/pkg/vsfactory/acme.go b/modules/gloo/pkg/vsfactory/acme.go index a37a314e8..b369e88bd 100644 --- a/modules/gloo/pkg/vsfactory/acme.go +++ b/modules/gloo/pkg/vsfactory/acme.go @@ -1,59 +1,77 @@ package vsfactory import ( - "strings" + "fmt" - "github.com/sirupsen/logrus" + "github.com/rancher/rio/pkg/constants" gatewayv1 "github.com/solo-io/gloo/projects/gateway/pkg/api/v1" solov1 "github.com/solo-io/gloo/projects/gateway/pkg/api/v1/kube/apis/gateway.solo.io/v1" v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/core/matchers" "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" - "k8s.io/apimachinery/pkg/labels" + "istio.io/api/networking/v1alpha3" + istiov1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" ) -func (f *VirtualServiceFactory) InjectACME(vs *solov1.VirtualService) error { +func (f *VirtualServiceFactory) InjectACME(vs *solov1.VirtualService) { for _, domain := range vs.Spec.VirtualHost.Domains { - ingresses, err := f.ingresses.List(f.systemNamespace, labels.Everything()) - if err != nil { - return err - } - for _, ing := range ingresses { - if len(ing.OwnerReferences) > 0 && strings.HasPrefix(ing.OwnerReferences[0].Name, domain) && len(ing.Spec.Rules) > 0 { - if len(ing.Spec.Rules) > 0 && ing.Spec.Rules[0].HTTP != nil && len(ing.Spec.Rules[0].HTTP.Paths) > 0 { - logrus.Infof("injecting acme http-01 path for domain %s", domain) - vs.Spec.VirtualHost.Routes = append([]*gatewayv1.Route{ + if _, err := f.publicDomainCache.Get(domain); err == nil { + vs.Spec.VirtualHost.Routes = append([]*gatewayv1.Route{ + { + Matchers: []*matchers.Matcher{ { - Matchers: []*matchers.Matcher{ - { - PathSpecifier: &matchers.Matcher_Exact{ - Exact: ing.Spec.Rules[0].HTTP.Paths[0].Path, - }, - }, + PathSpecifier: &matchers.Matcher_Prefix{ + Prefix: "/.well-known/acme-challenge", }, - Action: &gatewayv1.Route_RouteAction{ - RouteAction: &v1.RouteAction{ - Destination: &v1.RouteAction_Single{ - Single: &v1.Destination{ - DestinationType: &v1.Destination_Kube{ - Kube: &v1.KubernetesServiceDestination{ - Ref: core.ResourceRef{ - Name: ing.Spec.Rules[0].HTTP.Paths[0].Backend.ServiceName, - Namespace: ing.Namespace, - }, - Port: uint32(ing.Spec.Rules[0].HTTP.Paths[0].Backend.ServicePort.IntVal), - }, + }, + }, + Action: &gatewayv1.Route_RouteAction{ + RouteAction: &v1.RouteAction{ + Destination: &v1.RouteAction_Single{ + Single: &v1.Destination{ + DestinationType: &v1.Destination_Kube{ + Kube: &v1.KubernetesServiceDestination{ + Ref: core.ResourceRef{ + Name: constants.AcmeSolverServicName, + Namespace: f.systemNamespace, }, + Port: 8080, }, }, }, }, }, - }, vs.Spec.VirtualHost.Routes...) - } + }, + }, + }, vs.Spec.VirtualHost.Routes...) + } + } + return +} - } +func (f *VirtualServiceFactory) InjectACMEIstio(vs *istiov1alpha3.VirtualService) { + for _, domain := range vs.Spec.Hosts { + if _, err := f.publicDomainCache.Get(domain); err == nil { + vs.Spec.Http = append([]*v1alpha3.HTTPRoute{ + { + Match: []*v1alpha3.HTTPMatchRequest{ + { + Uri: &v1alpha3.StringMatch{ + MatchType: &v1alpha3.StringMatch_Prefix{ + Prefix: "/.well-known/acme-challenge", + }, + }, + }, + }, + Route: []*v1alpha3.HTTPRouteDestination{ + { + Destination: &v1alpha3.Destination{ + Host: fmt.Sprintf("%s.%s.svc.cluster.local", constants.AcmeSolverServicName, f.systemNamespace), + }, + }, + }, + }, + }, vs.Spec.Http...) } } - return nil } diff --git a/modules/gloo/pkg/vsfactory/app.go b/modules/gloo/pkg/vsfactory/app.go index 7362d2397..80dadc658 100644 --- a/modules/gloo/pkg/vsfactory/app.go +++ b/modules/gloo/pkg/vsfactory/app.go @@ -4,9 +4,12 @@ import ( "sort" "time" + "strings" + riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" solov1 "github.com/solo-io/gloo/projects/gateway/pkg/api/v1/kube/apis/gateway.solo.io/v1" gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + istiov1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" ) func (f *VirtualServiceFactory) ForApp(namespace, appName string, svcs []*riov1.Service) ([]*solov1.VirtualService, error) { @@ -19,7 +22,7 @@ func (f *VirtualServiceFactory) ForApp(namespace, appName string, svcs []*riov1. return nil, nil } - vs := newVirtualService(namespace, appName, hostnames, targets...) + vs := newGlooVirtualService(namespace, appName, hostnames, targets...) sort.Slice(svcs, func(i, j int) bool { if svcs[i].Spec.Weight == nil || svcs[j].Spec.Weight == nil { return false @@ -35,9 +38,7 @@ func (f *VirtualServiceFactory) ForApp(namespace, appName string, svcs []*riov1. vs.Spec.VirtualHost.Routes[0].Options.Timeout = &t } - if err := f.InjectACME(vs); err != nil { - return nil, err - } + f.InjectACME(vs) result := []*solov1.VirtualService{ vs, @@ -49,8 +50,38 @@ func (f *VirtualServiceFactory) ForApp(namespace, appName string, svcs []*riov1. } for hostname, tls := range tls { - result = append(result, tlsCopy(hostname, f.systemNamespace, tls, vs)) + if _, err := f.secretCache.Get(f.systemNamespace, tls); err == nil { + result = append(result, tlsCopy(hostname, f.systemNamespace, tls, vs)) + } } return result, nil } + +func (f *VirtualServiceFactory) ForAppIstio(namespace, appName string, svcs []*riov1.Service) (*istiov1alpha3.VirtualService, *istiov1alpha3.DestinationRule, error) { + hostnames, targets, err := f.getTargetsForApp(svcs, f.systemNamespace) + if err != nil { + return nil, nil, err + } + + if len(hostnames) == 0 { + return nil, nil, nil + } + + vs := newIstioVirtualService(namespace, appName, stripPorts(hostnames), targets...) + f.InjectACMEIstio(vs) + dest := newIstioDestinationRule(namespace, appName, targets...) + return vs, dest, nil +} + +func stripPorts(hostnames []string) (result []string) { + seen := map[string]bool{} + for _, hostname := range hostnames { + host := strings.SplitN(hostname, ":", 2)[0] + if !seen[host] { + seen[host] = true + result = append(result, host) + } + } + return result +} diff --git a/modules/gloo/pkg/vsfactory/gloo.go b/modules/gloo/pkg/vsfactory/gloo.go new file mode 100644 index 000000000..fae883b59 --- /dev/null +++ b/modules/gloo/pkg/vsfactory/gloo.go @@ -0,0 +1,154 @@ +package vsfactory + +import ( + "net" + + "github.com/rancher/wrangler/pkg/name" + soloapiv1 "github.com/solo-io/gloo/projects/gateway/pkg/api/v1" + solov1 "github.com/solo-io/gloo/projects/gateway/pkg/api/v1/kube/apis/gateway.solo.io/v1" + gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/core/matchers" + "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/options/headers" + "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/options/retries" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func newGlooVirtualService(namespace, name string, hosts []string, targets ...target) *solov1.VirtualService { + vs := &solov1.VirtualService{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: soloapiv1.VirtualService{ + VirtualHost: &soloapiv1.VirtualHost{ + Domains: hosts, + Routes: []*soloapiv1.Route{ + { + Matchers: []*matchers.Matcher{ + { + PathSpecifier: &matchers.Matcher_Prefix{ + Prefix: "/", + }, + }, + }, + }, + }, + }, + }, + } + + vs.Spec.VirtualHost.Routes[0].Action = newRouteAction(targets...) + vs.Spec.VirtualHost.Routes[0].Options = newRoutePlugin(targets...) + + return vs +} + +func tlsCopy(hostname, tlsNamespace, tlsName string, vs *solov1.VirtualService) *solov1.VirtualService { + vsTLS := vs.DeepCopy() + vsTLS.Name = name.SafeConcatName(vsTLS.Name, "tls", tlsName) + host, _, err := net.SplitHostPort(hostname) + if err != nil { + host = hostname + } + vsTLS.Spec.SslConfig = &gloov1.SslConfig{ + SslSecrets: &gloov1.SslConfig_SecretRef{ + SecretRef: &core.ResourceRef{ + Name: tlsName, + Namespace: tlsNamespace, + }, + }, + SniDomains: []string{ + host, + }, + } + vsTLS.Spec.VirtualHost.Domains = []string{hostname} + return vsTLS +} + +func newRouteAction(targets ...target) *soloapiv1.Route_RouteAction { + if len(targets) == 0 { + return nil + } + + if len(targets) == 1 { + return single(targets[0]) + } + + var dest []*gloov1.WeightedDestination + + for _, target := range targets { + dest = append(dest, &gloov1.WeightedDestination{ + Destination: destination(target), + Weight: uint32(target.Weight), + }) + } + + return &soloapiv1.Route_RouteAction{ + RouteAction: &gloov1.RouteAction{ + Destination: &gloov1.RouteAction_Multi{ + Multi: &gloov1.MultiDestination{ + Destinations: dest, + }, + }, + }, + } +} + +func destination(target target) *gloov1.Destination { + return &gloov1.Destination{ + DestinationType: &gloov1.Destination_Kube{ + Kube: &gloov1.KubernetesServiceDestination{ + Ref: core.ResourceRef{ + Name: target.Name, + Namespace: target.Namespace, + }, + Port: uint32(target.Port), + }, + }, + } +} + +func single(target target) *soloapiv1.Route_RouteAction { + return &soloapiv1.Route_RouteAction{ + RouteAction: &gloov1.RouteAction{ + Destination: &gloov1.RouteAction_Single{ + Single: destination(target), + }, + }, + } +} + +func newRoutePlugin(targets ...target) *v1.RouteOptions { + for _, t := range targets { + // use envoy to retry the connection if the service is being scaled up and increase envoy timeout + if t.ScaleIsZero { + return &v1.RouteOptions{ + Timeout: &overallTimeout, + Retries: &retries.RetryPolicy{ + RetryOn: "5xx", + NumRetries: scaleFromZeroAttempts, + PerTryTimeout: &perTryTimeout, + }, + HeaderManipulation: &headers.HeaderManipulation{ + RequestHeadersToAdd: []*headers.HeaderValueOption{ + { + Header: &headers.HeaderValue{ + Key: rioNameHeader, + Value: t.OriginalTarget.Name, + }, + }, + { + Header: &headers.HeaderValue{ + Key: rioNamespaceHeader, + Value: t.OriginalTarget.Namespace, + }, + }, + }, + }, + } + } + } + return nil +} diff --git a/modules/gloo/pkg/vsfactory/ingress.go b/modules/gloo/pkg/vsfactory/ingress.go index 752e5f58f..887820e48 100644 --- a/modules/gloo/pkg/vsfactory/ingress.go +++ b/modules/gloo/pkg/vsfactory/ingress.go @@ -9,7 +9,7 @@ import ( ) func (f *VirtualServiceFactory) ForIngress(ingress *v1beta1.Ingress) ([]runtime.Object, error) { - vs := newVirtualService(ingress.Namespace, ingress.Name, nil) + vs := newGlooVirtualService(ingress.Namespace, ingress.Name, nil) vs.Spec.VirtualHost.Routes = nil for _, rule := range ingress.Spec.Rules { vs.Spec.VirtualHost.Domains = append(vs.Spec.VirtualHost.Domains, rule.Host) diff --git a/modules/gloo/pkg/vsfactory/istio.go b/modules/gloo/pkg/vsfactory/istio.go new file mode 100644 index 000000000..90d77e24d --- /dev/null +++ b/modules/gloo/pkg/vsfactory/istio.go @@ -0,0 +1,296 @@ +package vsfactory + +import ( + "fmt" + + "github.com/gogo/protobuf/types" + "github.com/rancher/rio/modules/istio/controller/pkg" + riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" + "github.com/rancher/rio/pkg/constants" + networkingv1alpha3 "istio.io/api/networking/v1alpha3" + istiov1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func newIstioVirtualService(namespace, name string, hosts []string, targets ...target) *istiov1alpha3.VirtualService { + gateways := []string{ + fmt.Sprintf("%s/%s", constants.IstioSystemNamespace, constants.IstioRioGateway), + "mesh", + } + + var dests []*networkingv1alpha3.HTTPRouteDestination + if len(targets) == 1 { + targets[0].Weight = 100 + } + for _, target := range targets { + dests = append(dests, &networkingv1alpha3.HTTPRouteDestination{ + Destination: &networkingv1alpha3.Destination{ + Host: fmt.Sprintf("%s.%s.svc.cluster.local", target.App, target.Namespace), + Subset: target.Version, + }, + Weight: int32(target.Weight), + }) + } + vs := &istiov1alpha3.VirtualService{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Spec: networkingv1alpha3.VirtualService{ + Hosts: append(hosts, name), + Gateways: gateways, + Http: []*networkingv1alpha3.HTTPRoute{ + { + Match: []*networkingv1alpha3.HTTPMatchRequest{ + { + Port: 80, + }, + { + Port: 443, + }, + }, + Route: dests, + }, + }, + }, + } + + return vs +} + +func newIstioDestinationRule(namespace, name string, targets ...target) *istiov1alpha3.DestinationRule { + subsets := []*networkingv1alpha3.Subset{ + { + Name: "all", + Labels: map[string]string{ + "app": name, + }, + }, + } + for _, target := range targets { + subsets = append(subsets, &networkingv1alpha3.Subset{ + Name: target.Version, + Labels: map[string]string{ + "app": name, + "version": target.Version, + }, + }) + } + return &istiov1alpha3.DestinationRule{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Spec: networkingv1alpha3.DestinationRule{ + Host: fmt.Sprintf("%s.%s.svc.cluster.local", name, namespace), + Subsets: subsets, + }, + } +} + +func (f *VirtualServiceFactory) ForIstioRouter(router *riov1.Router) (*istiov1alpha3.VirtualService, error) { + dms, err := pkg.Domains(router) + if err != nil { + return nil, err + } + + vs := &istiov1alpha3.VirtualService{ + ObjectMeta: metav1.ObjectMeta{ + Name: router.Name, + Namespace: router.Namespace, + }, + Spec: networkingv1alpha3.VirtualService{ + Gateways: []string{ + "mesh", + }, + Hosts: dms, + }, + } + if !router.Spec.Internal { + vs.Spec.Gateways = append(vs.Spec.Gateways, + fmt.Sprintf("%s.%s.svc.cluster.local", constants.IstioRioGateway, constants.IstioSystemNamespace)) + } + for _, route := range router.Spec.Routes { + r := &networkingv1alpha3.HTTPRoute{} + + r.Match = []*networkingv1alpha3.HTTPMatchRequest{matchToIstioMatch(route.Match)} + r.Redirect = redirectToIstio(route.Redirect) + r.Rewrite = rewriteToIstio(route.Rewrite) + r.Retries = retryToIstio(route.Retry) + r.Fault = faultToIstio(route.Fault) + r.Mirror = mirrorToIstio(route.Mirror) + r.Headers = headerToIstio(route.Headers) + r.Route = toIstioRoute(route.To) + + if route.TimeoutSeconds != nil { + r.Timeout = &types.Duration{ + Seconds: int64(*route.TimeoutSeconds), + } + } + vs.Spec.Http = []*networkingv1alpha3.HTTPRoute{r} + } + f.InjectACMEIstio(vs) + + return vs, nil +} + +func toIstioRoute(to []riov1.WeightedDestination) []*networkingv1alpha3.HTTPRouteDestination { + var r []*networkingv1alpha3.HTTPRouteDestination + for _, t := range to { + r = append(r, &networkingv1alpha3.HTTPRouteDestination{ + Destination: &networkingv1alpha3.Destination{ + Host: t.App, + Subset: t.Version, + }, + Weight: int32(t.Weight), + }) + } + return r +} + +func headerToIstio(header *riov1.HeaderOperations) *networkingv1alpha3.Headers { + if header == nil { + return nil + } + + return &networkingv1alpha3.Headers{ + Request: &networkingv1alpha3.Headers_HeaderOperations{ + Set: nameValuesToMap(header.Set), + Add: nameValuesToMap(header.Add), + Remove: header.Remove, + }, + } +} + +func mirrorToIstio(mirror *riov1.Destination) *networkingv1alpha3.Destination { + if mirror == nil { + return nil + } + + return &networkingv1alpha3.Destination{ + Host: mirror.App, + Subset: mirror.Version, + Port: &networkingv1alpha3.PortSelector{Number: mirror.Port}, + } +} + +func nameValuesToMap(headers []riov1.NameValue) map[string]string { + r := map[string]string{} + for _, header := range headers { + r[header.Name] = header.Value + } + return r +} + +func faultToIstio(fault *riov1.Fault) *networkingv1alpha3.HTTPFaultInjection { + if fault == nil { + return nil + } + + f := &networkingv1alpha3.HTTPFaultInjection{ + Delay: &networkingv1alpha3.HTTPFaultInjection_Delay{ + HttpDelayType: &networkingv1alpha3.HTTPFaultInjection_Delay_FixedDelay{ + FixedDelay: &types.Duration{ + Nanos: int32(fault.DelayMillis * 100), + }}, + Percentage: &networkingv1alpha3.Percent{ + Value: float64(fault.Percentage), + }, + }, + } + + if fault.AbortHTTPStatus != 0 { + f.Abort = &networkingv1alpha3.HTTPFaultInjection_Abort{ + Percentage: &networkingv1alpha3.Percent{ + Value: float64(fault.Percentage), + }, + ErrorType: &networkingv1alpha3.HTTPFaultInjection_Abort_HttpStatus{ + HttpStatus: int32(fault.AbortHTTPStatus), + }, + } + } + return f +} + +func retryToIstio(retry *riov1.Retry) *networkingv1alpha3.HTTPRetry { + if retry == nil { + return nil + } + + return &networkingv1alpha3.HTTPRetry{ + Attempts: int32(retry.Attempts), + PerTryTimeout: &types.Duration{Seconds: int64(retry.TimeoutSeconds)}, + } +} + +func rewriteToIstio(rewrite *riov1.Rewrite) *networkingv1alpha3.HTTPRewrite { + if rewrite == nil { + return nil + } + return &networkingv1alpha3.HTTPRewrite{ + Uri: rewrite.Path, + Authority: rewrite.Host, + } +} + +func redirectToIstio(redirect *riov1.Redirect) *networkingv1alpha3.HTTPRedirect { + if redirect == nil { + return nil + } + return &networkingv1alpha3.HTTPRedirect{ + Authority: redirect.Host, + Uri: redirect.Path, + } +} + +func matchToIstioMatch(match riov1.Match) *networkingv1alpha3.HTTPMatchRequest { + return &networkingv1alpha3.HTTPMatchRequest{ + Uri: stringMatch(match.Path), + Scheme: stringMatch(match.Schema), + Method: methodToMatch(match.Methods), + Headers: headerMatch(match.Headers), + } +} + +// only support one match +func methodToMatch(methods []string) *networkingv1alpha3.StringMatch { + if len(methods) == 0 { + return nil + } + return &networkingv1alpha3.StringMatch{ + MatchType: &networkingv1alpha3.StringMatch_Exact{Exact: methods[0]}, + } +} + +func headerMatch(headers []riov1.HeaderMatch) map[string]*networkingv1alpha3.StringMatch { + r := map[string]*networkingv1alpha3.StringMatch{} + for _, hm := range headers { + r[hm.Name] = stringMatch(hm.Value) + } + return r +} + +func stringMatch(sm *riov1.StringMatch) *networkingv1alpha3.StringMatch { + if sm == nil { + return nil + } + if sm.Prefix != "" { + return &networkingv1alpha3.StringMatch{ + MatchType: &networkingv1alpha3.StringMatch_Prefix{Prefix: sm.Prefix}, + } + } + + if sm.Regexp != "" { + return &networkingv1alpha3.StringMatch{ + MatchType: &networkingv1alpha3.StringMatch_Regex{Regex: sm.Regexp}, + } + } + + if sm.Exact != "" { + return &networkingv1alpha3.StringMatch{ + MatchType: &networkingv1alpha3.StringMatch_Exact{Exact: sm.Exact}, + } + } + + return nil +} diff --git a/modules/gloo/pkg/vsfactory/revision.go b/modules/gloo/pkg/vsfactory/revision.go index db15feb68..09ed2b089 100644 --- a/modules/gloo/pkg/vsfactory/revision.go +++ b/modules/gloo/pkg/vsfactory/revision.go @@ -1,12 +1,14 @@ package vsfactory import ( + "fmt" "time" riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" "github.com/rancher/rio/pkg/services" solov1 "github.com/solo-io/gloo/projects/gateway/pkg/api/v1/kube/apis/gateway.solo.io/v1" gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" + istiov1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" ) func (f *VirtualServiceFactory) ForRevision(svc *riov1.Service) ([]*solov1.VirtualService, error) { @@ -17,7 +19,7 @@ func (f *VirtualServiceFactory) ForRevision(svc *riov1.Service) ([]*solov1.Virtu return nil, err } - vs := newVirtualService(target.Namespace, target.Name, target.Hosts, target) + vs := newGlooVirtualService(target.Namespace, target.Name, target.Hosts, target) if svc.Spec.RequestTimeoutSeconds != nil { t := time.Duration(int64(*svc.Spec.RequestTimeoutSeconds)) * time.Second @@ -27,9 +29,7 @@ func (f *VirtualServiceFactory) ForRevision(svc *riov1.Service) ([]*solov1.Virtu vs.Spec.VirtualHost.Routes[0].Options.Timeout = &t } - if err := f.InjectACME(vs); err != nil { - return nil, err - } + f.InjectACME(vs) result := []*solov1.VirtualService{ vs, @@ -41,8 +41,23 @@ func (f *VirtualServiceFactory) ForRevision(svc *riov1.Service) ([]*solov1.Virtu } for hostname, tls := range tls { - result = append(result, tlsCopy(hostname, f.systemNamespace, tls, vs)) + if _, err := f.secretCache.Get(f.systemNamespace, tls); err == nil { + result = append(result, tlsCopy(hostname, f.systemNamespace, tls, vs)) + } } return result, nil } + +func (f *VirtualServiceFactory) ForIstioRevision(svc *riov1.Service) (*istiov1alpha3.VirtualService, error) { + app, version := services.AppAndVersion(svc) + + target, err := f.getTarget(svc, f.systemNamespace) + if err != nil || !target.valid() { + return nil, err + } + + vs := newIstioVirtualService(svc.Namespace, fmt.Sprintf("%s-%s", app, version), stripPorts(target.Hosts), target) + f.InjectACMEIstio(vs) + return vs, nil +} diff --git a/modules/gloo/pkg/vsfactory/router.go b/modules/gloo/pkg/vsfactory/router.go index bbc8a0b96..4e9bb7dad 100644 --- a/modules/gloo/pkg/vsfactory/router.go +++ b/modules/gloo/pkg/vsfactory/router.go @@ -1,9 +1,10 @@ package vsfactory import ( - "net/url" "time" + "github.com/rancher/rio/modules/istio/controller/pkg" + "github.com/gogo/protobuf/types" riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" name2 "github.com/rancher/wrangler/pkg/name" @@ -14,7 +15,7 @@ import ( "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/options/faultinjection" "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/options/headers" "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/options/retries" - solovcorev1 "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -38,9 +39,7 @@ func (f *VirtualServiceFactory) ForRouter(router *riov1.Router) ([]*solov1.Virtu } configureHTTPSRedirect(router, result) - if err := f.InjectACME(vs); err != nil { - return nil, err - } + f.InjectACME(vs) return result, nil } @@ -72,7 +71,7 @@ func vsForRouter(router *riov1.Router) (*solov1.VirtualService, error) { }, } - domains, err := domains(router) + domains, err := pkg.Domains(router) if err != nil { return nil, err } @@ -86,25 +85,6 @@ func vsForRouter(router *riov1.Router) (*solov1.VirtualService, error) { return vs, nil } -func domains(router *riov1.Router) (result []string, err error) { - seen := map[string]bool{} - for _, endpoint := range router.Status.Endpoints { - u, err := url.Parse(endpoint) - if err != nil { - return nil, err - } - - if seen[u.Host] { - continue - } - seen[u.Host] = true - - result = append(result, u.Host) - } - - return -} - func headersToHeaders(hs *riov1.HeaderOperations) *headers.HeaderManipulation { result := &headers.HeaderManipulation{} @@ -228,7 +208,7 @@ func addSingleDestination(namespace string, route riov1.RouteSpec, gatewayRoute func addMirror(namespace string, route riov1.RouteSpec, gatewayRoute *gatewayv1.Route) { gatewayRoute.Action = &gatewayv1.Route_DelegateAction{ - DelegateAction: destinationToRef(namespace, route.Mirror), + DelegateAction: destinationToAction(namespace, route.Mirror), } } @@ -281,12 +261,23 @@ func addRetry(route riov1.RouteSpec, gatewayRoute *gatewayv1.Route) { } } -func destinationToRef(namespace string, dest *riov1.Destination) *solovcorev1.ResourceRef { +func destinationToAction(namespace string, dest *riov1.Destination) *gatewayv1.DelegateAction { + name := dest.App + if dest.Version != "" { + name = name2.SafeConcatName(name + "-" + dest.Version) + } + return &gatewayv1.DelegateAction{ + Name: name, + Namespace: namespace, + } +} + +func destinationToRef(namespace string, dest *riov1.Destination) *core.ResourceRef { name := dest.App if dest.Version != "" { name = name2.SafeConcatName(name + "-" + dest.Version) } - return &solovcorev1.ResourceRef{ + return &core.ResourceRef{ Name: name, Namespace: namespace, } diff --git a/modules/gloo/pkg/vsfactory/target.go b/modules/gloo/pkg/vsfactory/target.go index 8232a4a81..007bdd739 100644 --- a/modules/gloo/pkg/vsfactory/target.go +++ b/modules/gloo/pkg/vsfactory/target.go @@ -24,6 +24,8 @@ type target struct { Hosts []string Port int32 Name string + App string + Version string Namespace string Weight int ScaleIsZero bool @@ -42,6 +44,8 @@ func (f *VirtualServiceFactory) getTarget(obj *riov1.Service, systemNamespace st app, version := services.AppAndVersion(obj) result.Name = name.SafeConcatName(app, version) result.Namespace = obj.Namespace + result.Version = version + result.App = app if obj.Status.ComputedWeight != nil { result.Weight = *obj.Status.ComputedWeight } else if obj.Spec.Weight != nil { @@ -77,7 +81,6 @@ func (f *VirtualServiceFactory) getTarget(obj *riov1.Service, systemNamespace st result.Name = constants.AutoscalerServiceName result.Namespace = systemNamespace } - } return @@ -149,11 +152,10 @@ func (f *VirtualServiceFactory) getTargetsForApp(svcs []*riov1.Service, systemNa return nil, nil, err } hostname := u.Host - if seen[hostname] { - continue + if !seen[hostname] { + seen[hostname] = true + hostnames = append(hostnames, hostname) } - seen[hostname] = true - hostnames = append(hostnames, hostname) } if target.Weight != 0 { weightSet = true @@ -165,6 +167,20 @@ func (f *VirtualServiceFactory) getTargetsForApp(svcs []*riov1.Service, systemNa for i := range targets { targets[i].Weight = 1 } + } else { + totalWeight := 0 + for i := range targets { + totalWeight += targets[i].Weight + } + addedWeight := 0 + for i := range targets { + if i == len(targets)-1 { + targets[i].Weight = 100 - addedWeight + break + } + targets[i].Weight = int(float64(targets[i].Weight) / float64(totalWeight) * 100) + addedWeight += targets[i].Weight + } } return diff --git a/modules/gloo/pkg/vsfactory/vsfactory.go b/modules/gloo/pkg/vsfactory/vsfactory.go index 0326673a9..27df98ad5 100644 --- a/modules/gloo/pkg/vsfactory/vsfactory.go +++ b/modules/gloo/pkg/vsfactory/vsfactory.go @@ -1,23 +1,13 @@ package vsfactory import ( - "net" "time" rioadminv1controller "github.com/rancher/rio/pkg/generated/controllers/admin.rio.cattle.io/v1" "github.com/rancher/rio/types" corev1 "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1" + corev1controller "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1" extensionsv1beta1controller "github.com/rancher/wrangler-api/pkg/generated/controllers/extensions/v1beta1" - "github.com/rancher/wrangler/pkg/name" - soloapiv1 "github.com/solo-io/gloo/projects/gateway/pkg/api/v1" - solov1 "github.com/solo-io/gloo/projects/gateway/pkg/api/v1/kube/apis/gateway.solo.io/v1" - gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" - v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" - "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/core/matchers" - "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/options/headers" - "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/options/retries" - "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const ( @@ -32,6 +22,7 @@ var overallTimeout = time.Second * 30 var perTryTimeout = time.Second * 2 type VirtualServiceFactory struct { + secretCache corev1controller.SecretCache clusterDomainCache rioadminv1controller.ClusterDomainCache publicDomainCache rioadminv1controller.PublicDomainCache ingresses extensionsv1beta1controller.IngressCache @@ -41,6 +32,7 @@ type VirtualServiceFactory struct { func New(rContext *types.Context) *VirtualServiceFactory { return &VirtualServiceFactory{ + secretCache: rContext.Core.Core().V1().Secret().Cache(), clusterDomainCache: rContext.Admin.Admin().V1().ClusterDomain().Cache(), publicDomainCache: rContext.Admin.Admin().V1().PublicDomain().Cache(), systemNamespace: rContext.Namespace, @@ -48,141 +40,3 @@ func New(rContext *types.Context) *VirtualServiceFactory { endpoints: rContext.Core.Core().V1().Endpoints().Cache(), } } - -func newVirtualService(namespace, name string, hosts []string, targets ...target) *solov1.VirtualService { - vs := &solov1.VirtualService{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: soloapiv1.VirtualService{ - VirtualHost: &soloapiv1.VirtualHost{ - Domains: hosts, - Routes: []*soloapiv1.Route{ - { - Matchers: []*matchers.Matcher{ - { - PathSpecifier: &matchers.Matcher_Prefix{ - Prefix: "/", - }, - }, - }, - }, - }, - }, - }, - } - - vs.Spec.VirtualHost.Routes[0].Action = newRouteAction(targets...) - vs.Spec.VirtualHost.Routes[0].Options = newRoutePlugin(targets...) - - return vs -} - -func tlsCopy(hostname, tlsNamespace, tlsName string, vs *solov1.VirtualService) *solov1.VirtualService { - vsTLS := vs.DeepCopy() - vsTLS.Name = name.SafeConcatName(vsTLS.Name, "tls", tlsName) - host, _, err := net.SplitHostPort(hostname) - if err != nil { - host = hostname - } - vsTLS.Spec.SslConfig = &gloov1.SslConfig{ - SslSecrets: &gloov1.SslConfig_SecretRef{ - SecretRef: &core.ResourceRef{ - Name: tlsName, - Namespace: tlsNamespace, - }, - }, - SniDomains: []string{ - host, - }, - } - vsTLS.Spec.VirtualHost.Domains = []string{hostname} - return vsTLS -} - -func newRouteAction(targets ...target) *soloapiv1.Route_RouteAction { - if len(targets) == 0 { - return nil - } - - if len(targets) == 1 { - return single(targets[0]) - } - - var dest []*gloov1.WeightedDestination - - for _, target := range targets { - dest = append(dest, &gloov1.WeightedDestination{ - Destination: destination(target), - Weight: uint32(target.Weight), - }) - } - - return &soloapiv1.Route_RouteAction{ - RouteAction: &gloov1.RouteAction{ - Destination: &gloov1.RouteAction_Multi{ - Multi: &gloov1.MultiDestination{ - Destinations: dest, - }, - }, - }, - } -} - -func destination(target target) *gloov1.Destination { - return &gloov1.Destination{ - DestinationType: &gloov1.Destination_Kube{ - Kube: &gloov1.KubernetesServiceDestination{ - Ref: core.ResourceRef{ - Name: target.Name, - Namespace: target.Namespace, - }, - Port: uint32(target.Port), - }, - }, - } -} - -func single(target target) *soloapiv1.Route_RouteAction { - return &soloapiv1.Route_RouteAction{ - RouteAction: &gloov1.RouteAction{ - Destination: &gloov1.RouteAction_Single{ - Single: destination(target), - }, - }, - } -} - -func newRoutePlugin(targets ...target) *v1.RouteOptions { - for _, t := range targets { - // use envoy to retry the connection if the service is being scaled up and increase envoy timeout - if t.ScaleIsZero { - return &v1.RouteOptions{ - Timeout: &overallTimeout, - Retries: &retries.RetryPolicy{ - RetryOn: "5xx", - NumRetries: scaleFromZeroAttempts, - PerTryTimeout: &perTryTimeout, - }, - HeaderManipulation: &headers.HeaderManipulation{ - RequestHeadersToAdd: []*headers.HeaderValueOption{ - { - Header: &headers.HeaderValue{ - Key: rioNameHeader, - Value: t.OriginalTarget.Name, - }, - }, - { - Header: &headers.HeaderValue{ - Key: rioNamespaceHeader, - Value: t.OriginalTarget.Namespace, - }, - }, - }, - }, - } - } - } - return nil -} diff --git a/modules/ingress/controllers/ingress/ingress.go b/modules/ingress/controllers/ingress/ingress.go index f0e0ae845..b6c05bac4 100644 --- a/modules/ingress/controllers/ingress/ingress.go +++ b/modules/ingress/controllers/ingress/ingress.go @@ -36,6 +36,7 @@ func Register(ctx context.Context, rContext *types.Context) error { "ingress-services", h.generateFromService, nil) + return nil } diff --git a/modules/ingress/features/feature.go b/modules/ingress/features/feature.go index 5c01277e1..212d454fe 100644 --- a/modules/ingress/features/feature.go +++ b/modules/ingress/features/feature.go @@ -25,7 +25,7 @@ func Register(ctx context.Context, rContext *types.Context) error { // if running in arm or gloo feature is not enabled, enable ingress enabled := false - if !arch.IsAmd64() || (cfg.Features["gloo"].Enabled != nil && !*cfg.Features["gloo"].Enabled) { + if !arch.IsAmd64() || (!featureEnabled(cfg, "gloo") && !featureEnabled(cfg, "istio")) { enabled = true } feature := &features.FeatureController{ @@ -41,3 +41,10 @@ func Register(ctx context.Context, rContext *types.Context) error { } return feature.Register() } + +func featureEnabled(cfg config.Config, name string) bool { + if cfg.Features[name].Enabled != nil { + return *cfg.Features[name].Enabled + } + return false +} diff --git a/modules/istio/controller/app/handler.go b/modules/istio/controller/app/handler.go new file mode 100644 index 000000000..22513bdde --- /dev/null +++ b/modules/istio/controller/app/handler.go @@ -0,0 +1,60 @@ +package app + +import ( + "context" + "fmt" + + "github.com/rancher/rio/modules/gloo/pkg/vsfactory" + riov1controller "github.com/rancher/rio/pkg/generated/controllers/rio.cattle.io/v1" + "github.com/rancher/rio/pkg/indexes" + "github.com/rancher/rio/types" + corev1controller "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func Register(ctx context.Context, rContext *types.Context) error { + h := handler{ + serviceCache: rContext.Rio.Rio().V1().Service().Cache(), + vsFactory: vsfactory.New(rContext), + } + corev1controller.RegisterServiceGeneratingHandler(ctx, + rContext.Core.Core().V1().Service(), + rContext.Apply. + WithCacheTypes(rContext.Istio.Networking().V1alpha3().VirtualService(), + rContext.Istio.Networking().V1alpha3().DestinationRule()), + "", + "istio-app", + h.generate, + nil) + + return nil +} + +type handler struct { + serviceCache riov1controller.ServiceCache + vsFactory *vsfactory.VirtualServiceFactory +} + +func (h handler) generate(svc *v1.Service, status v1.ServiceStatus) ([]runtime.Object, v1.ServiceStatus, error) { + app := svc.Labels["rio.cattle.io/app"] + if app == "" { + return nil, status, nil + } + + svcs, err := h.serviceCache.GetByIndex(indexes.ServiceByApp, fmt.Sprintf("%s/%s", svc.Namespace, app)) + if err != nil { + return nil, status, err + } + + if len(svcs) == 0 { + return nil, status, nil + } + + vss, dest, err := h.vsFactory.ForAppIstio(svc.Namespace, app, svcs) + if err != nil { + return nil, status, err + } + + return []runtime.Object{vss, dest}, status, nil +} diff --git a/modules/istio/controller/gateway/gateway.go b/modules/istio/controller/gateway/gateway.go new file mode 100644 index 000000000..bd7e8ccf3 --- /dev/null +++ b/modules/istio/controller/gateway/gateway.go @@ -0,0 +1,231 @@ +package gateway + +import ( + "context" + "fmt" + "strings" + + adminv1 "github.com/rancher/rio/pkg/apis/admin.rio.cattle.io/v1" + "github.com/rancher/rio/pkg/config" + "github.com/rancher/rio/pkg/constants" + "github.com/rancher/rio/pkg/constructors" + adminv1controller "github.com/rancher/rio/pkg/generated/controllers/admin.rio.cattle.io/v1" + istiov1alpha3controller "github.com/rancher/rio/pkg/generated/controllers/networking.istio.io/v1alpha3" + "github.com/rancher/rio/types" + corev1controller "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1" + "github.com/rancher/wrangler/pkg/name" + "github.com/rancher/wrangler/pkg/relatedresource" + networkingv1alpha3 "istio.io/api/networking/v1alpha3" + istiov1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" +) + +func Register(ctx context.Context, rContext *types.Context) error { + h := handler{ + systemNamespace: rContext.Namespace, + secrets: rContext.Core.Core().V1().Secret().Cache(), + configmaps: rContext.Core.Core().V1().ConfigMap().Cache(), + gateways: rContext.Istio.Networking().V1alpha3().Gateway(), + clusterdomains: rContext.Admin.Admin().V1().ClusterDomain().Cache(), + publicdomains: rContext.Admin.Admin().V1().PublicDomain().Cache(), + } + + relatedresource.Watch(ctx, "gateway-enqueue", h.resolver, + enqueuer{Context: rContext}, + rContext.Core.Core().V1().Secret(), + rContext.Core.Core().V1().ConfigMap(), + rContext.Admin.Admin().V1().PublicDomain(), + rContext.Admin.Admin().V1().ClusterDomain()) + + adminv1controller.RegisterRioInfoGeneratingHandler(ctx, + rContext.Admin.Admin().V1().RioInfo(), + rContext.Apply. + WithCacheTypes(rContext.Istio.Networking().V1alpha3().Gateway(), + rContext.Core.Core().V1().Secret()), + "", + "istio-app", + h.generate, + nil) + return nil +} + +type enqueuer struct { + Context *types.Context +} + +func (e enqueuer) Enqueue(namespace, name string) { + e.Context.Admin.Admin().V1().RioInfo().Enqueue(name) +} + +func (h handler) resolver(namespace, name string, obj runtime.Object) ([]relatedresource.Key, error) { + switch obj.(type) { + case *v1.ConfigMap: + if name == "rio" && namespace == h.systemNamespace { + return []relatedresource.Key{ + { + Namespace: h.systemNamespace, + Name: "rio", + }, + }, nil + } + case *v1.Secret: + if strings.HasSuffix(name, "tls") { + return []relatedresource.Key{ + { + Namespace: h.systemNamespace, + Name: "rio", + }, + }, nil + } + default: + return []relatedresource.Key{ + { + Namespace: h.systemNamespace, + Name: "rio", + }, + }, nil + } + return nil, nil +} + +type handler struct { + systemNamespace string + secrets corev1controller.SecretCache + configmaps corev1controller.ConfigMapCache + gateways istiov1alpha3controller.GatewayController + clusterdomains adminv1controller.ClusterDomainCache + publicdomains adminv1controller.PublicDomainCache +} + +func (h handler) generate(obj *adminv1.RioInfo, status adminv1.RioInfoStatus) ([]runtime.Object, adminv1.RioInfoStatus, error) { + if obj == nil || obj.DeletionTimestamp != nil { + return nil, status, nil + } + + if obj.Name != "rio" { + return nil, status, nil + } + + cm, err := h.configmaps.Get(h.systemNamespace, "rio") + if err != nil { + return nil, status, err + } + + conf, err := config.FromConfigMap(cm) + if err != nil { + return nil, status, err + } + + // set default labels selector, or can be overridden by feature configuration + labelSelectors := map[string]string{ + "app": "istio-ingressgateway", + } + if conf.Features["istio"].Options != nil { + parts := strings.Split(conf.Features["istio"].Options["labelSelectors"], "=") + if len(parts) > 1 { + labelSelectors[parts[0]] = parts[1] + } + } + + var result []runtime.Object + + var servers []*networkingv1alpha3.Server + clusterdomains, err := h.clusterdomains.List(labels.Everything()) + if err != nil { + return nil, status, err + } + publicdomains, err := h.publicdomains.List(labels.Everything()) + if err != nil { + return nil, status, err + } + for _, cd := range clusterdomains { + secretName := name.SafeConcatName(cd.Name, "tls") + if cd.Spec.SecretName != "" { + secretName = cd.Spec.SecretName + } + secret := h.copySecret(h.systemNamespace, secretName) + if secret != nil { + result = append(result, secret) + } + servers = append(servers, + &networkingv1alpha3.Server{ + Port: &networkingv1alpha3.Port{ + Name: "http", + Protocol: "HTTP", + Number: 80, + }, + Hosts: []string{fmt.Sprintf("*.%s", cd.Name)}, + }, + &networkingv1alpha3.Server{ + Port: &networkingv1alpha3.Port{ + Name: "https", + Protocol: "HTTPS", + Number: 443, + }, + Hosts: []string{fmt.Sprintf("*.%s", cd.Name)}, + Tls: &networkingv1alpha3.ServerTLSSettings{ + Mode: networkingv1alpha3.ServerTLSSettings_SIMPLE, + CredentialName: secretName, + }, + }) + } + for _, cd := range publicdomains { + secretName := name.SafeConcatName(cd.Name, "tls") + if cd.Spec.SecretName != "" { + secretName = cd.Spec.SecretName + } + secret := h.copySecret(h.systemNamespace, secretName) + if secret != nil { + result = append(result, secret) + } + servers = append(servers, + &networkingv1alpha3.Server{ + Port: &networkingv1alpha3.Port{ + Name: cd.Name + "-http", + Protocol: "HTTP", + Number: 80, + }, + Hosts: []string{cd.Name}, + }, + &networkingv1alpha3.Server{ + Port: &networkingv1alpha3.Port{ + Name: cd.Name + "-https", + Protocol: "HTTPS", + Number: 443, + }, + Hosts: []string{cd.Name}, + Tls: &networkingv1alpha3.ServerTLSSettings{ + HttpsRedirect: true, + CredentialName: secretName, + }, + }) + } + + gateway := &istiov1alpha3.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rio-gateway", + Namespace: constants.IstioSystemNamespace, + }, + Spec: networkingv1alpha3.Gateway{ + Servers: servers, + Selector: labelSelectors, + }, + } + result = append(result, gateway) + + return result, status, nil +} + +func (h handler) copySecret(namespace, name string) *v1.Secret { + existingSecret, err := h.secrets.Get(namespace, name) + if err != nil { + return nil + } + secret := constructors.NewSecret(constants.IstioSystemNamespace, name, v1.Secret{ + Data: existingSecret.Data, + }) + return secret +} diff --git a/modules/istio/controller/ingress/ingress.go b/modules/istio/controller/ingress/ingress.go new file mode 100644 index 000000000..2df4e02b1 --- /dev/null +++ b/modules/istio/controller/ingress/ingress.go @@ -0,0 +1,241 @@ +package ingress + +import ( + "context" + "fmt" + "net/url" + + "github.com/rancher/rio/modules/istio/controller/pkg" + + "github.com/rancher/rio/modules/gloo/pkg/vsfactory" + riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" + "github.com/rancher/rio/pkg/config" + "github.com/rancher/rio/pkg/constructors" + riov1controller "github.com/rancher/rio/pkg/generated/controllers/rio.cattle.io/v1" + "github.com/rancher/rio/pkg/indexes" + "github.com/rancher/rio/pkg/serviceports" + "github.com/rancher/rio/pkg/services" + "github.com/rancher/rio/types" + corev1controller "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1" + "github.com/rancher/wrangler/pkg/generic" + "github.com/rancher/wrangler/pkg/name" + corev1 "k8s.io/api/core/v1" + "k8s.io/api/extensions/v1beta1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func Register(ctx context.Context, rContext *types.Context) error { + h := handler{ + namespace: rContext.Namespace, + secrets: rContext.Core.Core().V1().Secret(), + vsFactory: vsfactory.New(rContext), + serviceCache: rContext.Rio.Rio().V1().Service().Cache(), + } + + riov1controller.RegisterServiceGeneratingHandler(ctx, + rContext.Rio.Rio().V1().Service(), + rContext.Apply. + WithCacheTypes( + rContext.K8sNetworking.Extensions().V1beta1().Ingress(), + rContext.Core.Core().V1().Secret()), + "", + "ingress-istio-services", + h.generateFromService, + &generic.GeneratingHandlerOptions{ + AllowCrossNamespace: true, + }) + + riov1controller.RegisterRouterGeneratingHandler(ctx, + rContext.Rio.Rio().V1().Router(), + rContext.Apply.WithCacheTypes( + rContext.K8sNetworking.Extensions().V1beta1().Ingress(), + rContext.Core.Core().V1().Secret()), + "", + "ingress-istio-router", + h.generateFromRouter, + &generic.GeneratingHandlerOptions{ + AllowCrossNamespace: true, + }) + + corev1controller.RegisterServiceGeneratingHandler(ctx, + rContext.Core.Core().V1().Service(), + rContext.Apply. + WithCacheTypes( + rContext.K8sNetworking.Extensions().V1beta1().Ingress(), + rContext.Core.Core().V1().Secret()), + "", + "ingress-istio-app-services", + h.generateAppFromService, + &generic.GeneratingHandlerOptions{ + AllowCrossNamespace: true, + }) + return nil +} + +type handler struct { + namespace string + vsFactory *vsfactory.VirtualServiceFactory + secrets corev1controller.SecretController + serviceCache riov1controller.ServiceCache +} + +func (h handler) generateAppFromService(obj *corev1.Service, status corev1.ServiceStatus) ([]runtime.Object, corev1.ServiceStatus, error) { + app := obj.Labels["rio.cattle.io/app"] + if app == "" { + return nil, status, nil + } + + svcs, err := h.serviceCache.GetByIndex(indexes.ServiceByApp, fmt.Sprintf("%s/%s", obj.Namespace, app)) + if err != nil { + return nil, status, err + } + + if len(svcs) == 0 { + return nil, status, nil + } + + service := svcs[0] + result, err := h.generateIngressAndSecret(service, service.Status.AppEndpoints, true) + return result, status, err +} + +func (h handler) generateFromService(obj *riov1.Service, status riov1.ServiceStatus) ([]runtime.Object, riov1.ServiceStatus, error) { + if obj.Spec.Template { + return nil, status, nil + } + + result, err := h.generateIngressAndSecret(obj, obj.Status.Endpoints, false) + return result, status, err +} + +func (h handler) generateFromRouter(router *riov1.Router, status riov1.RouterStatus) ([]runtime.Object, riov1.RouterStatus, error) { + dms, err := pkg.Domains(router) + if err != nil { + return nil, status, err + } + + tlss, err := h.vsFactory.FindTLS(router.Namespace, router.Name, "", dms) + if err != nil { + return nil, status, err + } + var result []runtime.Object + + ingress := constructors.NewIngress(config.ConfigController.Gateway.ServiceNamespace, router.Name, v1beta1.Ingress{}) + + for _, hostname := range dms { + ingress.Spec.Rules = append(ingress.Spec.Rules, v1beta1.IngressRule{ + Host: hostname, + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ + { + Backend: v1beta1.IngressBackend{ + ServiceName: config.ConfigController.Gateway.ServiceName, + ServicePort: intstr.FromInt(80), + }, + }, + }, + }, + }, + }) + ingress.Spec.TLS = append(ingress.Spec.TLS, v1beta1.IngressTLS{ + Hosts: []string{hostname}, + SecretName: tlss[hostname], + }) + + if tlss[hostname] != "" { + existingSecret, err := h.secrets.Cache().Get(h.namespace, tlss[hostname]) + if err != nil { + return nil, status, err + } + secret := constructors.NewSecret(config.ConfigController.Gateway.ServiceNamespace, tlss[hostname], corev1.Secret{ + Data: existingSecret.Data, + }) + + result = append(result, secret) + } + } + result = append(result, ingress) + return result, status, nil + +} + +func (h handler) generateIngressAndSecret(obj *riov1.Service, endpoints []string, isApp bool) ([]runtime.Object, error) { + app, version := services.AppAndVersion(obj) + + var servicePort int32 + for _, port := range serviceports.ContainerPorts(obj) { + if port.IsExposed() && port.IsHTTP() { + servicePort = port.Port + continue + } + } + if servicePort == 0 { + return nil, nil + } + + var hostnames []string + seen := map[string]bool{} + for _, endpoint := range endpoints { + u, err := url.Parse(endpoint) + if err != nil { + continue + } + if seen[u.Hostname()] { + continue + } + seen[u.Hostname()] = true + + hostnames = append(hostnames, u.Hostname()) + } + + tlss, err := h.vsFactory.FindTLS(obj.Namespace, app, version, hostnames) + if err != nil { + return nil, err + } + + var result []runtime.Object + + ingressName := name.SafeConcatName(app, version) + if isApp { + ingressName = app + } + ingress := constructors.NewIngress(config.ConfigController.Gateway.ServiceNamespace, ingressName, v1beta1.Ingress{}) + + for _, hostname := range hostnames { + ingress.Spec.Rules = append(ingress.Spec.Rules, v1beta1.IngressRule{ + Host: hostname, + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ + { + Backend: v1beta1.IngressBackend{ + ServiceName: config.ConfigController.Gateway.ServiceName, + ServicePort: intstr.FromInt(80), + }, + }, + }, + }, + }, + }) + ingress.Spec.TLS = append(ingress.Spec.TLS, v1beta1.IngressTLS{ + Hosts: []string{hostname}, + SecretName: tlss[hostname], + }) + + if tlss[hostname] != "" { + existingSecret, err := h.secrets.Cache().Get(h.namespace, tlss[hostname]) + if err != nil { + return nil, err + } + secret := constructors.NewSecret(config.ConfigController.Gateway.ServiceNamespace, tlss[hostname], corev1.Secret{ + Data: existingSecret.Data, + }) + + result = append(result, secret) + } + } + result = append(result, ingress) + return result, nil +} diff --git a/modules/istio/controller/pkg/pkg.go b/modules/istio/controller/pkg/pkg.go new file mode 100644 index 000000000..25015d130 --- /dev/null +++ b/modules/istio/controller/pkg/pkg.go @@ -0,0 +1,26 @@ +package pkg + +import ( + "net/url" + + riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" +) + +func Domains(router *riov1.Router) (result []string, err error) { + seen := map[string]bool{} + for _, endpoint := range router.Status.Endpoints { + u, err := url.Parse(endpoint) + if err != nil { + return nil, err + } + + if seen[u.Host] { + continue + } + seen[u.Host] = true + + result = append(result, u.Host) + } + + return +} diff --git a/modules/istio/controller/routers/routers.go b/modules/istio/controller/routers/routers.go new file mode 100644 index 000000000..e0f8f0034 --- /dev/null +++ b/modules/istio/controller/routers/routers.go @@ -0,0 +1,41 @@ +package routers + +import ( + "context" + + "github.com/rancher/rio/modules/gloo/pkg/vsfactory" + riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" + riov1controller "github.com/rancher/rio/pkg/generated/controllers/rio.cattle.io/v1" + "github.com/rancher/rio/types" + "k8s.io/apimachinery/pkg/runtime" +) + +func Register(ctx context.Context, rContexts *types.Context) error { + h := &handler{ + vsFactory: vsfactory.New(rContexts), + } + + riov1controller.RegisterRouterGeneratingHandler(ctx, + rContexts.Rio.Rio().V1().Router(), + rContexts.Apply. + WithCacheTypes(rContexts.Istio.Networking().V1alpha3().VirtualService()), + "GatewayConfigured", + "istio-router", + h.generate, + nil) + + return nil +} + +type handler struct { + vsFactory *vsfactory.VirtualServiceFactory +} + +func (h *handler) generate(router *riov1.Router, status riov1.RouterStatus) ([]runtime.Object, riov1.RouterStatus, error) { + vs, err := h.vsFactory.ForIstioRouter(router) + if err != nil { + return nil, status, err + } + + return []runtime.Object{vs}, status, nil +} diff --git a/modules/istio/controller/service/handler.go b/modules/istio/controller/service/handler.go new file mode 100644 index 000000000..28728d860 --- /dev/null +++ b/modules/istio/controller/service/handler.go @@ -0,0 +1,50 @@ +package service + +import ( + "context" + + "github.com/rancher/rio/modules/gloo/pkg/vsfactory" + riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" + rioadminv1controller "github.com/rancher/rio/pkg/generated/controllers/admin.rio.cattle.io/v1" + riov1controller "github.com/rancher/rio/pkg/generated/controllers/rio.cattle.io/v1" + "github.com/rancher/rio/types" + "k8s.io/apimachinery/pkg/runtime" +) + +func Register(ctx context.Context, rContext *types.Context) error { + h := &handler{ + systemNamespace: rContext.Namespace, + clusterDomainCache: rContext.Admin.Admin().V1().ClusterDomain().Cache(), + vsFactory: vsfactory.New(rContext), + } + + riov1controller.RegisterServiceGeneratingHandler(ctx, + rContext.Rio.Rio().V1().Service(), + rContext.Apply.WithCacheTypes( + rContext.Istio.Networking().V1alpha3().VirtualService(), + ), + "", + "istio-service", + h.generate, + nil) + + return nil +} + +type handler struct { + systemNamespace string + clusterDomainCache rioadminv1controller.ClusterDomainCache + vsFactory *vsfactory.VirtualServiceFactory +} + +func (h *handler) generate(obj *riov1.Service, status riov1.ServiceStatus) ([]runtime.Object, riov1.ServiceStatus, error) { + if obj.Spec.Template { + return nil, status, nil + } + vs, err := h.vsFactory.ForIstioRevision(obj) + if err != nil { + return nil, status, err + } + + return []runtime.Object{vs}, status, nil +} diff --git a/modules/istio/features/features.go b/modules/istio/features/features.go new file mode 100644 index 000000000..fea3d12f3 --- /dev/null +++ b/modules/istio/features/features.go @@ -0,0 +1,39 @@ +package features + +import ( + "context" + + "github.com/rancher/rio/modules/istio/controller/app" + "github.com/rancher/rio/modules/istio/controller/gateway" + "github.com/rancher/rio/modules/istio/controller/ingress" + "github.com/rancher/rio/modules/istio/controller/routers" + "github.com/rancher/rio/modules/istio/controller/service" + "github.com/rancher/rio/modules/linkerd/pkg/injector" + "github.com/rancher/rio/pkg/arch" + "github.com/rancher/rio/pkg/features" + "github.com/rancher/rio/types" +) + +func Register(ctx context.Context, rContext *types.Context) error { + feature := &features.FeatureController{ + FeatureName: "istio", + FeatureSpec: features.FeatureSpec{ + Enabled: arch.IsAmd64(), + Description: "Istio service mesh", + }, + Controllers: []features.ControllerRegister{ + app.Register, + routers.Register, + service.Register, + gateway.Register, + ingress.Register, + }, + OnStart: func() error { + injector.RegisterInjector() + rContext.Rio.Rio().V1().Service().Enqueue("*", "*") + return nil + }, + } + + return feature.Register() +} diff --git a/modules/istio/module.go b/modules/istio/module.go new file mode 100644 index 000000000..3b0dfdf99 --- /dev/null +++ b/modules/istio/module.go @@ -0,0 +1,12 @@ +package istio + +import ( + "context" + + "github.com/rancher/rio/modules/istio/features" + "github.com/rancher/rio/types" +) + +func Register(ctx context.Context, rContext *types.Context) error { + return features.Register(ctx, rContext) +} diff --git a/modules/letsencrypt/controllers/account/handler.go b/modules/letsencrypt/controllers/account/handler.go new file mode 100644 index 000000000..18506ddb7 --- /dev/null +++ b/modules/letsencrypt/controllers/account/handler.go @@ -0,0 +1,129 @@ +package account + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "fmt" + + "github.com/go-acme/lego/v3/certcrypto" + "github.com/go-acme/lego/v3/lego" + "github.com/go-acme/lego/v3/registration" + + "github.com/rancher/rio/modules/letsencrypt/pkg" + + "github.com/rancher/rio/pkg/config" + "github.com/rancher/rio/types" + corev1controller "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1" + "github.com/rancher/wrangler/pkg/apply" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" +) + +var register = false + +const ( + handlerName = "letsencrypt-issuer" + + defaultEmail = "cert@rancher.dev" + defaultAccount = "letsencrypt-account" + defaultServerURL = "https://acme-v02.api.letsencrypt.org/directory" +) + +func Register(ctx context.Context, rContext *types.Context) error { + h := &handler{ + key: fmt.Sprintf("%s/%s", rContext.Namespace, config.ConfigName), + namespace: rContext.Namespace, + secrets: rContext.Core.Core().V1().Secret().Cache(), + apply: rContext.Apply. + WithSetID(handlerName). + WithSetOwnerReference(true, true). + WithCacheTypes(rContext.Core.Core().V1().Secret()), + } + + rContext.Core.Core().V1().ConfigMap().OnChange(ctx, handlerName, h.sync) + return nil +} + +type handler struct { + key string + namespace string + apply apply.Apply + secrets corev1controller.SecretCache +} + +func (h *handler) sync(key string, cm *v1.ConfigMap) (*v1.ConfigMap, error) { + if cm == nil || key != h.key || register { + return nil, nil + } + + config, err := config.FromConfigMap(cm) + if err != nil { + return cm, err + } + + if _, err := h.secrets.Get(h.namespace, defaultAccount); err != nil && !errors.IsNotFound(err) { + return cm, err + } else if err == nil { + return cm, nil + } + + secret, err := constructSecret(h.namespace, config) + if err != nil { + return cm, err + } + + if err := h.apply.WithOwner(cm).ApplyObjects(secret); err != nil { + return cm, err + } + register = true + return cm, nil +} + +func withDefault(val, def string) string { + if val == "" { + return def + } + return val +} + +func constructSecret(namespace string, config config.Config) (*corev1.Secret, error) { + account := withDefault(config.LetsEncrypt.Account, defaultAccount) + email := withDefault(config.LetsEncrypt.Email, defaultEmail) + url := withDefault(config.LetsEncrypt.ServerURL, defaultServerURL) + + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, err + } + + user := &pkg.User{ + Name: account, + Email: email, + URL: url, + Key: privateKey, + } + + conf := lego.NewConfig(user) + + // This CA URL is configured for a local dev instance of Boulder running in Docker in a VM. + conf.CADirURL = user.URL + conf.Certificate.KeyType = certcrypto.RSA2048 + + // A client facilitates communication with the CA server. + client, err := lego.NewClient(conf) + if err != nil { + return nil, err + } + + // New users will need to register + reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) + if err != nil { + return nil, err + } + user.Registration = reg + + return pkg.SetSecret(namespace, user) +} diff --git a/modules/letsencrypt/controllers/certificate/certificate.go b/modules/letsencrypt/controllers/certificate/certificate.go new file mode 100644 index 000000000..4e1479468 --- /dev/null +++ b/modules/letsencrypt/controllers/certificate/certificate.go @@ -0,0 +1,151 @@ +package certificate + +import ( + "context" + "strings" + + "github.com/go-acme/lego/challenge/http01" + + "github.com/go-acme/lego/v3/certcrypto" + "github.com/go-acme/lego/v3/certificate" + "github.com/go-acme/lego/v3/lego" + "github.com/rancher/rio/modules/letsencrypt/pkg" + v1 "github.com/rancher/rio/pkg/apis/admin.rio.cattle.io/v1" + "github.com/rancher/rio/pkg/constants" + "github.com/rancher/rio/pkg/constructors" + adminv1controller "github.com/rancher/rio/pkg/generated/controllers/admin.rio.cattle.io/v1" + "github.com/rancher/rio/types" + corev1controller "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1" + "github.com/rancher/wrangler/pkg/apply" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +const ( + dnsType = "dns-01" + httpType = "http-01" +) + +func Register(ctx context.Context, rContext *types.Context) error { + h := handler{ + namespace: rContext.Namespace, + apply: rContext.Apply.WithCacheTypes(rContext.Core.Core().V1().Secret()), + secrets: rContext.Core.Core().V1().Secret().Cache(), + } + + adminv1controller.RegisterCertificateGeneratingHandler(ctx, + rContext.Admin.Admin().V1().Certificate(), + rContext.Apply.WithCacheTypes(rContext.Core.Core().V1().Secret()), + "CertificateDeployed", + "certificate-provisioned", + h.generate, + nil, + ) + + return nil +} + +type handler struct { + namespace string + apply apply.Apply + secrets corev1controller.SecretCache +} + +func (h handler) generate(obj *v1.Certificate, status v1.CertificateStatus) ([]runtime.Object, v1.CertificateStatus, error) { + if obj == nil || obj.DeletionTimestamp != nil { + return nil, status, nil + } + + providerType := "" + for _, dnsName := range obj.Spec.DNSNames { + if strings.HasPrefix(dnsName, "*") { + providerType = dnsType + } else { + providerType = httpType + } + break + } + + secret, err := h.secrets.Get(h.namespace, constants.LetsEncryptAccountSecretName) + if err != nil { + return nil, status, err + } + + user, err := pkg.FromSecret(secret) + if err != nil { + return nil, status, err + } + + config := lego.NewConfig(user) + + // This CA URL is configured for a local dev instance of Boulder running in Docker in a VM. + config.CADirURL = user.URL + config.Certificate.KeyType = certcrypto.RSA2048 + + // A client facilitates communication with the CA server. + client, err := lego.NewClient(config) + if err != nil { + return nil, status, err + } + + switch providerType { + case dnsType: + rdnsSecret, err := h.secrets.Get(h.namespace, pkg.RdnsSecretName) + if err != nil { + return nil, status, err + } + + rdnsProvider, err := pkg.NewDNSProviderCredential(constants.RDNSURL, string(rdnsSecret.Data["token"])) + if err != nil { + return nil, status, err + } + + err = client.Challenge.SetDNS01Provider(rdnsProvider) + if err != nil { + return nil, status, err + } + + request := certificate.ObtainRequest{ + Domains: obj.Spec.DNSNames, + Bundle: true, + } + certificates, err := client.Certificate.Obtain(request) + if err != nil { + return nil, status, err + } + + secret := constructors.NewSecret(obj.Spec.SecretRef.Namespace, obj.Spec.SecretRef.Name, corev1.Secret{ + Type: corev1.SecretTypeTLS, + Data: map[string][]byte{ + corev1.TLSPrivateKeyKey: certificates.PrivateKey, + corev1.TLSCertKey: certificates.Certificate, + }, + }) + + return []runtime.Object{secret}, status, nil + case httpType: + err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "8080")) + if err != nil { + return nil, status, err + } + request := certificate.ObtainRequest{ + Domains: obj.Spec.DNSNames, + Bundle: true, + } + certificates, err := client.Certificate.Obtain(request) + if err != nil { + return nil, status, err + } + secret := constructors.NewSecret(obj.Spec.SecretRef.Namespace, obj.Spec.SecretRef.Name, corev1.Secret{ + Type: corev1.SecretTypeTLS, + Data: map[string][]byte{ + corev1.TLSPrivateKeyKey: certificates.PrivateKey, + corev1.TLSCertKey: certificates.Certificate, + }, + }) + + return []runtime.Object{secret}, status, nil + } + + return nil, status, err +} diff --git a/modules/letsencrypt/controllers/clusterdomain/controller.go b/modules/letsencrypt/controllers/clusterdomain/controller.go index bf8d19df1..968e2859d 100644 --- a/modules/letsencrypt/controllers/clusterdomain/controller.go +++ b/modules/letsencrypt/controllers/clusterdomain/controller.go @@ -3,16 +3,16 @@ package clusterdomain import ( "context" - certmanagerv1alpha2 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2" - cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1" - "github.com/rancher/rio/modules/letsencrypt/controllers/issuer" + "github.com/rancher/wrangler/pkg/generic" + + adminv1 "github.com/rancher/rio/pkg/apis/admin.rio.cattle.io/v1" v1 "github.com/rancher/rio/pkg/apis/admin.rio.cattle.io/v1" adminv1controller "github.com/rancher/rio/pkg/generated/controllers/admin.rio.cattle.io/v1" "github.com/rancher/rio/pkg/indexes" "github.com/rancher/rio/types" - "github.com/rancher/wrangler-api/pkg/generated/controllers/cert-manager.io/v1alpha2" "github.com/rancher/wrangler/pkg/condition" name2 "github.com/rancher/wrangler/pkg/name" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -21,39 +21,38 @@ import ( func Register(ctx context.Context, rContext *types.Context) error { fh := &certsHandler{ namespace: rContext.Namespace, - certificateCache: rContext.CertManager.Certmanager().V1alpha2().Certificate().Cache(), clusterDomainCache: rContext.Admin.Admin().V1().ClusterDomain().Cache(), clusterDomainController: rContext.Admin.Admin().V1().ClusterDomain(), + certificateCache: rContext.Admin.Admin().V1().Certificate().Cache(), } - apply := rContext.Apply. - WithCacheTypes(rContext.CertManager.Certmanager().V1alpha2().Certificate()) - adminv1controller.RegisterClusterDomainGeneratingHandler(ctx, rContext.Admin.Admin().V1().ClusterDomain(), - apply, + rContext.Apply.WithCacheTypes(rContext.Admin.Admin().V1().Certificate()), "LetsencryptCertificateDeployed", "clusterdomain-letsencrypt", fh.Handle, - nil) + &generic.GeneratingHandlerOptions{ + AllowClusterScoped: true, + }) - rContext.CertManager.Certmanager().V1alpha2().Certificate().OnChange(ctx, "letsencrypt", fh.onCertChange) + rContext.Admin.Admin().V1().Certificate().OnChange(ctx, "letsencrypt", fh.onCertChange) return nil } type certsHandler struct { namespace string - certificateCache v1alpha2.CertificateCache + certificateCache adminv1controller.CertificateCache clusterDomainCache adminv1controller.ClusterDomainCache clusterDomainController adminv1controller.ClusterDomainController } -func (f *certsHandler) onCertChange(key string, obj *certmanagerv1alpha2.Certificate) (*certmanagerv1alpha2.Certificate, error) { +func (f *certsHandler) onCertChange(key string, obj *adminv1.Certificate) (*adminv1.Certificate, error) { if obj == nil { return nil, nil } - domains, err := f.clusterDomainCache.GetByIndex(indexes.ClusterDomainByAssignedSecret, obj.Spec.SecretName) + domains, err := f.clusterDomainCache.GetByIndex(indexes.ClusterDomainByAssignedSecret, obj.Spec.SecretRef.Name) if err != nil { return obj, err } @@ -71,18 +70,18 @@ func (f *certsHandler) Handle(obj *v1.ClusterDomain, status v1.ClusterDomainStat } cert := wildcardDNS(f.namespace, obj.Name) - status.AssignedSecretName = cert.Spec.SecretName + status.AssignedSecretName = cert.Spec.SecretRef.Name if status.AssignedSecretName == "" { status.HTTPSSupported = false } else { - cert, err := f.certificateCache.Get(f.namespace, status.AssignedSecretName) + cert, err := f.certificateCache.Get(status.AssignedSecretName) if errors.IsNotFound(err) { status.HTTPSSupported = false } else if err != nil { return nil, status, err } else { - status.HTTPSSupported = condition.Cond("Ready").IsTrue(cert) + status.HTTPSSupported = condition.Cond("CertificateDeployed").IsTrue(cert) } } @@ -91,10 +90,10 @@ func (f *certsHandler) Handle(obj *v1.ClusterDomain, status v1.ClusterDomainStat }, status, nil } -func wildcardDNS(namespace, name string) *certmanagerv1alpha2.Certificate { +func wildcardDNS(namespace, name string) *v1.Certificate { secretName := name2.SafeConcatName(name, "tls") wildcardDomain := "*." + name - return &certmanagerv1alpha2.Certificate{ + return &adminv1.Certificate{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: secretName, @@ -102,11 +101,10 @@ func wildcardDNS(namespace, name string) *certmanagerv1alpha2.Certificate { "cert-manager.io/issue-temporary-certificate": "true", }, }, - Spec: certmanagerv1alpha2.CertificateSpec{ - SecretName: secretName, - IssuerRef: cmmeta.ObjectReference{ - Kind: "Issuer", - Name: issuer.RioDNSIssuer, + Spec: v1.CertificateSpec{ + SecretRef: corev1.SecretReference{ + Name: secretName, + Namespace: namespace, }, DNSNames: []string{ wildcardDomain, diff --git a/modules/letsencrypt/controllers/issuer/handler.go b/modules/letsencrypt/controllers/issuer/handler.go deleted file mode 100644 index e42568357..000000000 --- a/modules/letsencrypt/controllers/issuer/handler.go +++ /dev/null @@ -1,135 +0,0 @@ -package issuer - -import ( - "context" - "fmt" - - "github.com/rancher/rio/pkg/constants" - - cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1alpha2" - certmanagerv1alpha2 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2" - cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1" - "github.com/rancher/rio/pkg/config" - "github.com/rancher/rio/types" - "github.com/rancher/wrangler/pkg/apply" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - handlerName = "letsencrypt-issuer" - rdnsTokenName = "rdns-token" - RioDNSIssuer = "rio-dns-issuer" - RioHTTPIssuer = "rio-http-issuer" - - defaultEmail = "cert@rancher.dev" - defaultAccount = "letsencrypt-account" - defaultServerURL = "https://acme-v02.api.letsencrypt.org/directory" -) - -func Register(ctx context.Context, rContext *types.Context) error { - h := &handler{ - key: fmt.Sprintf("%s/%s", rContext.Namespace, config.ConfigName), - namespace: rContext.Namespace, - apply: rContext.Apply. - WithSetID(handlerName). - WithSetOwnerReference(true, true). - WithCacheTypes( - rContext.CertManager.Certmanager().V1alpha2().Issuer(), - rContext.Core.Core().V1().Secret()), - } - - rContext.Core.Core().V1().ConfigMap().OnChange(ctx, handlerName, h.sync) - return nil -} - -type handler struct { - key string - namespace string - apply apply.Apply -} - -func (h *handler) sync(key string, cm *v1.ConfigMap) (*v1.ConfigMap, error) { - if cm == nil || key != h.key { - return nil, nil - } - - config, err := config.FromConfigMap(cm) - if err != nil { - return cm, err - } - - return cm, h.apply. - WithOwner(cm). - ApplyObjects( - constructIssuer(h.namespace, "dns", config), - constructIssuer(h.namespace, "http", config), - ) -} - -func withDefault(val, def string) string { - if val == "" { - return def - } - return val -} - -func constructIssuer(namespace, issuerType string, config config.Config) *certmanagerv1alpha2.Issuer { - account := withDefault(config.LetsEncrypt.Account, defaultAccount) - email := withDefault(config.LetsEncrypt.Email, defaultEmail) - url := withDefault(config.LetsEncrypt.ServerURL, defaultServerURL) - - issuer := &certmanagerv1alpha2.Issuer{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - }, - Spec: certmanagerv1alpha2.IssuerSpec{ - IssuerConfig: certmanagerv1alpha2.IssuerConfig{ - ACME: &cmacme.ACMEIssuer{ - Server: url, - Email: email, - PrivateKey: cmmeta.SecretKeySelector{ - LocalObjectReference: cmmeta.LocalObjectReference{ - Name: account, - }, - }, - }, - }, - }, - } - - rdnsURL := config.RdnsURL - if rdnsURL == "" { - rdnsURL = constants.RDNSURL - } - if issuerType == "dns" { - issuer.Name = RioDNSIssuer - issuer.Spec.ACME.Solvers = []cmacme.ACMEChallengeSolver{ - { - DNS01: &cmacme.ACMEChallengeSolverDNS01{ - RDNS: &cmacme.ACMEIssuerDNS01ProviderRDNS{ - APIEndpoint: rdnsURL, - ClientToken: cmmeta.SecretKeySelector{ - Key: "token", - LocalObjectReference: cmmeta.LocalObjectReference{ - Name: rdnsTokenName, - }, - }, - }, - }, - }} - } else { - issuer.Name = RioHTTPIssuer - issuer.Spec.ACME.Solvers = []cmacme.ACMEChallengeSolver{ - { - HTTP01: &cmacme.ACMEChallengeSolverHTTP01{ - Ingress: &cmacme.ACMEChallengeSolverHTTP01Ingress{ - Class: &[]string{"gloo"}[0], - }, - }, - }, - } - } - - return issuer -} diff --git a/modules/letsencrypt/controllers/publicdomain/controller.go b/modules/letsencrypt/controllers/publicdomain/controller.go index 7aa0146fc..555dbd3c2 100644 --- a/modules/letsencrypt/controllers/publicdomain/controller.go +++ b/modules/letsencrypt/controllers/publicdomain/controller.go @@ -3,14 +3,15 @@ package publicdomain import ( "context" - certmanagerv1alpha2 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2" - cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1" - "github.com/rancher/rio/modules/letsencrypt/controllers/issuer" + "github.com/rancher/wrangler/pkg/condition" + "k8s.io/apimachinery/pkg/api/errors" + v1 "github.com/rancher/rio/pkg/apis/admin.rio.cattle.io/v1" adminv1controller "github.com/rancher/rio/pkg/generated/controllers/admin.rio.cattle.io/v1" "github.com/rancher/rio/pkg/indexes" "github.com/rancher/rio/types" corev1controller "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1" + "github.com/rancher/wrangler/pkg/generic" name2 "github.com/rancher/wrangler/pkg/name" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -23,10 +24,11 @@ func Register(ctx context.Context, rContext *types.Context) error { secretsCache: rContext.Core.Core().V1().Secret().Cache(), publicDomainCache: rContext.Admin.Admin().V1().PublicDomain().Cache(), publicDomainController: rContext.Admin.Admin().V1().PublicDomain(), + certificateCache: rContext.Admin.Admin().V1().Certificate().Cache(), } apply := rContext.Apply. - WithCacheTypes(rContext.CertManager.Certmanager().V1alpha2().Certificate()) + WithCacheTypes(rContext.Admin.Admin().V1().Certificate()) adminv1controller.RegisterPublicDomainGeneratingHandler(ctx, rContext.Admin.Admin().V1().PublicDomain(), @@ -34,7 +36,9 @@ func Register(ctx context.Context, rContext *types.Context) error { "LetsencryptCertificateDeployed", "letsencrypt-publicdomain", fh.Handle, - nil) + &generic.GeneratingHandlerOptions{ + AllowClusterScoped: true, + }) rContext.Core.Core().V1().Secret().OnChange(ctx, "letsencrypt", fh.onSecretChange) @@ -46,6 +50,7 @@ type certsHandler struct { secretsCache corev1controller.SecretCache publicDomainCache adminv1controller.PublicDomainCache publicDomainController adminv1controller.PublicDomainController + certificateCache adminv1controller.CertificateCache } func (f *certsHandler) onSecretChange(key string, obj *corev1.Secret) (*corev1.Secret, error) { @@ -66,13 +71,19 @@ func (f *certsHandler) Handle(obj *v1.PublicDomain, status v1.PublicDomainStatus } cert := certificateHTTP(f.namespace, obj.Name) - status.AssignedSecretName = cert.Spec.SecretName + status.AssignedSecretName = cert.Spec.SecretRef.Name if status.AssignedSecretName == "" { status.HTTPSSupported = false } else { - _, err := f.secretsCache.Get(f.namespace, status.AssignedSecretName) - status.HTTPSSupported = err == nil + cert, err := f.certificateCache.Get(status.AssignedSecretName) + if errors.IsNotFound(err) { + status.HTTPSSupported = false + } else if err != nil { + return nil, status, err + } else { + status.HTTPSSupported = condition.Cond("CertificateDeployed").IsTrue(cert) + } } return []runtime.Object{ @@ -80,21 +91,17 @@ func (f *certsHandler) Handle(obj *v1.PublicDomain, status v1.PublicDomainStatus }, status, nil } -func certificateHTTP(namespace, domain string) *certmanagerv1alpha2.Certificate { +func certificateHTTP(namespace, domain string) *v1.Certificate { name := name2.SafeConcatName(domain, "tls") - return &certmanagerv1alpha2.Certificate{ + return &v1.Certificate{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: name, - Annotations: map[string]string{ - "cert-manager.io/issue-temporary-certificate": "true", - }, }, - Spec: certmanagerv1alpha2.CertificateSpec{ - SecretName: name, - IssuerRef: cmmeta.ObjectReference{ - Kind: "Issuer", - Name: issuer.RioHTTPIssuer, + Spec: v1.CertificateSpec{ + SecretRef: corev1.SecretReference{ + Name: name, + Namespace: namespace, }, DNSNames: []string{ domain, diff --git a/modules/letsencrypt/features/feature.go b/modules/letsencrypt/features/feature.go index e953d9998..1d13eb38b 100644 --- a/modules/letsencrypt/features/feature.go +++ b/modules/letsencrypt/features/feature.go @@ -3,8 +3,9 @@ package features import ( "context" + "github.com/rancher/rio/modules/letsencrypt/controllers/account" + "github.com/rancher/rio/modules/letsencrypt/controllers/certificate" "github.com/rancher/rio/modules/letsencrypt/controllers/clusterdomain" - "github.com/rancher/rio/modules/letsencrypt/controllers/issuer" "github.com/rancher/rio/modules/letsencrypt/controllers/publicdomain" "github.com/rancher/rio/pkg/features" "github.com/rancher/rio/pkg/stack" @@ -12,24 +13,18 @@ import ( ) func Register(ctx context.Context, rContext *types.Context) error { - apply := rContext.Apply.WithCacheTypes(rContext.Rio.Rio().V1().Service(), rContext.Core.Core().V1().ConfigMap()) feature := &features.FeatureController{ FeatureName: "letsencrypt", FeatureSpec: features.FeatureSpec{ Enabled: true, Description: "Let's Encrypt", }, - FixedAnswers: map[string]string{ - "TAG": "v0.11.0-rio.1", - "NAMESPACE": rContext.Namespace, - }, - SystemStacks: []*stack.SystemStack{ - stack.NewSystemStack(apply, rContext.Admin.Admin().V1().SystemStack(), rContext.Namespace, "cert-manager"), - }, + SystemStacks: []*stack.SystemStack{}, Controllers: []features.ControllerRegister{ - issuer.Register, + account.Register, publicdomain.Register, clusterdomain.Register, + certificate.Register, }, } diff --git a/modules/letsencrypt/pkg/rdns.go b/modules/letsencrypt/pkg/rdns.go new file mode 100644 index 000000000..c4fa58e97 --- /dev/null +++ b/modules/letsencrypt/pkg/rdns.go @@ -0,0 +1,148 @@ +package pkg + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "strings" + "time" + + "github.com/go-acme/lego/challenge/dns01" + + "github.com/sirupsen/logrus" +) + +const ( + AuthorizationHeader = "Authorization" + ContentTypeHeader = "Content-Type" + ContentTypeJSON = "application/json" + RdnsSecretName = "rdns-token" + txtPathPattern = "%s/domain/%s/txt" +) + +type DNSProvider struct { + client DNSClient +} + +func NewDNSProvider() (*DNSProvider, error) { + apiEndpoint := os.Getenv("RDNS_API_ENDPOINT") + token := os.Getenv("RDNS_TOKEN") + return NewDNSProviderCredential(apiEndpoint, token) +} + +func NewDNSProviderCredential(apiEndpoint, token string) (*DNSProvider, error) { + if apiEndpoint == "" { + return nil, fmt.Errorf("rdns api endpoint is empty") + } + + if token == "" { + return nil, fmt.Errorf("rdns token is missing") + } + + dnsClient := DNSClient{ + httpClient: http.DefaultClient, + base: apiEndpoint, + token: token, + } + return &DNSProvider{ + client: dnsClient, + }, nil +} + +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + fqdn, value := dns01.GetRecord(domain, keyAuth) + return d.client.SetTXTRecord(fqdn, value) +} + +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + return d.client.DeleteDNSRecord(domain) +} + +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return 30 * time.Second, 5 * time.Second +} + +type DNSClient struct { + httpClient *http.Client + base string + token string +} + +func (d *DNSClient) SetTXTRecord(domain, text string) error { + url := fmt.Sprintf(txtPathPattern, d.base, strings.TrimSuffix(domain, ".")) + payload := map[string]string{ + "text": text, + } + buf := &bytes.Buffer{} + if err := json.NewEncoder(buf).Encode(payload); err != nil { + return err + } + + // get txt domain first + method := "" + resp, err := d.Do(http.MethodGet, url, nil) + if err != nil { + return err + } + if resp.StatusCode == http.StatusOK { + // todo: rdns server need to return better status code + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + if strings.Contains(string(data), "failed to filter TXT records") { + method = http.MethodPost + } else { + method = http.MethodPut + } + } + resp.Body.Close() + + resp, err = d.Do(method, url, buf) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + logrus.Infof("expect 200, got %v. Error: %s", resp.StatusCode, string(data)) + return fmt.Errorf("expect 200, got %v. Error: %s", resp.StatusCode, string(data)) + } + return nil +} + +func (d *DNSClient) Do(method, url string, data io.Reader) (*http.Response, error) { + request, err := http.NewRequest(method, url, data) + if err != nil { + return nil, err + } + request.Header.Set(AuthorizationHeader, fmt.Sprintf("Bearer %s", d.token)) + request.Header.Set(ContentTypeHeader, ContentTypeJSON) + resp, err := d.httpClient.Do(request) + if err != nil { + return nil, err + } + return resp, nil +} + +func (d *DNSClient) DeleteDNSRecord(domain string) error { + url := fmt.Sprintf(txtPathPattern, d.base, domain) + request, err := http.NewRequest(http.MethodDelete, url, nil) + if err != nil { + return err + } + request.Header.Set(AuthorizationHeader, fmt.Sprintf("Bearer %s", d.token)) + request.Header.Set(ContentTypeHeader, ContentTypeJSON) + _, err = d.httpClient.Do(request) + if err != nil { + return err + } + return nil +} diff --git a/modules/letsencrypt/pkg/user.go b/modules/letsencrypt/pkg/user.go new file mode 100644 index 000000000..29599996c --- /dev/null +++ b/modules/letsencrypt/pkg/user.go @@ -0,0 +1,78 @@ +package pkg + +import ( + "crypto" + "crypto/ecdsa" + "crypto/x509" + "encoding/json" + "encoding/pem" + + "github.com/rancher/rio/pkg/constants" + + "github.com/rancher/rio/pkg/constructors" + + v1 "k8s.io/api/core/v1" + + "github.com/go-acme/lego/v3/registration" +) + +type User struct { + Name string + Email string + Registration *registration.Resource + Key crypto.PrivateKey + URL string +} + +func (u *User) GetEmail() string { + return u.Email +} +func (u User) GetRegistration() *registration.Resource { + return u.Registration +} +func (u *User) GetPrivateKey() crypto.PrivateKey { + return u.Key +} + +func FromSecret(secret *v1.Secret) (*User, error) { + block, _ := pem.Decode(secret.Data["privateKey"]) + x509Encoded := block.Bytes + privateKey, err := x509.ParseECPrivateKey(x509Encoded) + if err != nil { + return nil, err + } + + var reg registration.Resource + if err := json.Unmarshal(secret.Data["registration"], ®); err != nil { + return nil, err + } + + return &User{ + Name: secret.Name, + Email: string(secret.Data["email"]), + Registration: ®, + Key: privateKey, + URL: string(secret.Data["url"]), + }, nil +} + +func SetSecret(namespace string, user *User) (*v1.Secret, error) { + x509Encoded, _ := x509.MarshalECPrivateKey(user.Key.(*ecdsa.PrivateKey)) + pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: x509Encoded}) + + reg, err := json.Marshal(user.Registration) + if err != nil { + return nil, err + } + + secret := constructors.NewSecret(namespace, constants.LetsEncryptAccountSecretName, v1.Secret{ + Data: map[string][]byte{ + "email": []byte(user.Email), + "privateKey": pemEncoded, + "url": []byte(user.URL), + "registration": reg, + }, + }) + + return secret, nil +} diff --git a/modules/linkerd/pkg/injector/injector.go b/modules/linkerd/pkg/injector/injector.go index e824378c4..043e97a59 100644 --- a/modules/linkerd/pkg/injector/injector.go +++ b/modules/linkerd/pkg/injector/injector.go @@ -1,16 +1,17 @@ package injector import ( + "github.com/rancher/rio/pkg/config" "github.com/rancher/wrangler/pkg/apply/injectors" appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/runtime" ) func RegisterInjector() { - injectors.Register("mesh", addLinkerdLabel) + injectors.Register("mesh", addServiceMeshLabel) } -func addLinkerdLabel(objs []runtime.Object) ([]runtime.Object, error) { +func addServiceMeshLabel(objs []runtime.Object) ([]runtime.Object, error) { for _, obj := range objs { switch o := obj.(type) { case *appsv1.Deployment: @@ -25,11 +26,16 @@ func addLinkerdLabel(objs []runtime.Object) ([]runtime.Object, error) { } func setAnnotations(annotation map[string]string) { + if annotation == nil { + annotation = map[string]string{} + } if annotation["rio.cattle.io/mesh"] != "true" { + if config.ConfigController.MeshMode == "istio" { + annotation["sidecar.istio.io/inject"] = "disabled" + } return } - if annotation == nil { - annotation = map[string]string{} + if config.ConfigController.MeshMode == "linkerd" { + annotation["linkerd.io/inject"] = "enabled" } - annotation["linkerd.io/inject"] = "enabled" } diff --git a/modules/modules.go b/modules/modules.go index f7f3da3f5..9a203000b 100644 --- a/modules/modules.go +++ b/modules/modules.go @@ -3,6 +3,8 @@ package modules import ( "context" + "github.com/rancher/rio/modules/istio" + "github.com/rancher/rio/modules/autoscale" "github.com/rancher/rio/modules/build" "github.com/rancher/rio/modules/dashboard" @@ -54,5 +56,8 @@ func Register(ctx context.Context, rContext *types.Context) error { if err := ingress.Register(ctx, rContext); err != nil { return err } + if err := istio.Register(ctx, rContext); err != nil { + return err + } return nil } diff --git a/modules/rdns/controllers/service/handler.go b/modules/rdns/controllers/service/handler.go index 9fb253fe9..cf7a1c73d 100644 --- a/modules/rdns/controllers/service/handler.go +++ b/modules/rdns/controllers/service/handler.go @@ -22,6 +22,13 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) +var ( + httpPortMap = map[string]string{ + "istio": "http2", + "linkerd": "http", + } +) + func Register(ctx context.Context, rContext *types.Context) error { cm, err := rContext.Core.Core().V1().ConfigMap().Get(rContext.Namespace, config.ConfigName, metav1.GetOptions{}) if err != nil { @@ -180,13 +187,13 @@ func (h *handler) generateFromService(svc *corev1.Service, status corev1.Service for _, port := range svc.Spec.Ports { portNum := 0 - if nodePort { + if nodePort && config.ConfigController.MeshMode == "linkerd" { portNum = int(port.NodePort) } else { portNum = int(port.Port) } - if port.Name == "http" { + if port.Name == httpPortMap[config.ConfigController.MeshMode] { clusterDomain.Spec.HTTPPort = portNum } else if port.Name == "https" { clusterDomain.Spec.HTTPSPort = portNum diff --git a/modules/smi/controllers/app/handler.go b/modules/smi/controllers/app/handler.go index 8fdf5b66a..406cf8c7c 100644 --- a/modules/smi/controllers/app/handler.go +++ b/modules/smi/controllers/app/handler.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "sort" + "strconv" "github.com/deislabs/smi-sdk-go/pkg/apis/split/v1alpha1" riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" @@ -77,10 +78,10 @@ func (h *handler) getSplit(namespace string, svcs []*riov1.Service) *v1alpha1.Tr app, version := services.AppAndVersion(svc) service = app - quantity := resource.NewQuantity(int64(*svc.Status.ComputedWeight), resource.BinarySI) + q := resource.MustParse(strconv.Itoa(*svc.Status.ComputedWeight)) backends = append(backends, v1alpha1.TrafficSplitBackend{ Service: fmt.Sprintf("%s-%s", app, version), - Weight: *quantity, + Weight: q, }) } diff --git a/pkg/apis/admin.rio.cattle.io/v1/certificate.go b/pkg/apis/admin.rio.cattle.io/v1/certificate.go new file mode 100644 index 000000000..1a930f7d0 --- /dev/null +++ b/pkg/apis/admin.rio.cattle.io/v1/certificate.go @@ -0,0 +1,37 @@ +package v1 + +import ( + "github.com/rancher/wrangler/pkg/condition" + "github.com/rancher/wrangler/pkg/genericcondition" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var ( + CertificateConditionReady = condition.Cond("Ready") +) + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Certificate is an admin group which manages letsencrypt certificates that are used by ClusterDomain and PublicDomain +type Certificate struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CertificateSpec `json:"spec,omitempty"` + Status CertificateStatus `json:"status,omitempty"` +} + +type CertificateSpec struct { + // SecretRef holds secret reference that stores tls.key and tls.crt + SecretRef v1.SecretReference `json:"secretRef,omitempty"` + + // DNSNames store SANs used by certificate + DNSNames []string `json:"dnsNames,omitempty"` +} + +type CertificateStatus struct { + Conditions []genericcondition.GenericCondition `json:"conditions,omitempty"` +} diff --git a/pkg/apis/admin.rio.cattle.io/v1/zz_generated_deepcopy.go b/pkg/apis/admin.rio.cattle.io/v1/zz_generated_deepcopy.go index ed67bdf81..db3e819cc 100644 --- a/pkg/apis/admin.rio.cattle.io/v1/zz_generated_deepcopy.go +++ b/pkg/apis/admin.rio.cattle.io/v1/zz_generated_deepcopy.go @@ -41,6 +41,110 @@ func (in *Address) DeepCopy() *Address { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Certificate) DeepCopyInto(out *Certificate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Certificate. +func (in *Certificate) DeepCopy() *Certificate { + if in == nil { + return nil + } + out := new(Certificate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Certificate) 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 *CertificateList) DeepCopyInto(out *CertificateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Certificate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateList. +func (in *CertificateList) DeepCopy() *CertificateList { + if in == nil { + return nil + } + out := new(CertificateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CertificateList) 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 *CertificateSpec) DeepCopyInto(out *CertificateSpec) { + *out = *in + out.SecretRef = in.SecretRef + if in.DNSNames != nil { + in, out := &in.DNSNames, &out.DNSNames + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateSpec. +func (in *CertificateSpec) DeepCopy() *CertificateSpec { + if in == nil { + return nil + } + out := new(CertificateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CertificateStatus) DeepCopyInto(out *CertificateStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]genericcondition.GenericCondition, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateStatus. +func (in *CertificateStatus) DeepCopy() *CertificateStatus { + if in == nil { + return nil + } + out := new(CertificateStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterDomain) DeepCopyInto(out *ClusterDomain) { *out = *in diff --git a/pkg/apis/admin.rio.cattle.io/v1/zz_generated_list_types.go b/pkg/apis/admin.rio.cattle.io/v1/zz_generated_list_types.go index eb3113cd4..3d03b317b 100644 --- a/pkg/apis/admin.rio.cattle.io/v1/zz_generated_list_types.go +++ b/pkg/apis/admin.rio.cattle.io/v1/zz_generated_list_types.go @@ -91,3 +91,20 @@ func NewSystemStack(namespace, name string, obj SystemStack) *SystemStack { obj.Namespace = namespace return &obj } + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// CertificateList is a list of Certificate resources +type CertificateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []Certificate `json:"items"` +} + +func NewCertificate(namespace, name string, obj Certificate) *Certificate { + obj.APIVersion, obj.Kind = SchemeGroupVersion.WithKind("Certificate").ToAPIVersionAndKind() + obj.Name = name + obj.Namespace = namespace + return &obj +} diff --git a/pkg/apis/admin.rio.cattle.io/v1/zz_generated_register.go b/pkg/apis/admin.rio.cattle.io/v1/zz_generated_register.go index eabcf7eff..b5230c4cd 100644 --- a/pkg/apis/admin.rio.cattle.io/v1/zz_generated_register.go +++ b/pkg/apis/admin.rio.cattle.io/v1/zz_generated_register.go @@ -28,6 +28,7 @@ import ( ) var ( + CertificateResourceName = "certificates" ClusterDomainResourceName = "clusterdomains" PublicDomainResourceName = "publicdomains" RioInfoResourceName = "rioinfos" @@ -55,6 +56,8 @@ var ( // Adds the list of known types to Scheme. func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, + &Certificate{}, + &CertificateList{}, &ClusterDomain{}, &ClusterDomainList{}, &PublicDomain{}, diff --git a/pkg/apis/pipeline/v1alpha1/doc.go b/pkg/apis/pipeline/v1alpha1/doc.go new file mode 100644 index 000000000..377bf4bd7 --- /dev/null +++ b/pkg/apis/pipeline/v1alpha1/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2020 Rancher Labs. + +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 main. DO NOT EDIT. + +// +k8s:deepcopy-gen=package +// +groupName=tekton.dev +package v1alpha1 diff --git a/pkg/apis/rio.cattle.io/v1/router.go b/pkg/apis/rio.cattle.io/v1/router.go index 6fec290c8..f9cf8f487 100644 --- a/pkg/apis/rio.cattle.io/v1/router.go +++ b/pkg/apis/rio.cattle.io/v1/router.go @@ -156,6 +156,9 @@ type Match struct { // regex: "value" for ECMAscript style regex-based match Path *StringMatch `json:"path,omitempty"` + // Schema defines schema based match + Schema *StringMatch `json:"schema,omitempty"` + // HTTP Method values are case-sensitive and formatted as follows: // // exact: "value" for exact string match diff --git a/pkg/apis/rio.cattle.io/v1/zz_generated_deepcopy.go b/pkg/apis/rio.cattle.io/v1/zz_generated_deepcopy.go index 668779b91..9b870f3d7 100644 --- a/pkg/apis/rio.cattle.io/v1/zz_generated_deepcopy.go +++ b/pkg/apis/rio.cattle.io/v1/zz_generated_deepcopy.go @@ -504,6 +504,11 @@ func (in *Match) DeepCopyInto(out *Match) { *out = new(StringMatch) **out = **in } + if in.Schema != nil { + in, out := &in.Schema, &out.Schema + *out = new(StringMatch) + **out = **in + } if in.Methods != nil { in, out := &in.Methods, &out.Methods *out = make([]string, len(*in)) diff --git a/pkg/codegen/main.go b/pkg/codegen/main.go index 6992e09ca..8a81a56f2 100644 --- a/pkg/codegen/main.go +++ b/pkg/codegen/main.go @@ -1,6 +1,7 @@ package main import ( + splitv1alpha1 "github.com/deislabs/smi-sdk-go/pkg/apis/split/v1alpha1" adminv1 "github.com/rancher/rio/pkg/apis/admin.rio.cattle.io/v1" v3 "github.com/rancher/rio/pkg/apis/management.cattle.io/v3" riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" @@ -8,6 +9,7 @@ import ( "github.com/rancher/wrangler/pkg/controller-gen/args" solov1 "github.com/solo-io/gloo/projects/gateway/pkg/api/v1/kube/apis/gateway.solo.io/v1" gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1/kube/apis/gloo.solo.io/v1" + "istio.io/client-go/pkg/apis/networking/v1alpha3" ) var ( @@ -25,6 +27,7 @@ func main() { adminv1.RioInfo{}, adminv1.PublicDomain{}, adminv1.SystemStack{}, + adminv1.Certificate{}, }, GenerateTypes: true, }, @@ -60,6 +63,23 @@ func main() { }, GenerateTypes: true, }, + "split.smi-spec.io": { + Types: []interface{}{ + splitv1alpha1.TrafficSplit{}, + }, + PackageName: "split", + GenerateClients: true, + }, + "networking.istio.io": { + Types: []interface{}{ + v1alpha3.Gateway{}, + v1alpha3.VirtualService{}, + v1alpha3.DestinationRule{}, + v1alpha3.ServiceEntry{}, + }, + PackageName: "networking", + GenerateClients: true, + }, }, }) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 096697dfe..d1de6b7e2 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -24,6 +24,8 @@ type ControllerConfig struct { WebhookHost string IPAddresses string Features string + MeshMode string + Gateway Gateway } type Config struct { diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 97b60d853..267ae1a5a 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -4,6 +4,9 @@ const ( AuthWebhookSecretName = "rio-api-validator" AuthWebhookServiceName = "rio-api-validator" + IstioRioGateway = "rio-gateway" + IstioSystemNamespace = "istio-system" + DevWebhookPort = ":7443" RegistryService = "localhost:80" LocalRegistry = "localhost:5442" @@ -23,6 +26,10 @@ const ( GitCommitLabel = "gitwatcher.rio.cattle.io/git-commit" LogTokenLabel = "gitwatcher.rio.cattle.io/log-token" + + LetsEncryptAccountSecretName = "letsencrypt-account" + + AcmeSolverServicName = "rio-acme-solver" ) var ( diff --git a/pkg/constructors/constructors.go b/pkg/constructors/constructors.go index dcce5617a..65004bc76 100644 --- a/pkg/constructors/constructors.go +++ b/pkg/constructors/constructors.go @@ -2,7 +2,7 @@ package constructors import ( splitv1alpha1 "github.com/deislabs/smi-sdk-go/pkg/apis/split/v1alpha1" - tektonv1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + tektonv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1" @@ -103,7 +103,7 @@ func NewEndpoints(namespace, name string, obj v1.Endpoints) *v1.Endpoints { return &obj } -func NewTaskRun(namespace, name string, obj tektonv1alpha1.TaskRun) *tektonv1alpha1.TaskRun { +func NewTaskRun(namespace, name string, obj tektonv1beta1.TaskRun) *tektonv1beta1.TaskRun { obj.APIVersion = "tekton.dev/v1alpha1" obj.Kind = "TaskRun" obj.Name = name diff --git a/pkg/features/controller.go b/pkg/features/controller.go index 8196868c9..938cee5ff 100644 --- a/pkg/features/controller.go +++ b/pkg/features/controller.go @@ -3,6 +3,8 @@ package features import ( "context" + "k8s.io/apimachinery/pkg/api/errors" + riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" "github.com/rancher/rio/pkg/stack" "github.com/rancher/rio/types" @@ -68,7 +70,7 @@ func (f *FeatureController) Stop() error { var errs []error for _, ss := range f.SystemStacks { - if err := ss.Remove(); err != nil { + if err := ss.Remove(); err != nil && !errors.IsNotFound(err) { errs = append(errs, err) } } diff --git a/pkg/server/crds.go b/pkg/server/crds.go index 2157486d9..1e7df73fb 100644 --- a/pkg/server/crds.go +++ b/pkg/server/crds.go @@ -23,7 +23,8 @@ func getCRDs() []crd.CRD { crds = append(crds, newClusterCRD("ClusterDomain.admin.rio.cattle.io/v1", rioadminv1.ClusterDomain{}), - newClusterCRD("PublicDomain.admin.rio.cattle.io/v1", rioadminv1.PublicDomain{})) + newClusterCRD("PublicDomain.admin.rio.cattle.io/v1", rioadminv1.PublicDomain{}), + newClusterCRD("Certificate.admin.rio.cattle.io/v1", rioadminv1.Certificate{})) crds = append(crds, crd.NonNamespacedTypes( "RioInfo.admin.rio.cattle.io/v1", diff --git a/pkg/server/startup.go b/pkg/server/startup.go index 772b71cdb..4f26e2bb5 100644 --- a/pkg/server/startup.go +++ b/pkg/server/startup.go @@ -74,22 +74,32 @@ func configureFeature(rioContext *types.Context, systemNamespace string) error { if conf.Features == nil { conf.Features = map[string]config.FeatureConfig{} } - t := true if config.ConfigController.Features == "*" { - f := conf.Features["*"] - f.Enabled = &t - conf.Features["*"] = f + setFeature(&conf, "*", true) } else { for _, feature := range strings.Split(config.ConfigController.Features, ",") { if feature == "" { continue } - f := conf.Features[feature] - f.Enabled = &t - conf.Features[feature] = f + setFeature(&conf, feature, true) } } + if config.ConfigController.MeshMode == "istio" { + setFeature(&conf, "istio", true) + setFeature(&conf, "gloo", false) + setFeature(&conf, "linkerd", false) + } else if config.ConfigController.MeshMode == "linkerd" { + setFeature(&conf, "istio", false) + setFeature(&conf, "gloo", true) + setFeature(&conf, "linkerd", true) + } + + if config.ConfigController.Gateway.ServiceName != "" && config.ConfigController.Gateway.ServiceNamespace != "" { + conf.Gateway.ServiceName = config.ConfigController.Gateway.ServiceName + conf.Gateway.ServiceNamespace = config.ConfigController.Gateway.ServiceNamespace + } + featureConfig, err := config.SetConfig(constructors.NewConfigMap(systemNamespace, config.ConfigName, v1.ConfigMap{}), conf) if err != nil { return err @@ -106,6 +116,12 @@ func configureFeature(rioContext *types.Context, systemNamespace string) error { return err } +func setFeature(conf *config.Config, featureName string, enabled bool) { + f := conf.Features[featureName] + f.Enabled = &enabled + conf.Features[featureName] = f +} + func Types(ctx context.Context, config *rest.Config) error { factory, err := crd.NewFactoryFromClient(config) if err != nil { diff --git a/pkg/stack/systemstack.go b/pkg/stack/systemstack.go index 8fc171c9e..a8c55378c 100644 --- a/pkg/stack/systemstack.go +++ b/pkg/stack/systemstack.go @@ -1,7 +1,6 @@ package stack import ( - adminv1 "github.com/rancher/rio/pkg/apis/admin.rio.cattle.io/v1" adminv1controller "github.com/rancher/rio/pkg/generated/controllers/admin.rio.cattle.io/v1" "github.com/rancher/rio/pkg/riofile" "github.com/rancher/rio/pkg/template" @@ -9,7 +8,6 @@ import ( "github.com/rancher/wrangler/pkg/apply" "github.com/rancher/wrangler/pkg/objectset" "github.com/sirupsen/logrus" - "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/dynamic" @@ -31,17 +29,19 @@ func NewSystemStack(apply apply.Apply, stacks adminv1controller.SystemStackClien name: name, Stack: Stack{}, } - if stacks != nil { - stack, err := stacks.Get(name, metav1.GetOptions{}) - if errors.IsNotFound(err) { - stack, _ = stacks.Create(&adminv1.SystemStack{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - }) - } - s.apply = s.apply.WithSetOwnerReference(true, true).WithOwner(stack) - } + // todo: figure out owner reference doesn't work with tekton and k8s 1.17 + //if stacks != nil { + // stack, err := stacks.Get(name, metav1.GetOptions{}) + // if errors.IsNotFound(err) { + // stack, _ = stacks.Create(&adminv1.SystemStack{ + // ObjectMeta: metav1.ObjectMeta{ + // Name: name, + // }, + // }) + // } + // + // s.apply = s.apply.WithSetOwnerReference(true, true).WithOwner(stack) + //} contents, err := s.content() if err != nil { logrus.Fatal(err) diff --git a/pkg/webhook/webhook.go b/pkg/webhook/webhook.go index 978ea667c..e288fc138 100644 --- a/pkg/webhook/webhook.go +++ b/pkg/webhook/webhook.go @@ -8,12 +8,11 @@ import ( "os" "path/filepath" - "github.com/rancher/rio/pkg/config" - "github.com/linkerd/linkerd2/controller/k8s" "github.com/linkerd/linkerd2/controller/webhook" "github.com/linkerd/linkerd2/pkg/tls" riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" + "github.com/rancher/rio/pkg/config" "github.com/rancher/rio/pkg/constants" "github.com/rancher/rio/types" admissionv1beta1 "k8s.io/api/admission/v1beta1" diff --git a/scripts/test b/scripts/test index 49bb7aca9..dce86c22c 100755 --- a/scripts/test +++ b/scripts/test @@ -36,7 +36,7 @@ if [[ ${ARCH} == amd64 ]]; then export KUBECONFIG=/etc/rancher/k3s/k3s.yaml export PATH=$(pwd)/bin:$PATH - RUN_API_VALIDATOR=true RUN_API_VALIDATOR_PORT=:7443 RUN_API_VALIDATOR_HOST=127.0.0.1 rio-controller &> ./rio-controller-log & + RUN_API_VALIDATOR=true RUN_API_VALIDATOR_PORT=:7443 RUN_API_VALIDATOR_HOST=127.0.0.1 rio-controller --mesh-mode linkerd &> ./rio-controller-log & rio install --check 2>&1 kubectl get po -n rio-system diff --git a/stacks/bindata.go b/stacks/bindata.go index 4381eff90..4c791d020 100644 --- a/stacks/bindata.go +++ b/stacks/bindata.go @@ -1,4 +1,4 @@ -// Package stacks Code generated by go-bindata. (@generated) DO NOT EDIT. +// Code generated for package stacks by go-bindata DO NOT EDIT. (@generated) // sources: // stacks/bindata-non-static.go // stacks/build-stack.yaml @@ -272,7 +272,7 @@ func stacksRioAutoscalerStackYaml() (*asset, error) { return a, nil } -var _stacksRioBootstrapStackYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xe4\x59\x5f\x6f\xe3\x36\x0c\x7f\xef\xa7\x10\x82\x01\x07\x1c\x60\x1f\xb6\xdd\xc3\x90\xb7\xae\x38\xec\x65\x3b\x14\xed\x6d\xef\x8c\xcc\xb8\x5c\x64\x49\xa0\xa8\x64\xb9\x6d\xdf\x7d\xf0\x9f\xb8\x4e\xea\x3a\xa9\x1b\xe7\xb6\xdd\x5b\x44\x52\xe4\x8f\x7f\x4c\x89\xca\x2a\x2e\x90\x2d\x0a\x86\xf9\x95\x52\x05\x58\x5a\x62\x90\xb9\xfa\x2b\xb9\x52\x4a\x29\xf0\xf4\x1b\x72\x20\x67\xe7\x6a\xfd\x6d\x45\x5a\x91\xcd\xe6\xea\x23\x14\x18\x3c\x68\xac\x68\x05\x0a\x64\x20\x30\xaf\x56\x4a\x59\x28\x70\xae\xbe\xf9\xf3\xe3\xf5\x2f\x1f\xee\x6f\xaf\x6f\x3e\xfc\xdd\x30\x0c\x2c\xd0\x84\x9d\x98\x62\x72\xa9\x06\x11\x83\x29\xb9\x77\x14\x92\xb0\x0d\x82\xc5\x5c\xcd\x84\x23\xce\x2a\xb1\x24\x79\x0a\x85\x17\xa0\x53\x88\xf2\xe0\x98\x3e\x83\x90\xb3\xe9\xea\x87\x50\xea\xd8\x03\x79\x63\x62\x10\xe4\x3b\x67\x86\x60\x32\xb9\x44\xd7\x92\x09\x64\x05\xd9\x8a\xcb\xd1\x60\x03\x34\x29\x6d\xff\xc4\x2e\xfa\x16\x79\xa2\x66\xb3\xe6\x27\x63\x70\x91\x35\x76\x79\x6f\x77\xcc\x35\xf2\xa2\x87\x91\x28\xeb\xec\x5d\xb3\xf1\xd7\xbb\x9f\x4f\xd9\xfb\xe6\xed\x9b\x01\x34\xe0\x89\x31\xa7\x20\xdc\x0d\xc7\x10\x46\xf0\x14\x90\xd7\xa4\x31\x1c\x45\xdb\x6b\x31\x2b\x28\x94\xe9\x78\xa1\xdd\x22\x0a\x08\xd9\x7c\x83\x8b\x07\xe7\x56\xda\xd9\x25\xe5\xb1\xde\xde\x22\x49\xd4\x6c\x0d\x86\xb2\xe3\x92\x2f\xc2\x8c\x7f\x08\xda\x70\x60\x08\xbc\x0f\x43\x80\x19\xbd\x21\x0d\x01\xa5\xbb\x2b\x43\x6f\xdc\xb6\x40\xbb\x4f\x05\x2c\x9c\x3d\x10\x0d\x02\x82\xcb\x68\xba\xe4\x17\x25\x77\x11\xc9\x64\xe9\xca\x82\xd0\x1a\xd3\x0c\xd7\x1d\xe5\x1a\xf4\x03\xd9\x3c\x25\x2b\xc8\x16\x4c\x9f\xd8\xb8\x12\xed\x43\xa2\x91\x25\x29\xc0\x42\x8e\x7c\x24\xcf\xe3\x0c\x58\x94\x8d\xe3\x55\xe9\xd1\x7e\x29\xf5\x66\xaf\xcf\x2e\xd9\x9c\x31\x04\xec\x66\xa0\xa5\xbd\x2b\x73\x11\xc7\x15\xcf\x02\x44\x3f\x9c\xdf\x63\x88\xe2\x82\x06\x43\x36\x3f\xbf\xf2\x67\x1b\xe5\x04\x7e\x94\xad\x33\xdd\xeb\xe8\x13\xf8\x33\xad\xfa\x9c\x64\x53\x66\x19\x79\x4a\x2b\x86\xec\x0a\x39\x3b\xa2\xbb\x69\xce\x9e\xdd\x92\xcc\xd8\x1e\xed\xe9\xf1\xa3\x39\x21\xf3\x3a\x06\x71\xc5\x8e\x93\xe1\x92\x2c\x8d\xef\xb6\x65\xdd\xa1\x15\xd2\x75\xe1\x51\x10\x72\x93\x04\xd4\x3b\x43\x7a\x3b\xa4\xd7\xbb\x2c\xa0\x8e\x4c\xb2\xad\x84\x69\x64\x40\x83\x37\x24\x69\x28\x28\x09\x1e\xf5\x11\x67\x84\x61\xb9\x24\x5d\xed\x19\x69\x4e\x1c\x43\x8e\x27\x64\xae\x91\xd4\x06\xba\xad\xef\x65\xc5\x0f\x82\x1b\xd8\xa6\xc1\x99\x32\x4d\xe9\xfa\xbb\xd9\xb3\xcc\x2e\xc7\x38\x77\x48\x3e\x5b\x6a\x05\x57\xe2\x6c\x79\x96\x3d\xaf\x59\x20\xac\x42\xbb\x6a\x6e\x72\xfb\xc4\x72\xc5\xd1\x3e\x12\x3c\x79\x34\x64\xf1\x29\xa5\x57\xac\xb5\xda\xef\x42\x8e\xd2\xfe\x36\x14\x1e\x17\x9a\x11\x04\xdb\x65\xf4\x59\x77\x99\xa1\xc1\xce\xd2\x97\x9d\xa7\x5d\x6d\xda\xd5\xab\x02\x53\xba\xf3\x6e\x49\x16\x0c\x7d\x46\xee\x77\xf7\x29\xff\xbf\xe3\xdf\xee\x44\xef\xcd\xff\x21\xaf\x0d\xc8\x01\xbd\xad\x86\xe7\x18\x43\x9b\x5a\x60\xfb\x02\x5f\x3e\x84\x75\x57\x3c\x08\x5f\x35\xb7\x1d\x86\x39\x39\xfc\x1c\x7a\x82\xdd\xd3\x41\xfb\x3d\x8d\xa1\xc6\x7b\x9e\xb9\xed\x47\xb2\x19\xd9\xbc\x62\x0e\x8c\x6f\xce\x0a\x3b\x63\x90\x93\x45\x67\x03\x3b\x83\x77\xb8\xdc\xc9\xef\x62\x34\x80\xa3\x91\xec\x1f\x1f\x87\x07\xc6\x10\x17\xbf\xa3\x96\x76\x66\xac\x75\xdc\xd7\xa7\xf8\xb5\xd6\x2e\x5a\x19\x02\xde\x9c\xf7\xf0\x44\xb2\x1a\xb3\x7b\xa6\xe9\xbe\x00\xef\x85\xb1\xc7\xf6\x49\x21\x9c\x02\x89\x66\x3c\x86\x00\x3c\x25\xcd\xd0\xe7\xf8\x8c\xfe\x9f\xd9\x6c\x79\xf0\xef\xb4\x78\xc7\xd2\x29\xfd\x5a\xe9\x83\x88\x0f\xc9\xfb\xf7\xdf\x37\xf4\x5a\x6c\xae\xf6\x28\xec\xc4\x69\x67\xe6\xea\xd3\xcd\x6d\x4b\x15\xe0\x1c\xe5\xf6\x40\x3a\xa0\x41\x2d\x8e\xdb\x57\x93\x83\x7c\x5d\xfe\xb5\xe4\xf4\x57\x92\xbd\xfb\xfa\xb9\x2f\x07\x3d\x13\xc7\xd9\x4d\x34\xc3\x19\x4e\x6b\xe6\x94\xd3\x6e\xb6\x3b\xbd\x46\x5d\xee\x26\x98\xf7\x3a\xcf\x26\x27\x0f\xe7\xe3\x5f\x1d\x76\x8f\x0e\x53\x8d\xaf\x2f\xba\xd9\x4f\x39\xf7\x8d\xd3\xfd\x2a\x8d\x97\xe8\x19\x8c\x90\x39\x6b\xb6\x97\x69\x1b\x39\xca\xac\x1b\xf8\xd0\x5d\x6e\x1e\x1f\x72\xa6\xfb\x1a\x5f\x85\x60\x5c\x3a\x5f\x67\xf2\xbc\xdf\xf3\xc9\x58\x2e\x51\x7b\x9e\x69\x4d\x06\x73\xcc\xbe\xfc\xa1\xf5\x7f\x2f\xae\xba\x61\x57\x43\xcd\x57\x5a\x6d\x41\xc0\x66\xc0\x17\xaa\xb5\xd1\x79\xa9\x08\xf5\xb8\xb9\xf7\xb7\x46\x39\x70\x76\x08\xfe\x2b\x6f\x96\xff\xfa\x7a\xbe\x12\x2c\xbc\x01\xc1\x72\x7f\xee\x3e\xed\x56\xaa\x1c\x0a\xae\x94\x42\xbb\xbe\x8f\x8b\x20\x35\xe1\x9f\x00\x00\x00\xff\xff\x15\x16\x17\xdf\xf0\x1d\x00\x00") +var _stacksRioBootstrapStackYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xe4\x19\x5d\x6f\xdb\x46\xec\x3d\xbf\xe2\x60\x0c\x28\x50\x40\x4e\xb7\xf5\x21\xf0\x5b\x16\x14\x7b\xd9\x8a\x20\xe9\xf6\x4e\x9f\x68\x85\xf3\xe9\xee\xc0\xa3\xec\xb9\xdb\xfe\xfb\x20\x59\x52\x24\x45\xfe\x88\x22\xa5\xdd\xfa\xe6\x23\x79\xfc\x16\x8f\xa4\xd7\xd9\x12\xd9\xa2\x60\x58\x5c\x28\x95\x82\xa5\x15\x06\x59\xa8\xbf\xa3\x0b\xa5\x94\x02\x4f\xbf\x23\x07\x72\x76\xa1\x36\xdf\x17\xa0\x35\xd9\x78\xa1\x3e\x42\x8a\xc1\x83\xc6\x02\x96\xa2\x40\x0c\x02\x8b\xe2\xa4\x94\x85\x14\x17\xea\xbb\xbf\x3e\x5e\xff\xfa\xe1\xfe\xf6\xfa\xe6\xc3\x3f\x25\xc2\xc0\x12\x4d\xa8\xc8\x14\x93\x9b\x6b\x10\x31\x38\x27\x77\x49\x21\x0a\xbb\x20\x98\x2e\xd4\x4c\x38\xc3\x59\x41\x16\x45\x4f\x55\xe1\x25\xe8\x39\x64\xf2\xe0\x98\x3e\x83\x90\xb3\xf3\xf5\x55\xc8\x79\xb4\x94\xbc\x31\x59\x10\xe4\x3b\x67\x8e\xa9\xc9\xe4\x22\xbd\xa7\x8c\x20\x4e\xc9\x16\x58\xce\x0c\x96\x8a\x46\xb9\xec\x9f\xd9\x65\xbe\xd6\x3c\x52\xb3\x59\xf9\x93\x31\xb8\x8c\x35\x36\x71\x6f\x2b\xe4\x06\x79\xd9\x83\x88\x94\x75\xf6\xae\xbc\xf8\xdb\xdd\x2f\xe7\xdc\x7d\xf3\xf6\xcd\x11\x6d\xc0\x13\x63\x42\x41\xb8\xe9\x8e\x63\x3a\x82\xa7\x80\xbc\x21\x8d\xe1\xa4\xb6\xbd\x12\xe3\x94\x42\x1e\x8e\x67\xca\x4d\x33\x01\x21\x9b\x6c\x71\xf9\xe0\xdc\x5a\x3b\xbb\xa2\x24\xdb\x5f\xaf\x35\x89\xd4\x6c\x03\x86\xe2\xd3\x94\xcf\xd2\x19\xff\x14\xb4\xa1\x23\x08\xbc\x0f\xc7\x14\x66\xf4\x86\x34\x04\x94\xe6\xad\x18\xbd\x71\xbb\x14\x6d\x1b\x0a\x98\x3a\xdb\x21\x0d\x02\x82\xab\xcc\x34\xc1\xcf\x0a\xee\x32\x23\x13\xcf\xd7\x16\x84\x36\x38\x8f\x71\xd3\x60\xae\x41\x3f\x90\x4d\xe6\x64\x05\xd9\x82\xe9\x23\x1b\x96\xa2\x7d\x9a\x68\x64\x89\x52\xb0\x90\x20\x9f\x88\xf3\x30\x01\x16\x65\xeb\x78\x9d\x5b\xd4\x4e\xa5\xde\xe8\xf5\xc9\x25\x9b\x30\x86\x80\xcd\x08\xd4\xb0\xcb\x3c\x16\xd9\xb0\xe4\x59\x82\xe8\x87\xf1\x2d\x86\x4c\x5c\xd0\x60\xc8\x26\xe3\x33\x3f\x58\x28\x27\xb0\x23\x2f\x9d\xf3\x56\x45\x9f\xc0\x9e\x69\xd9\x27\x24\xdb\x3c\xca\xc8\x53\x4a\x31\x64\xd7\xc8\xf1\x09\xde\x65\x71\xf6\xec\x56\x64\x86\xd6\x68\x4f\x8f\x1f\xcd\x19\x91\xd7\x59\x10\x97\x56\x98\x18\x57\x64\x69\x78\xb5\xcd\xf3\x0e\xad\x90\xde\x27\x1e\x05\x21\x37\x89\x43\xbd\x33\xa4\x77\xc7\xf8\x7a\x17\x07\xd4\x19\x93\xec\x0a\x62\x1a\xe8\xd0\xe0\x0d\xc9\x3c\xa4\x14\x05\x8f\xfa\x84\x31\xc2\xb0\x5a\x91\x2e\xee\x0c\x14\x27\x8e\x21\xc1\x33\x22\x57\x52\x6a\x03\xcd\xd2\xf7\xbc\xe4\x07\xc1\x2d\xec\xe6\xc1\x99\x3c\x4c\xf3\xcd\x0f\xb3\x83\xc8\x26\xc6\x38\xd7\x05\x8f\x16\x5a\xc1\xb5\x38\x9b\xbf\x65\x87\x39\x0b\x84\x75\xa8\x4f\x65\x27\xd7\x06\xe6\x27\xce\xec\x23\xc0\x93\x47\x43\x16\x9f\x42\x7a\xc9\x6a\xa9\xfd\x26\x24\x28\xf5\x6f\x43\xe1\xf1\xa0\x19\x41\xb0\x3e\x66\x3e\x6e\x1e\x63\x34\xd8\x38\xfa\xbc\xf2\xd4\xa7\x6d\x7d\x7a\x91\x63\x72\x73\x2e\x57\x64\xc1\xd0\x67\xe4\x7e\x73\x9f\xe2\xff\x3b\xf6\x55\x2f\x7a\x6f\xfc\xbb\xb8\xda\x21\x1d\x78\x9d\x0d\x87\x10\xc7\x2e\xd5\x8a\xb5\x09\xbe\xbc\x0b\xf7\x55\xb1\xe3\xbe\x62\x6e\xeb\xba\x39\xea\x7e\x0e\x3d\xce\xee\xa9\xa0\xfd\x96\x66\x01\x0f\xeb\xd4\x68\xef\xaa\xe7\xe0\xb0\xc8\xaa\x29\x3e\xdc\x2d\x8f\x32\x1a\xfe\x44\x36\x26\x9b\x14\xc8\x23\x13\xa2\xb3\xc2\xce\x18\xe4\x68\xd9\xb8\xc0\xce\xe0\x1d\xae\x2a\xfa\xca\xe4\x23\x7a\x94\x94\xfd\x13\xea\xf1\x99\x34\x64\xcb\x3f\x50\x4b\x3d\x96\xee\x79\xdc\xef\x1b\x85\x6b\xad\x5d\x66\xe5\x98\xe2\x65\x4b\x01\x4f\x28\x8b\x49\xbe\x67\x60\xef\x73\x70\xcb\x8d\x3d\xb2\xcf\x72\xe1\x14\x9a\x68\xc6\x53\x1a\x80\xa7\xa8\x9c\x2b\x1d\x8f\x68\xff\xc8\x62\xf3\xde\xa2\xe2\xe2\x1d\x4b\xf3\x03\x2a\x98\x3e\x88\xf8\x10\xbd\x7f\xff\x63\x09\xdf\x93\x2d\x54\x0b\xc2\x4e\x9c\x76\x66\xa1\x3e\xdd\xdc\xd6\x50\x01\x4e\x50\x6e\x3b\xd4\x01\x0d\x6a\x71\x5c\x2f\x66\x3a\xf1\x3a\xb9\x90\x19\xe2\x14\x9d\x62\x14\x9c\xd9\xe0\x68\x2e\x89\xae\xde\x5d\xbd\xeb\xb8\xa4\x0d\x3a\xe9\x93\x06\xf9\x8b\x9d\x32\xfa\x96\xea\xfc\xed\x54\x6b\x4e\x1a\xbb\x29\xeb\x99\xf4\x46\x17\x51\x0e\xc5\x38\xad\x98\x73\xba\x8c\x59\xd5\x35\x0c\x6a\xaa\x27\x98\xb3\x1b\xeb\xaa\xb3\x97\x22\xc3\xb7\x3d\xd5\xb2\x67\xaa\xb5\xc1\xb3\x26\xaa\x29\xe7\xed\x61\xbc\x47\xe7\xf8\x35\x76\x48\x27\xca\x12\x23\xc4\xce\x9a\xdd\xeb\x54\xa6\x04\x65\xd6\x8c\x6d\x68\x1e\xb7\x8f\x3b\xba\xe9\x3e\xf8\x17\x69\x30\x2c\x63\x5e\x26\x72\xdc\x92\x71\xb6\x2e\xaf\x91\x7b\x9e\x69\x43\x06\x13\x8c\xbf\xfc\xbb\xf8\x7f\x4f\xae\xfd\x9b\x50\xcc\xab\xdf\x68\xb6\x05\x01\x1b\x03\xbf\x52\xae\x0d\x8e\x4b\x01\xd8\x6f\x12\x5a\xff\x58\x19\x6c\x01\xfc\x37\x5e\x2c\xbf\xfa\x7c\xbe\x10\x4c\xbd\x01\xc1\xfc\x7e\xe2\x3e\x55\x27\x95\xcf\x1d\x17\x4a\xa1\xdd\xdc\x67\xcb\x20\x7b\xc0\xbf\x01\x00\x00\xff\xff\x38\x07\xf2\xb9\xcb\x1f\x00\x00") func stacksRioBootstrapStackYamlBytes() ([]byte, error) { return bindataRead( @@ -287,12 +287,12 @@ func stacksRioBootstrapStackYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "stacks/rio-bootstrap-stack.yaml", size: 7664, mode: os.FileMode(420), modTime: time.Unix(1557785965, 0)} + info := bindataFileInfo{name: "stacks/rio-bootstrap-stack.yaml", size: 8139, mode: os.FileMode(420), modTime: time.Unix(1557785965, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _stacksRioControllerStackYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x54\x4d\x6f\xdb\x30\x0c\xbd\xf7\x57\x10\xc6\xae\x6a\xb6\x61\x27\xdd\xbc\x25\x28\x02\x34\x1f\x48\xdb\x5c\x03\x46\x66\x52\xa1\xb2\xe4\xea\xc3\x43\x90\xe5\xbf\x0f\x96\x9d\x34\x96\xdb\x6e\xba\x89\x7c\x7a\x7c\x14\x9f\xf4\x12\xb6\x64\x35\x79\x72\xfc\x06\xa0\x44\x2d\x77\xe4\x3c\x87\x3f\xec\x06\x00\x00\x2b\xb9\x26\xeb\xa4\xd1\x1c\xb0\xaa\xdc\xa8\xfe\x16\xe3\x2f\x52\x17\x1c\xc6\x54\x29\x73\x28\x49\xfb\x18\x2c\xc9\x63\x81\x1e\x79\xdc\x01\x68\x2c\x89\x83\x95\x86\x09\xa3\xbd\x35\x4a\x91\xbd\x4a\xb9\x0a\x05\x71\xf8\x72\x9c\xe7\xb3\xc9\xc3\x32\xff\x35\x39\xc5\xac\xab\x48\x9c\x29\x1c\x29\x12\xde\xd8\xf3\xbe\x91\xe8\xc5\xf3\x3d\x6e\x49\xb9\xb7\x20\x24\x55\x38\x64\xde\x06\xca\x3a\x80\xa7\xb2\x52\xe8\xe9\x8a\x25\x91\xda\x2c\x35\x20\xfd\x07\x6d\x5f\x6a\x2b\xd7\xd6\x52\x50\x2e\x84\x09\xda\xcf\xdf\xe9\x9f\x75\x10\x6c\x21\x57\x67\x6b\xa3\x42\x49\xbd\xfa\xac\xbb\x42\x47\xc2\x92\x67\x58\x49\x56\xa3\x92\x05\x7a\x63\x7b\x32\x5b\x40\x5f\x3a\x40\x41\x3b\x0c\xca\xcf\x4c\x41\x1c\x7e\x7c\xff\x9a\xa4\x4d\xe5\xa5\xd1\xa8\x38\x34\x4d\x25\xc9\x96\xf1\xad\x83\x8f\x6a\x37\x9d\xa1\xd4\x64\x13\xe1\xb2\xc4\x7d\x1c\xee\x74\x96\xdf\x75\x83\x3d\xaf\x98\x5b\x06\xa5\x96\x46\x49\x71\xe0\x90\xab\xdf\x78\x70\x3d\xcc\x27\xde\x69\x17\xda\x7d\x32\x2b\xf6\x19\xfc\x78\x64\x20\x77\x40\xaf\x70\xbb\x46\x15\xc8\xdd\xae\xa6\x8b\xcd\x78\xf2\xf3\xe9\xae\x1b\x2a\x9c\x4e\x09\x1d\x63\x05\x6d\xc3\x7e\xc0\x43\xba\x78\x07\xbb\x23\xf4\xc1\x92\x4b\x12\x59\xd6\x0b\x90\xae\x53\xd5\x6d\xab\xab\xa7\xf9\x26\x5f\x4e\x37\xeb\xfc\x7e\x3a\xce\x1f\x17\xab\x64\x20\x75\xa3\xba\xb9\xd0\x01\x30\x95\xd2\xf1\x4d\x17\x9b\xcb\xc3\xfa\x88\x2b\x79\x79\x97\x7c\xb4\xe2\xac\x31\xe8\xe0\x8e\xcb\x26\xba\x44\xff\xcc\x61\x54\xa3\x1d\xd9\xa0\x47\x56\x9a\x91\x73\x2a\xa9\xf2\x9f\xde\x05\xb0\x84\xc5\x42\xab\x43\x67\xc4\x9b\xeb\xe7\xba\x37\x8f\xe7\xdd\xd9\xa6\xa4\xeb\x87\xb0\x6d\x3e\xa9\x2e\xf0\x1a\xc8\x35\x56\x8e\x5a\x19\xd4\x68\x25\x6e\x15\x71\xe8\xf7\x5f\x90\x13\x56\x46\xd3\x73\xc8\x2e\x3f\x10\x78\x03\x45\xfc\xc8\xc0\x9b\x2c\x61\xb8\x98\x24\x8d\x0f\xa6\xf5\x37\x00\x00\xff\xff\x7f\x2a\x3e\x42\x49\x05\x00\x00") +var _stacksRioControllerStackYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x54\x4d\x6f\x1a\x31\x10\xbd\xf3\x2b\x46\xab\x5c\x1d\xda\xaa\x27\xdf\xb6\x05\xa5\x48\xd9\x80\x20\xe1\x8a\x86\xf5\x00\x56\xbc\xf6\xc6\x1f\x5b\x21\xca\x7f\xaf\xf6\x03\xc2\x1a\x92\xd6\x37\xcf\x9b\x7d\x7e\xb3\xef\xd9\xaf\x61\x4d\x56\x93\x27\xc7\x07\x00\x05\x6a\xb9\x21\xe7\x39\xfc\x61\x03\x00\x00\x2c\xe5\x92\xac\x93\x46\x73\xc0\xb2\x74\xc3\xea\x6b\x53\x7f\x95\x5a\x70\x18\x51\xa9\xcc\xbe\x20\xed\x9b\x62\x41\x1e\x05\x7a\xe4\xcd\x0e\x40\x63\x41\x1c\xac\x34\x2c\x37\xda\x5b\xa3\x14\xd9\x0b\xc8\x95\x98\x13\x87\xbb\xc3\x53\x9a\x8d\x17\xb3\xf4\xe7\xf8\xd8\xa0\xae\xa4\xfc\x44\xe1\x48\x51\xee\x8d\x3d\xed\x6b\x89\x3e\xdf\x3d\xe2\x9a\x94\x7b\x2f\x42\x74\x0a\x87\xc4\xdb\x40\x49\xd7\xe0\xa9\x28\x15\x7a\xba\x60\x89\xa4\xd6\x4b\x5d\x91\xfe\x83\xb6\x2f\xb5\x95\x6b\x2b\x99\x53\x9a\xe7\x26\x68\xff\x74\x63\x7e\xd6\xb5\x60\xdb\x72\xf1\x6d\x65\x54\x28\xa8\x77\x3e\xeb\x7e\xa1\xa3\xdc\x92\x67\x58\x4a\x56\xa1\x92\x02\xbd\xb1\x3d\x99\x6d\x43\x5f\x3a\x80\xa0\x0d\x06\xe5\x33\x23\x88\xc3\xf7\x6f\x5f\x22\xd8\x94\x5e\x1a\x8d\x8a\x43\x3d\x54\x04\xb6\x8c\xef\x13\x7c\x74\x76\x3d\x19\x4a\x4d\x36\x12\x2e\x0b\xdc\x36\xe6\x4e\xb2\xf4\xa1\x33\xf6\xb4\x1a\x6c\x16\x94\x9a\x19\x25\xf3\x3d\x87\x54\xfd\xc6\xbd\xeb\xf5\x7c\x92\x9d\x76\xa1\xdd\x46\x5e\xb1\xcf\xda\x0f\x07\x06\x72\x03\xf4\x06\xf7\x4b\x54\x81\xdc\xfd\x7c\x32\x5d\x8d\xc6\x3f\x5e\x1e\x3a\x53\xe1\x78\x8c\xe8\x18\x13\xb4\x0e\xdb\x2b\x1e\xd2\xe2\x46\x6f\x41\x6e\xc7\x0a\x23\x28\x42\xee\x0e\xd9\x78\xf1\x6b\x95\x4d\x47\xe3\xeb\x8f\x36\x84\x3e\x58\x72\x11\x90\x24\xbd\x02\xe9\x2a\x1e\xb5\xfd\x3f\xf3\x97\xa7\x55\x3a\x9b\xac\x96\xe9\xe3\x64\x94\x3e\x4f\xe7\x91\x8b\x55\x3d\x6a\xed\xc2\x55\x63\x2c\xa5\xe3\x9b\x4c\x57\xe7\xdb\xf8\x11\x57\x74\x5d\xcf\x78\x93\xdf\xac\x4e\xf5\x95\x31\x45\x5d\x9d\xa1\xdf\x71\x18\x56\x68\x87\x36\xe8\xa1\x95\x66\xe8\x9c\x8a\x4e\xf9\xcf\xc0\x03\x58\x42\x31\xd5\x6a\xdf\xa5\x77\x70\x79\xc7\xb7\xe6\xf9\xb4\x3b\x65\x9b\x74\xb5\x08\xeb\xfa\x65\xeb\x0a\x6f\x81\x5c\x9d\xff\x46\x2b\x83\x0a\xad\xc4\xb5\x22\x0e\xfd\xf9\x05\xb9\xdc\xca\xe6\xa6\x70\x48\xce\xcf\x16\x78\x03\xa2\x79\xfd\xc0\x9b\x24\x62\x38\x27\x2b\xae\xdf\x70\xeb\x12\x3f\xe7\x64\xf0\x37\x00\x00\xff\xff\x8d\xee\x55\x32\x96\x05\x00\x00") func stacksRioControllerStackYamlBytes() ([]byte, error) { return bindataRead( @@ -307,12 +307,12 @@ func stacksRioControllerStackYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "stacks/rio-controller-stack.yaml", size: 1353, mode: os.FileMode(420), modTime: time.Unix(1557785965, 0)} + info := bindataFileInfo{name: "stacks/rio-controller-stack.yaml", size: 1430, mode: os.FileMode(420), modTime: time.Unix(1557785965, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _stacksSmiStackYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x52\xbb\x8e\xe3\x30\x0c\xec\xfd\x15\xfc\x01\xfb\x90\xee\xa0\x36\x57\x5d\x91\x0b\x2e\xc1\xf6\x8c\x4c\xc7\x44\xf4\x82\x48\x19\x59\x60\x3f\x7e\x61\xc5\xce\x3a\x8b\xad\x48\x8a\x43\x72\x66\xa0\x5b\xb9\x50\x0e\xa4\x24\xa6\x01\xf0\x18\x78\x20\x51\x03\x1f\x6d\x03\x00\xd0\xb6\x8f\x88\x89\xdf\x28\x0b\xc7\x60\xe6\x9c\xee\x4a\x61\xae\xa4\xbb\xfd\x96\x8e\xe3\xaf\x69\x77\x21\xc5\x5d\x05\xdf\x38\xf4\x06\xf6\x45\x34\xfa\xff\x24\xb1\x64\x4b\x7f\x68\xe0\xc0\xca\x31\x54\x88\x27\xc5\x1e\x15\x4d\xad\x00\x02\x7a\x32\xa0\x19\x87\x81\xad\x24\xc7\x2a\x5d\x0d\x9d\x78\x6e\x25\x91\xed\x38\x56\xec\x9c\xaf\x53\xd7\x1c\x4b\x32\xf0\x33\x10\x60\x5a\x19\x4f\x3b\x74\x69\x5c\xd8\x01\x88\x8d\x89\x0c\x1c\xd0\x93\x24\xb4\xd4\x6f\x48\xc8\xba\x7b\x95\x71\x7e\x70\x3a\xcd\x37\x9e\x2d\x19\x63\xd6\xc3\x2b\x1c\xa0\x05\x95\x67\x99\x5c\xc9\xe8\xbe\x69\xfa\x5a\xc0\xe1\x5a\x1c\xe6\xd7\xfe\xd2\xc6\xbe\xaf\x4e\xa1\x3b\x66\x0e\x4a\x79\x1f\x5d\xf1\x61\x73\xab\x5d\x0c\x3b\x51\x9e\xd8\xd2\x86\x83\xbe\xcf\xd2\x44\x33\x87\xeb\xe6\xb9\x27\xb1\x99\x93\x56\x3b\xce\x23\x01\x26\xba\x83\x3c\xc6\x21\x0e\xa0\x23\xcb\x62\xe4\x66\xec\xef\xe9\xdf\xe1\x88\x3a\x1a\xe8\xaa\xb5\xb2\xb9\x37\x7f\x8d\xa6\xf9\x0c\x00\x00\xff\xff\x6c\x0d\xd6\x42\x40\x02\x00\x00") +var _stacksSmiStackYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x52\xc1\x0e\x9b\x30\x0c\xbd\xf3\x15\xfe\x01\x98\x7a\x9b\x72\xed\x4e\x3b\x74\xd5\x5a\xed\xee\x06\x03\x56\x21\x89\x6c\x07\x75\xd2\x3e\x7e\x22\x85\x96\x4e\x3b\x25\x76\x9e\xfd\x9e\x9f\x73\xcf\x37\x92\x40\x46\xea\x2a\x80\x09\x03\x77\xa4\xe6\xe0\x4f\x5d\x01\x00\xd4\xf5\xf3\xc4\xc4\xbf\x48\x94\x63\x70\xcb\x9d\x1e\x46\x61\x89\xb4\xb9\x7f\xd5\x86\xe3\x97\xf9\x70\x23\xc3\x43\x01\xdf\x39\xb4\x0e\x8e\x59\x2d\x4e\x3f\x49\x63\x16\x4f\xdf\xa8\xe3\xc0\xc6\x31\x14\xc8\x44\x86\x2d\x1a\xba\x12\x01\x04\x9c\xc8\x81\x09\x76\x1d\x7b\x4d\x23\x9b\x36\xe5\x68\x74\xe2\x5a\x13\xf9\x86\x63\xc1\x2e\xf7\xad\xaa\x97\x98\x93\x83\xff\x03\x01\xe6\xa7\x62\xdd\xe0\xf5\x4a\x33\x1f\x70\x4c\xc3\x2a\xb6\xf4\xb4\x28\xd8\x17\x01\x99\xde\x59\x92\x99\xda\x8f\xa4\xfa\x98\xc8\xc1\x09\x27\xd2\x84\x9e\xda\x9d\xfc\x17\xcd\x66\xc0\xf5\x39\xcd\x65\x51\xf7\x6e\x3a\x44\xb1\xd3\x27\x7c\x51\x66\xfa\x0a\xd3\x98\x05\xc7\x7f\xdc\x78\x37\xe0\xd0\xe7\x11\xe5\xf3\x7d\x7d\xc6\xb6\x2d\x1e\xe3\x78\x16\x0e\x46\x72\x8c\x63\x9e\xc2\x8e\x6b\xf3\xe0\x42\x32\xb3\xa7\x9d\x06\xfb\xbd\x8c\xa6\x26\x1c\xfa\x5d\xba\x25\xf5\xc2\xc9\xca\xea\xaf\x03\x01\x26\x7a\x14\x6f\xd8\x13\xc4\x0e\x6c\x60\x5d\x57\xb0\x2b\xfb\x7e\xf9\x71\x3a\xa3\x0d\x0e\x9a\xb2\x14\xdd\xf1\x2d\x9f\xaa\xaa\xfe\x06\x00\x00\xff\xff\x55\x86\xe0\x78\x7a\x02\x00\x00") func stacksSmiStackYamlBytes() ([]byte, error) { return bindataRead( @@ -327,7 +327,7 @@ func stacksSmiStackYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "stacks/smi-stack.yaml", size: 576, mode: os.FileMode(420), modTime: time.Unix(1557785965, 0)} + info := bindataFileInfo{name: "stacks/smi-stack.yaml", size: 634, mode: os.FileMode(420), modTime: time.Unix(1557785965, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -352,7 +352,7 @@ func stacksTektonStackSh() (*asset, error) { return a, nil } -var _stacksTektonStackYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5c\xeb\x73\xdc\x38\x72\xff\xee\xbf\x02\x35\xf9\x90\xbb\x2a\xcf\x88\xef\xc7\x54\x2e\x39\xaf\xad\x75\x29\x59\xcb\x2a\x4b\x7b\xc9\xd5\xd5\xd5\x5e\x03\x68\x72\xb0\x22\x01\x06\x00\x65\x6b\x2f\xf7\xbf\xa7\xf8\x9a\x21\x39\x33\xb2\xa4\xd5\x7a\xbd\x3e\xcf\x07\x97\x00\x34\x1a\xbf\x7e\x01\x8d\x26\xe9\xeb\x9a\xa2\x96\x68\xd1\xac\x9f\x11\x52\x82\x14\x19\x1a\xbb\x26\xff\xf7\x8c\x10\x42\xa0\x12\x7f\x42\x6d\x84\x92\x6b\x72\xe3\xb6\x5d\xd7\x42\xf2\x35\x39\x87\x12\x4d\x05\x0c\xdb\xbe\x12\x2d\x70\xb0\xb0\x6e\x5b\x84\x48\x28\x71\x4d\x2c\x5e\x5b\x25\x97\x95\xa8\xb0\x10\x12\x4d\x3b\xd8\xfe\xb3\x5c\x2e\xf7\xd8\x57\xaa\x10\xec\xf6\xe4\xc6\xa5\x68\x61\xbc\xd4\x85\xe2\x97\xc8\x6a\x2d\xec\xed\x45\x4b\xf3\xd0\x25\x4d\x85\x6c\x20\x83\xa2\x50\xef\x2f\xb4\xb8\x11\x05\xe6\x78\x6a\x18\x14\x60\xdb\xf5\x33\x28\x0c\xf6\x54\x99\x79\xad\x55\x5d\x0d\x93\x08\xd1\x20\xf3\x4e\x43\xdd\x6f\x49\x4a\xf8\xb0\x26\x51\x18\xfa\xe1\xb6\x93\x90\x52\xc8\x35\x71\x77\xb3\xea\x02\xd7\xe4\x4d\x6d\xec\xbb\x5a\xbe\x30\x7d\xff\x46\x19\x7b\x76\xf1\x72\xba\x62\xd3\x79\x8e\xf6\xbd\xd2\xd7\xfb\x03\x17\x67\xaf\xa6\x9d\xd5\x20\x00\x9f\xf6\xeb\x66\x99\xef\x0d\xea\xf5\x0c\x43\xbb\xfe\x0b\x79\xdb\x77\x1b\xfc\x4e\xc8\xfa\xc3\xc7\xa8\xea\xaa\x2a\xb0\x44\x69\xa1\x68\xf5\x61\x7e\x19\x85\xdc\xa8\xa2\x2e\x77\xcc\x96\x04\xcb\xca\xde\xbe\x12\x7a\xdb\xc1\x94\xcc\x44\xfe\x06\xaa\x6d\x8f\x41\xa6\xd1\xde\xed\x51\x9a\x02\x5b\x41\x6d\x37\x4a\x8b\x9f\x5a\x2b\xaf\xae\x13\xb3\x12\xea\x64\xe2\xca\x2f\x8b\xda\x58\xd4\xef\x54\xf1\x10\x67\x5e\x02\x2f\x85\x7c\x36\x48\xd5\xa3\x5f\x36\x00\xa6\xba\x5a\x92\xc5\x62\x30\x0f\x1a\x55\x6b\x36\x16\xb5\x52\xdc\x4c\x1a\x27\x85\xca\xb7\x1d\x72\x88\x32\x33\x13\x7c\xd7\xc6\x1b\x94\x76\x3c\xac\x6f\x04\x43\x60\x4c\xd5\xe3\xfe\x4e\x83\x25\x54\xa3\xd5\x1a\x3d\x19\x8b\xd2\x76\x06\x60\x05\x88\x72\x6b\x13\xd4\x74\x04\x33\xef\x75\xdd\xfc\x5d\x08\xb3\x6b\x30\x8d\x60\x71\xdb\xac\x2b\x3e\x6e\x72\x2c\x70\xd4\xac\xc0\xb2\xcd\xb6\xf5\x7e\xdb\x3a\xa8\x35\xa8\xb6\x58\x0f\xe8\x8d\x63\x55\xa8\xdb\x72\x24\xfb\x6f\x08\xf1\x49\x26\x24\x14\xe2\x27\xd4\x9f\x11\x78\x5e\x0a\xd3\xc4\x8d\xc6\x5c\x18\xab\xc7\xf1\x72\x5c\xa8\xb2\xb6\x60\x85\xcc\xdf\x23\xdd\x28\x75\xdd\x79\x59\xdd\x4d\xfe\x7c\x64\xeb\x62\x77\xc5\xf1\xe6\xb8\x24\x16\xcc\xf5\x28\x5c\xba\x3d\x61\xda\xd9\xb4\x74\x2d\x47\x11\x34\x39\x66\xc6\x3d\x07\xc9\xb6\xab\x8e\xa3\x92\x8b\xdf\xa6\xae\x1a\x09\xf7\xfd\x78\xaa\x81\xcf\xd1\xcf\xef\xed\x0b\x27\xc6\x82\xad\x0f\xbb\xc4\x7c\x6c\xab\x90\x59\xff\xd6\x41\x8e\x0d\xdc\x35\x69\x0b\x6c\x4a\xf0\xeb\xab\xb0\xda\x65\x61\x3b\xf5\xb5\x09\xe1\x5c\xcd\xcb\x79\x84\x1c\x39\x01\xfb\xf4\xae\x65\x2c\xf0\x88\xa4\x75\x9f\xe6\x1c\x3d\xef\x27\xa7\xfa\x65\x77\x10\xbe\xe8\x0e\xc2\x87\x1c\xec\x4c\x49\xab\x55\x51\xa0\x1e\xd1\xb5\xa7\xf0\x43\x53\xda\xbb\x12\x90\x79\x96\x3b\xca\x42\xbe\x11\x92\x0b\x99\x3f\x0e\xf3\x38\x2f\x51\x05\xbe\xc3\x6c\x9b\xfa\xf6\x96\xbc\x03\x57\x4f\x79\x38\x2f\xba\x47\x26\x64\x6a\xfa\x23\x32\xbb\x4d\x86\x8e\x9a\xe2\x13\x28\x1f\x2a\x81\x1f\x2c\xca\xa6\x65\xee\xd0\x7b\x6d\xac\x2a\xdf\xf5\x7e\xf9\x0a\x33\x21\xdb\x1d\xf9\x0e\xed\x8f\x77\x82\xd5\x6c\x3f\x19\xdf\x35\xf2\x4e\xdb\x7b\x3b\x8e\x1c\x87\x0a\x21\x0c\x2c\xe6\x4a\x8b\x69\x3a\x0d\x45\x31\x6a\x1d\x09\xa7\x99\xa9\xae\xc0\x5c\x6f\x47\xaa\xa2\xd6\x50\xac\x0f\x1d\x65\x86\xa9\x0a\xb7\xb3\x86\xce\x9a\xee\x45\x27\x21\xdd\xd6\xb3\x26\x7f\xff\xc7\x2e\x28\x87\x68\x83\xa2\xda\xf4\xba\xfc\x35\xac\xb0\x3d\x3b\x3f\x13\x1b\x0c\x78\xf6\x2d\x30\x3f\xe5\x7b\xfd\x6f\xef\xd0\xfc\x37\x67\x82\x02\x28\x16\x23\x8c\xd7\x12\xac\xb8\xc1\x46\xc1\x27\x4c\xf3\xa5\x90\xc6\x42\x51\xac\xc9\xc2\xea\x1a\x17\x13\xc3\x89\x12\x72\x34\x2b\x06\x6c\x23\x64\xbe\x12\xd2\xa2\x96\x50\xac\x46\x3c\x8e\x99\xf1\xa3\x73\xee\x69\xd8\x7e\xda\x72\xe0\x33\x1a\xea\x97\x98\x19\xf7\xac\xc1\xbc\x67\xd8\x4e\x92\x9d\xa1\x36\x4a\xdb\xf3\xe9\xfa\x4b\x22\xca\x1d\x33\x23\x64\x5e\x17\xa0\xfb\xa9\x5f\x8c\x3b\x74\x86\xdd\x46\xc6\xe7\x11\x90\x17\x7d\xff\x9e\xd9\xe6\x13\xbe\x34\xfd\x37\x49\xe5\x5d\x26\x00\xde\x6d\x47\x50\x5c\xe8\x36\x02\x5e\x36\xf7\x7f\x39\x4a\xb5\xfe\xf3\xf2\xed\xf9\x05\xd8\xcd\x9a\xac\x3a\x71\x57\xbb\x3d\xec\x2f\xff\xf1\xbb\x3f\xae\xec\x6d\x85\x7f\xf8\xc3\xe2\xb2\x66\x0c\x91\x23\x5f\xfc\xfe\xaf\xab\x49\x96\x3a\x60\xda\x52\x6c\xfb\x9b\xa9\x6b\x62\xac\xde\x45\xd9\x23\x17\xd4\x08\x66\xb4\xdd\x76\x0b\xbe\x9b\x76\xde\x73\x35\x63\x41\xdb\x2b\x51\xe2\x1c\xfe\x5e\x7f\xc7\x70\x92\x4b\x1f\x02\x5f\x56\x05\x36\xe8\x0f\xf0\x7c\x79\x78\x70\x8f\xf1\xa7\x8f\x94\x77\xf5\xfe\xe1\x75\xe0\x46\x7b\x6c\xa7\xab\xf4\xa4\xf1\xc5\x86\xd7\x00\xfa\xf3\xda\xe6\x06\x59\x8e\x5b\x70\x56\x7e\xf8\x52\xec\xf2\x19\xe5\xe1\x07\x13\xf0\x03\x99\xf7\x17\xa1\xf3\xaf\xc7\xcc\xd7\x63\xe6\xc1\xd1\x71\xe8\x88\x99\xd5\x55\x8f\x1d\x2f\x56\x4f\x1a\xbf\x4e\x48\x1d\xaa\x31\x1d\x8c\x93\xf9\x0d\x09\xaa\xea\xbe\xe5\x8e\xa7\x28\x8b\x8c\x03\xb1\x52\xda\x8e\xa2\xae\x5b\xa2\x44\xab\x05\xdb\xe9\xbc\x21\x5a\x93\xd4\x49\x9d\x5d\x97\x56\x56\x31\x55\xac\xc9\xd5\xcb\x8b\x9d\x03\x81\xce\xd1\x5e\xcc\xc9\x0d\x16\xc8\xac\xd2\x0f\x94\xf8\x53\x29\xba\x7f\x40\x71\xb7\x96\xf7\x89\x1e\xa9\xe2\x4e\x99\x41\xe0\x1f\xd4\x5a\xb2\x1b\xb8\xaf\xd6\xc6\xc8\x8e\xaa\xec\x89\x9e\x77\xce\x15\x7a\x9c\x2d\xe4\xb9\xc6\x1c\x2c\x2e\xad\xea\xaa\x80\xb3\x0b\xff\xbd\x27\x23\x17\xf6\x60\xb1\xa0\xd7\xc4\x8e\xb8\xa1\x6c\x29\x3e\xf6\xd4\xf5\xe1\x0f\x7d\x3e\xfd\xf3\x9d\x59\x8d\x7e\x56\x94\xef\x9a\xac\x09\x16\x36\x2a\x2c\xdd\x51\xf3\x9f\x96\xf1\x67\x25\xff\x5d\x55\xff\x33\xf5\xa0\x1b\x81\xef\xef\xe7\x04\x0d\xe5\x17\xe3\x04\x47\xad\x79\x0f\x83\xf5\x66\x69\x95\x4f\x64\xdd\x9f\xc6\xdb\x82\xe4\xe8\xa5\x89\xa3\x45\xd4\x4c\xe4\x4b\xd0\x56\x64\xc0\xec\x92\xd6\xec\x1a\xed\x3d\x37\xc0\x4f\x86\xaa\xba\x61\x4f\x09\xa9\xe7\xf5\x03\x7e\x80\x26\x45\x1a\xde\x75\x6a\x7e\xff\xf2\x91\xdf\x8e\x90\xdc\xf9\x9b\x11\x9e\xfe\xcf\x8b\x37\x17\xdf\x9d\x92\x97\x6f\xcf\xbf\x3d\x7b\xfd\xfd\xbb\x17\x57\x67\x6f\xcf\x0f\x11\xde\x8b\xe3\x7d\x30\x8e\xd8\x5e\x6d\x84\x21\xb4\x50\xec\x9a\x08\x43\xa4\xb2\x04\x98\xad\xa1\x28\x6e\x49\x56\x4b\xd6\xa5\xe8\x64\xf2\xe8\xfe\xf9\x68\x3a\xad\x6d\xfb\x56\x09\x1a\x62\x15\x11\x45\x13\xfb\x1a\x2c\x12\xbb\x41\x02\x37\x20\x0a\xa0\x05\x4e\xe7\x8f\xa6\xab\xaa\x75\x7d\x02\x92\x13\xae\x58\x5d\xa2\xb4\xcd\xd4\x92\x08\x49\x80\xbc\x87\x5b\x62\x37\x60\x1b\x68\xc0\x18\x1a\x23\x68\x81\xa3\xf9\x56\x91\xda\xa0\x36\x1d\xd5\xdf\xae\x6b\x8a\xcc\x16\xa4\x39\x06\xfe\x46\x6c\x23\x5a\xb7\x32\x29\xa1\x5a\x3d\x3b\xa4\xff\xab\x0d\x1a\x24\xa6\xb5\xf5\x14\xe6\x16\x5c\x09\xb7\x84\x36\x83\x95\x40\x4e\x54\x6d\x89\xca\xc6\x18\x9a\x65\x7a\x6f\xe9\x35\xd9\x88\x53\x4b\x21\x39\x4a\x8b\xbc\x41\x49\xb1\x91\xa8\x51\x4a\xe3\x64\x1d\xd9\x54\x8e\xad\xda\xd9\x06\x64\xde\x29\x70\x82\x67\x35\x37\x1d\xc7\x0c\xea\xc2\x2e\xad\x28\x51\xd5\x76\x59\x0a\x59\x5b\x6c\x45\xb6\x20\xa4\xe9\x96\xeb\x88\x88\xac\x4b\x8a\x7a\x8a\x7c\x98\xd0\x69\x91\x64\x4a\x0f\x59\x78\x2b\xc1\xa8\xf0\xf3\x9c\x88\x8c\x48\x25\xb1\xb1\x44\x93\xda\x88\x4c\x20\xdf\x69\xf4\x08\x94\x35\x59\x44\xce\xa2\x59\x2a\x72\x86\xd5\x8e\x49\xd1\xbf\x9b\xb4\xec\x5f\x4e\x3a\x2c\x45\x4f\x44\x06\xa2\x26\xe2\xf7\xdc\xe1\x29\x04\x99\xa1\x59\x93\x45\x3f\xb2\x78\xdc\x6e\xd5\xcf\x36\xbf\xc0\x4e\x55\xa8\xbc\xc0\x1b\x2c\x56\xbb\xe4\x79\x4d\x84\xcc\xd4\x7c\xbc\x4f\x13\x27\x83\x3f\x41\xb5\x2c\x54\x9e\xa3\x5e\x76\x48\xc7\xfb\xdd\xdf\x9f\xed\x76\x98\x45\xcb\x63\xb1\x26\x8b\x66\xf6\xe2\xf9\x78\x88\x37\x43\xaa\x6a\x82\x77\xd1\xbf\x72\x38\x19\x6f\xa3\x4b\xc8\x7c\xb1\x9e\xb0\x24\x0d\x2f\x61\x05\x34\x6c\x5d\xc7\x79\x3e\x1d\xb3\x1b\xd4\x08\x99\x45\xdd\x0d\x8f\x46\xff\x31\x61\xaf\x6a\x5b\xd5\xb6\xb9\xfc\x9a\xc5\x9a\xfc\x65\x61\x2c\x57\xb5\x5d\xfc\x75\x42\x84\x5a\x2b\xfd\x76\x9f\x12\xb5\x9e\x53\x4a\xa6\x78\x87\x76\xf1\xa3\x51\x72\xb1\x3f\x8a\xba\x33\xfd\xbe\x40\x4d\x00\xfc\x17\xde\x36\x73\x17\x33\x79\x5a\x05\xf6\x63\x9d\x32\x67\x04\x8d\x53\x0c\xe3\xad\x49\xe6\x04\x0c\x1a\xe3\xf6\x24\x5d\x63\x4e\x52\xa2\x31\x90\x0f\x6c\x4a\x93\xcf\x09\x8c\x05\x76\x6d\x35\xb0\x81\x66\xd7\xb1\x07\x58\x48\x3c\x95\x83\x2a\x0e\x8a\x73\xda\x69\xe3\xd0\x78\xa3\x89\x3b\x86\x79\xbf\xab\xdd\x41\xd2\x49\x38\x26\x18\xbb\xc0\xb3\xe9\x5f\x0f\x8e\xc8\x46\xc5\xbb\x7a\xcc\xd7\xd4\xe1\x6b\xea\xf0\x4f\x94\x3a\xf4\xf5\x9d\x15\x05\x76\x8d\x92\x2f\x39\x1a\x2b\x64\x87\x3d\x13\x58\xf0\xed\xd9\xd8\x9d\xbf\xe6\xd6\x58\x2c\x87\x69\x64\x44\x3e\x52\x0d\x39\xb3\xed\x5b\xe1\x4a\x5b\x43\x50\x34\xfb\x37\xa9\xb4\x2a\xd1\x6e\xb0\x36\xe4\x77\xa3\x83\xfc\xf7\x44\x69\xd2\xee\x3c\x5c\x8b\x1b\xd4\x63\x2e\xe7\xca\xe2\x9a\x7c\x6f\x84\xcc\xc9\xe5\x8e\x84\xbc\x17\x45\x41\x84\x64\xb5\x1e\x15\x8f\x1b\x91\x75\x8e\x66\xc7\xe0\x0e\xd1\xd6\x23\x38\xc7\x34\x32\x02\xb5\xac\xb4\xfa\x11\x99\x5d\x0a\x7e\x50\x29\x63\x70\x3d\x29\x39\x7b\xb5\x6a\xe3\x62\xc4\xb9\x9b\x2a\x4c\xef\x14\x50\xac\xc8\x7f\x6f\x50\x12\x5d\x4b\xd9\xc8\xa8\x24\x79\xfd\xf2\xf4\x39\x81\xaa\x2a\x04\xeb\x6c\x30\xe4\x3b\x4c\x63\xe3\x08\x02\x0a\xd3\xc9\x4f\xc7\x2e\x5c\x1b\xe4\xad\xc3\x0c\x76\xe9\x49\x88\x69\x63\x41\x75\x8e\xd0\x5d\xc7\xff\xd5\x6c\x31\x8a\xac\x73\xbc\x01\xd8\x88\x63\x13\xc4\x95\x56\x37\x82\x8f\x93\xa2\xbb\x75\xb3\x26\x8b\x7f\xbb\x55\xf5\xc4\x9e\xbb\xb5\xf8\xbf\x2f\x8e\xa9\xba\xfd\xda\x62\x39\x66\xca\xda\x67\x0d\xcb\x41\x1c\x21\x79\xa3\x10\x34\xe4\xfd\x06\x5b\x77\x12\x5d\x3c\x37\x13\x47\xd5\xfc\x36\x08\x0c\x8e\x14\x61\xd5\xc4\x3a\x75\xeb\x4c\x8b\xbc\x50\x14\x8a\xc5\xf6\x9a\xdf\x96\xbb\x5b\x05\x76\x0b\xef\x61\x6c\x09\x56\xe4\x12\xad\x6d\x18\x74\x5a\x2b\x20\x6f\xf8\x77\x95\x08\xc2\x54\x5d\x70\xc2\xa0\xc9\x3b\xf1\x83\xd5\x30\x5e\x78\xc4\xb0\xf3\xd3\x15\x21\x67\xd9\x9d\xd1\xd7\xef\xa4\x23\x26\xcf\xbb\x75\x27\x76\x12\xb9\x54\xfa\x90\x89\x3e\xa6\xd3\x35\x59\xb4\xf9\xd9\x23\xd3\x58\x45\x9b\x1d\x1b\xa8\x28\x84\xbd\xfd\xb9\x47\x27\x54\x95\x99\x96\x8e\x5e\x6d\xdf\x82\x3f\x08\xe5\x40\x31\x77\xb5\xfb\x42\x69\x25\xd4\x09\x53\x65\xa5\x24\x36\x09\xfb\x5e\x51\xfc\x10\xfd\x1d\xdf\x05\xfd\x32\xd5\x76\x8d\x6d\x90\x9b\xdd\x77\x2f\xfb\x45\xde\x12\x2c\xdb\x7c\x37\x93\xf5\xde\xcf\x08\x2c\x96\x55\x01\x16\xd7\x63\xdf\x98\xa8\xb1\x65\x26\xa5\xb2\xdd\x8b\xf8\xeb\x49\xd6\xd5\x6f\x17\x4b\xa8\xad\x32\x0c\x0a\xd4\x33\x95\x19\xc8\xba\xa2\xec\x8d\x60\x76\xea\x4e\x87\xad\xf4\x00\xec\x8f\x33\xeb\x23\x4c\x3b\x35\x4a\x2b\x77\x77\xdd\x44\x3d\x41\xbe\x24\xa0\xf3\x99\x2c\x4b\xd2\xe4\x8e\x56\x75\x97\x87\xf9\x50\xd7\x6b\x37\x1a\xcd\x46\x15\x7c\x36\x7c\x76\xfe\xed\xdb\xf9\x8c\x36\xf3\xe8\xc2\xeb\xbd\x16\x8d\xee\xc5\xe4\x7d\xb2\x8e\x2c\x67\xba\x91\xaa\x97\x47\x63\x81\x60\xd0\x9c\xe4\xc2\x6e\x6a\xba\x62\xaa\xec\x87\x18\x3f\x19\x84\x3d\x61\x25\x3f\xd9\x71\xef\x98\xff\xd1\x6c\xc0\x0b\xa3\x75\xea\x7a\xdc\x77\x7c\x3f\xc0\xc8\x4f\xd2\x34\xf3\x93\x38\x4c\x9c\x88\x3a\x91\xef\xd3\x90\xf3\xcc\x0f\x62\x87\x47\x41\x46\xb9\xe7\xfb\x7e\xc6\x3c\x16\xfa\x01\x64\x2c\x83\x34\x89\xbd\xb9\x60\xcb\xe6\xa4\x32\x4f\x8a\xbc\xe7\x28\x85\x1d\x30\x27\x59\xc2\x02\x1f\x5c\x37\x4c\x93\x00\x53\x87\x53\x9f\x3a\x8c\xfa\x19\xe3\x41\x84\x6e\x94\xa6\xc8\xdc\x30\x74\x43\x06\x31\xf7\xc2\x24\x8c\x5d\x08\x02\x16\xf3\x38\x72\x02\x87\xc1\x1c\x73\x2e\xec\x93\x22\x6e\xf9\x8d\xf0\x3a\x4e\x10\x45\x98\x20\x8b\x79\x02\x5e\x92\xba\x81\x93\xa4\x7e\xe8\xf9\xdc\xf7\xbd\xc8\xa5\x10\x3a\x4e\xc4\x33\xea\x52\x9e\xd2\x34\x02\x44\x2f\xf6\xa3\x8c\xc5\x7e\xca\x29\x84\x7b\x78\xa5\xaa\x9e\x14\xaf\x54\xd5\x00\x95\xc6\x71\x1a\x86\x14\xbc\xd8\x75\xd1\xa5\xe0\x3b\x40\x83\x24\x8a\x1d\xca\x20\xe3\xb1\x17\x72\xce\x1c\x17\x5c\x27\xe4\x6e\xec\x7b\x61\x84\x6e\x98\x38\xa1\x9f\xba\x01\x67\x81\xc7\xe6\x50\x29\x98\xcd\x52\xaa\x27\x06\xdc\x70\x1d\x10\x43\x1a\xd1\x30\x09\x1c\xc6\x91\x7a\x10\x85\x69\x02\x49\x18\x45\x90\x44\x4e\x4c\x53\x2f\x8c\x7d\xcf\x4b\x22\x48\x32\x9e\xb9\xa1\x1b\xc4\x14\xfd\x30\x75\x69\xcc\xe2\x8c\x06\x6e\x16\xef\x39\x83\xa9\xad\x28\x9e\xd6\x1f\x5a\x96\x5b\x6f\x70\x7d\x07\x59\x18\x79\x34\x49\x63\x16\xa6\x5e\xea\x7a\x3e\x0f\xd0\x0d\x18\xf7\xbd\xd8\x65\x3c\x4c\x5c\xc7\xcb\xdc\x2c\x70\xdd\x2c\xf4\x18\x8d\xb2\xc0\x0d\x9d\x24\xa1\x59\x98\x06\xc1\x1c\x30\x4a\xab\x6f\x2b\x25\xe4\xd3\x3a\xf1\x8e\xed\x00\x3c\x64\x1e\xc4\x5e\xe4\xf2\xd4\xf3\x69\x02\x99\x97\x02\xf7\x29\xfa\x01\xa4\x2c\x05\x1f\x28\x77\x91\xbb\x2e\x38\xbe\xc3\xc0\x65\xcc\x73\x62\x2f\xf5\xb9\xe7\xf8\x71\x18\x02\xdd\x03\xde\xa2\xe5\x22\x47\x63\x97\xf8\xa1\xb9\x2e\x3c\xf1\x9e\x37\x5a\x60\xe0\x3f\xc8\xe2\xf9\xe8\x71\x8c\x12\x96\x44\x1c\x83\x34\x00\x0a\x69\xc2\x81\x66\x8e\x47\xdd\x38\xc4\x8c\x39\xa1\x9b\x78\x31\x0b\x3d\x3f\x74\x28\xe7\x29\x24\x69\x16\x81\xcf\xd3\x28\x45\x48\xe7\xb2\x54\x4f\x8b\xbc\xaa\x8b\x42\xe3\xff\xd6\x8d\x6a\xc6\x3b\x89\x17\x81\x9b\xb8\x90\xa4\xcc\x71\x80\x26\x81\x13\xa6\x69\xe8\x24\x98\x36\xf1\xc8\x32\x44\x1e\x72\xea\xd1\x00\x21\x70\x33\xca\x58\xe4\x33\xcf\x4b\xe3\xd4\x4f\x52\x0c\x20\x88\xf6\xc2\xb3\x16\x05\x5f\xe6\xcc\x2c\x33\xb4\x6c\xf3\x84\xfa\xbf\x41\xc9\x95\x1e\xd3\xe4\x4a\xe5\x05\xb2\x42\xd5\xbc\x49\x4c\x32\xa5\xcb\x93\xb6\xd5\xa1\x40\x6d\x4e\x46\x40\xba\xc0\xd9\xb5\xb7\x4e\x48\xd1\x43\x37\x40\x1e\xd1\x34\x89\xdc\x34\xa1\xc8\xc0\x73\x21\xf5\x21\xf3\x03\x4c\x9c\x38\x4c\x22\xce\xb2\xd4\x6d\xb6\xb1\x66\x30\xce\xc2\x34\x76\xc1\xcb\xc0\xf1\x5d\x77\x22\x1a\xca\x9b\xf9\x99\xde\xbf\x6a\xf3\xe7\xcb\xab\xd3\x37\x3f\x9c\xbf\x78\x73\x7a\x79\xf1\xe2\xe5\xe9\x84\x88\x90\x1b\x28\x6a\xfc\x56\xab\x72\x3d\x1b\x20\xdd\x65\x6a\xf4\x3d\xcd\xde\x58\xf7\x72\xce\x90\x8b\xad\xe4\xe4\x13\xf9\x39\x8e\xae\x9e\xf3\xc3\x77\x6f\x5f\xbf\x3e\x3b\x7f\xdd\xe2\x39\x04\xe5\x48\x31\xeb\x20\xaf\xb7\xdf\x5c\x9e\xbe\xfb\xd3\x8b\x6f\xce\xbe\x3b\xbb\xfa\xf3\xc7\x39\x1e\xca\xf4\xa7\x7c\xdf\x9c\x5e\xbd\x3b\x7b\x79\xf9\xc3\xab\xb7\x6f\x5e\x9c\x9d\x1f\x66\xb6\x7b\xc2\xba\xf5\x90\x09\x61\xeb\x75\xeb\xc7\xa7\x08\xdb\x64\x70\x70\x93\xd8\x03\x8f\x72\xf0\x5c\x1a\x52\xe6\xf9\x61\xe8\x60\x1a\x64\x3c\x8b\x19\x62\x02\x11\x86\x94\x27\x5e\xe4\xb8\x59\xc8\x13\x17\x22\xcf\x8f\x33\xe6\xd1\x84\x05\x9e\xef\xb9\x10\x4e\xe3\xfb\xbe\x17\x80\x5e\xe4\xf6\x3b\xe9\x37\xed\x97\xd5\x73\xef\x2a\x9b\xde\xce\x03\x4e\xd0\xb2\x93\x3b\xcc\x76\x67\x99\x92\xb4\x17\x86\xf1\x07\x53\xe7\x0f\xc0\x38\xfb\x94\x9e\x0c\x0f\xa0\xbb\xdb\xdf\xdc\x73\x3f\x02\xe3\x0e\x82\x5f\xe9\xc6\xd7\x3f\xdf\x38\x24\xfb\xd3\xdc\xfc\x1e\xff\x06\xd0\x93\x5f\xfb\xa6\x50\x7e\x43\x77\xbe\x29\xf0\x9f\x67\xd5\x47\x58\xf6\xfe\x17\xbf\xcf\xf6\x8c\xf8\x99\x7b\x66\xaf\xcf\x61\xc3\x74\x79\xe4\xfb\x51\xe6\xc5\x41\xc2\x68\x82\x21\x73\x53\xea\xc6\x6e\xea\x42\x18\xb0\x08\x38\xa5\x2c\x8e\xd1\xe3\x2e\x8b\x9c\xc4\x4d\xb2\xd4\xf7\x12\x0f\x59\x90\x78\x94\x7a\x69\x18\x1f\xd8\x11\x0e\x99\xf8\xeb\xe6\xb8\xb7\x39\x3e\x1b\x47\x6d\xae\xae\x86\x56\xff\x7f\xb6\xfc\x7f\x00\x00\x00\xff\xff\x75\xe8\xd9\x72\x7b\x47\x00\x00") +var _stacksTektonStackYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x6b\x73\x23\x37\x92\xe0\x77\xff\x0a\x04\x75\x11\x3d\x13\x21\x52\x64\x91\x2c\x3e\xf6\xf6\x6e\x65\xb5\xdc\xa3\x9b\x6e\xb5\x42\x92\xed\x9b\x98\x75\x78\x50\xa8\x2c\x12\xa3\x22\x50\x07\xa0\xa8\xa6\x77\xf6\xbf\x5f\xe0\x51\xef\x12\x45\xa9\xd5\xb6\xdc\xae\xfe\xd0\x21\x16\x80\x44\x22\x5f\xc8\x4c\xbc\xee\xd2\x00\x04\x03\x05\x72\xf9\x0d\x42\x1b\xcc\x68\x04\x52\x2d\xd1\xbf\xbe\x41\x08\xa1\x23\x74\xc6\x93\x9d\xa0\xab\xb5\x42\xde\x70\xb4\x40\xb7\x6b\x40\xb7\x70\xa7\x38\x43\xa7\xa9\x5a\x73\x21\x6d\x3d\x57\xfb\x3d\x25\xc0\x24\x84\x28\x65\x21\x08\xa4\xd6\x80\x4e\x13\x4c\xd6\x90\x95\x1c\xa3\x1f\x40\x48\xca\x19\xf2\x06\x43\xf4\x27\x5d\xa1\xe7\x8a\x7a\x7f\xfe\x37\x07\x65\xc7\x53\xb4\xc1\x3b\xc4\xb8\x42\xa9\x04\xa4\xd6\x54\xa2\x88\xc6\x80\xe0\x13\x81\x44\x21\xca\x10\xe1\x9b\x24\xa6\x98\x11\x40\xf7\x54\xad\x4d\x57\x0e\xd0\xc0\x81\xf9\x9b\x03\xc3\x03\x85\x29\x43\x18\x11\x9e\xec\x10\x8f\xca\x75\x11\x56\x95\x01\xe8\x7f\x6b\xa5\x92\xe5\xc9\xc9\xfd\xfd\xfd\x00\x1b\xe4\x07\x5c\xac\x4e\x62\xdb\x40\x9e\xbc\xbf\x38\x3b\xbf\xbc\x39\xef\x7b\x83\x61\xa5\xe9\xf7\x2c\x06\x29\x91\x80\xff\x97\x52\x01\x21\x0a\x76\x08\x27\x49\x4c\x09\x0e\x62\x40\x31\xbe\x47\x5c\x20\xbc\x12\x00\x21\x52\x5c\x8f\xe1\x5e\x50\x45\xd9\xea\x18\x49\x1e\xa9\x7b\x2c\xc0\x41\x0a\xa9\x54\x82\x06\xa9\xaa\x10\x32\xc3\x98\xca\x4a\x05\xce\x10\x66\xa8\x77\x7a\x83\x2e\x6e\x7a\xe8\xdb\xd3\x9b\x8b\x9b\x63\x07\xe7\xc7\x8b\xdb\xbf\x7c\xfc\xfe\x16\xfd\x78\x7a\x7d\x7d\x7a\x79\x7b\x71\x7e\x83\x3e\x5e\xa3\xb3\x8f\x97\x6f\x2f\x6e\x2f\x3e\x5e\xde\xa0\x8f\xdf\xa1\xd3\xcb\xbf\xa1\xbf\x5e\x5c\xbe\x3d\x46\x40\xd5\x1a\x04\x82\x4f\x89\xd0\xe3\xe0\x02\x51\x4d\x62\x08\x33\x7a\xde\x00\x54\x10\x89\xb8\x45\x4c\x26\x40\x68\x44\x09\x8a\x31\x5b\xa5\x78\x05\x68\xc5\xb7\x20\x18\x65\x2b\x94\x80\xd8\x50\xa9\x19\x2e\x11\x66\xa1\x83\x14\xd3\x0d\x55\x58\x99\xaf\x8d\x01\xda\xee\xcc\x7f\x38\xa1\x4e\x5c\x96\x68\x3b\x32\x9f\xee\x28\x0b\x97\xe8\x12\x6f\x40\x26\x98\x58\x8a\x6d\x40\xe1\x10\x2b\xbc\x34\xbf\x10\x62\x78\x03\x4b\xa4\x8c\x98\xf6\x13\x9a\x40\x4c\x19\xc8\x02\x6c\xbf\xdf\xef\xa4\xbb\x93\xee\xd7\x24\xdd\x09\x8f\x29\xd9\x9d\x6c\x47\x01\x28\x5c\x96\xf4\x2b\x1e\xde\x00\x49\x05\x55\xbb\x2b\x53\xe7\xa9\x12\xaf\xf1\xcf\xaa\x25\x82\x6e\x69\x0c\x2b\x08\x97\x28\xc2\xb1\x04\xf7\x1d\xc7\x31\xbf\xbf\xca\x0a\xcf\x25\xc1\xb1\xc1\xbf\x5a\x6b\xcb\xe3\x74\x63\xe7\x09\xa3\x45\xe8\x0d\x6c\x12\xb5\x7b\x4b\xc5\x9b\xe2\x13\xe1\x2c\xa2\xab\x0f\x38\x29\x7d\x93\x40\x04\xa8\xec\xc3\x9a\x4b\x75\x09\xea\x9e\x8b\xbb\x2a\x7c\x5d\x70\x71\x75\xd6\xfc\x78\x75\xf1\xb6\xfa\x51\xa4\xec\x54\x7e\x2f\x41\x64\xb8\xe8\x4f\x31\x2c\xd1\x9b\x6b\x5d\x72\xca\x76\x59\x67\x12\xde\x53\x96\x7e\x7a\xbc\x5e\x9a\x24\x31\x6c\x80\x29\x1c\xbf\x13\x3c\x4d\x64\xa3\xc9\x87\x54\x2a\xd3\xec\x4d\x51\x82\xd9\x0a\x4a\x35\xfb\x68\x43\xd9\x12\x8d\xf2\x0f\x7a\x46\xfd\xb4\x44\xfe\x74\x3a\x9e\xba\x8f\x91\x34\xf0\xbf\x0c\xf8\x3d\xe6\xcd\x1b\xfe\x61\xcc\x9b\xec\xec\xdb\x6b\xb0\x6f\xd6\x80\x9d\xc5\xa9\x54\x20\xae\x79\x0c\x0d\xab\x27\x02\x4c\x06\xd8\x88\x22\xfd\xc5\x40\x1c\xdc\xcd\xe5\x80\xf2\x13\x37\xdb\x1f\x66\xe7\xfa\x84\x33\x25\x78\x1c\x83\xe8\x13\xdb\x5d\x1f\x13\x02\xd2\xca\xb7\x56\x30\xa7\x45\x7d\xdd\xbd\xd3\x6f\xf4\xf7\x5e\xef\x27\x07\xf7\xa8\xf0\x28\x90\x6d\xa9\x19\x55\x08\x03\x10\x6c\x65\x19\x50\xd1\x17\x52\x74\x03\x3c\x55\x68\x8d\x59\x18\x6b\x7a\xc5\x7c\x45\x49\x0e\x92\x2a\x10\x58\x81\x44\x9a\x9e\xda\xc6\x1a\xf4\x4d\x27\x86\x9c\xa6\xbd\x44\x1a\x02\x66\x3b\x74\xe5\x86\x73\x9d\x32\x89\xd4\x1a\x6b\xb8\x5b\xad\x35\x89\xc6\x61\x90\x83\xbd\xe2\xe1\x63\x38\x2a\x2c\xef\x44\xca\xca\xb8\xde\x63\xa6\xa4\x96\xdc\x00\x50\x9a\x84\x58\x0b\xdf\xfd\x1a\x58\x0e\x16\x1b\xc0\x86\x9f\xf1\x4e\x0f\x06\xa3\x5b\x2c\xef\xae\x35\x98\xb5\x31\x44\x48\x2a\xac\x20\x43\x44\x80\xe4\xa9\x20\x60\x08\x59\x0c\xac\x77\x8c\x7a\x09\x0f\x65\x4e\xda\x2d\x88\xc0\xd4\x89\xa9\x54\xba\xf4\x1e\x2b\xb2\x2e\x51\xfe\xac\x40\x92\x01\x84\x12\x39\x16\x66\x83\x54\xdc\xd0\xce\xa9\xfe\xd9\xf5\x5b\x47\x1d\xaa\xec\xf8\x65\xc2\x99\xa4\x5a\x4b\x23\x2e\x72\xa8\x1b\xcc\xf0\x8a\xb2\xd5\xa0\x8d\xef\x56\x80\x06\x21\x6c\x73\x3c\x2a\xc3\xd1\xf4\x33\x23\x71\xa8\xe4\xbf\x1d\x61\xed\x28\x33\xf1\x2b\xff\xa8\x17\xe6\x60\x7b\xc7\xb9\xd5\xee\x11\xce\x42\x6a\xf4\xa7\x49\xa5\x15\x18\x22\x65\xc4\x22\x02\xb0\x02\xfd\x97\x65\x9a\xfe\x2b\x84\x18\xec\x5f\x89\x21\x65\x8d\xa6\xcf\x18\xab\xc6\xfa\x24\xa2\x0c\xc7\xf4\x17\x10\x8d\x01\x95\x8b\x5e\x07\xc2\xf2\x44\xcb\x62\xda\xe0\x51\xe9\x73\x3e\xae\xe2\x53\xce\xb1\xfc\x5b\xc1\x93\xca\x78\x9b\x4d\x72\x0c\xb2\xb2\x2f\x4f\x07\xeb\x12\xb6\xd3\x40\x6b\x98\xf3\x0a\x4d\x35\x0a\xb2\x51\xd1\x58\xb4\x82\xa0\x85\xb9\x6c\xe2\x9e\x4a\xc8\x70\x70\xbe\xc3\x17\xb0\xdd\x47\xe8\x56\xbb\x02\x54\x1a\x3d\xce\x94\x5b\x6b\x72\xcd\xac\x5a\x2b\xa0\x67\x46\x3d\x01\xf5\x73\xdb\x82\x02\x2c\xa9\x1c\x1c\x3c\x15\x28\x60\x98\xa9\x27\xce\x04\x0d\x32\x67\x06\xed\x24\xe6\x2b\xfd\xb7\x75\x66\xcd\x67\xd8\x02\xb3\x7f\x49\x10\x5b\x4a\x00\x13\xc2\x53\xf7\xc9\x3a\xc2\x1b\x9c\x54\xc5\x4c\x93\x4f\x2a\x60\xca\x7a\xd3\x24\xc6\x74\x23\xad\xe0\x6c\xa8\xb2\x1e\xdf\x97\x10\x2e\xeb\xe3\x90\x18\xb0\x40\x34\xb2\x5e\x59\x31\x8b\x60\xa2\x52\x1c\xc7\xbb\x7c\x3a\x19\x20\x74\xa3\x1d\x88\x1d\xc2\x68\xcd\xe3\xb0\x6f\xe6\xb0\x48\xf0\x8d\xe1\x56\x22\x60\x4b\x79\x2a\x8b\xb9\x8e\x11\x2c\x98\x11\x83\xcc\x52\x17\x9c\x78\x23\xcb\x92\xd4\x6a\x91\x71\x92\x34\x25\xd8\x94\x84\x90\xc4\x7c\xb7\x31\x94\xfe\xf2\x4a\x77\x10\x1e\x5f\xda\x1e\x7e\x39\x1d\x7c\x40\x69\xee\x21\x58\x73\x7e\xf7\xa8\xf3\x74\x64\x42\x08\x57\xdb\xe9\xa9\x75\x29\x8c\x97\xac\x38\xd2\xc3\x36\xbe\x8d\x1d\x2d\x22\xa9\x54\x7c\x93\x11\x32\x84\x88\x32\x3b\xf3\x1d\x97\x66\x6a\xca\xe2\x9d\x6e\xec\xda\xa8\x52\x1f\x04\x84\xd2\xde\xa9\xf6\xa3\x32\xcd\xaf\x31\x8c\xc2\x27\x05\xcc\x38\xa9\x6e\xf4\xed\x0c\x7c\x10\x15\xc3\xa6\x87\x0a\x0f\xb5\xf7\x05\x77\x0f\x12\xb3\xd0\xf9\xd5\x02\x56\x3a\x1a\x28\xf3\xae\xa4\xb0\x65\x6a\x27\x20\x22\x2e\x36\x12\x61\x24\x80\x70\x46\x68\x4c\x9d\xbe\x31\x4d\x32\xed\xfa\xdd\xf3\x62\xc8\x86\x0b\x5a\x07\x29\x4b\x79\x2a\xe3\x5d\x0e\xd5\xa2\x2a\x91\xb5\x50\xa9\xeb\xbc\x8d\x64\x9b\x54\xfb\xf9\x6c\xe5\x70\xa8\x34\x30\x64\xdb\xe2\x98\x86\x7b\xaa\x14\x43\xb9\xd3\xc6\x61\x0b\xda\x87\x14\x4a\x22\xca\xf4\x60\x40\xc8\x12\xf6\x6b\xca\x56\xf2\x18\xdd\xaf\x29\x59\x6b\xa3\x74\xbf\xde\xa1\x7b\x30\x62\x86\x56\xa0\x8e\x0b\xe1\x32\x94\x1d\x1c\xe2\x60\x3e\x87\xee\x4f\x20\xc1\x4f\xd5\xb9\x2d\xab\x9c\x33\xad\xe9\xa0\x9a\xa8\xd2\xf8\xd7\x21\x44\x38\x8d\xad\x53\x6e\x35\x12\xf1\xe0\x9f\x40\x54\x61\x55\xb1\x99\x2b\x77\x08\x0b\xd0\x4c\x07\xba\x2d\x02\x81\xc6\x34\xef\xfa\x1c\x64\x6a\x3d\x68\x71\xa4\x8e\xd0\x8f\x6b\x30\x04\x17\x60\xa0\x66\xae\xbd\xc6\xc1\xda\xec\x88\xae\x4c\xf8\xe8\xe6\xb9\xe3\x9c\x73\x99\xd8\xe8\x7a\x35\xaa\xb8\x76\x79\x27\x79\x50\x9f\x45\x1a\x65\x35\x46\x2e\xf0\x14\x10\x09\x90\x6b\x08\x91\x04\xa5\x27\x0d\x63\x6a\x1a\x5c\x75\x7a\xe6\xf4\xeb\x85\xb8\x7a\xb8\xdc\x66\x35\x39\x1b\xec\x21\x70\xa1\x9d\x92\xac\x61\x83\x4b\xcd\x4c\x94\x85\x76\x3c\x3d\x36\xec\x87\x4f\x78\x93\xc4\x70\x8c\xec\xc4\x90\xc5\x58\xb2\x88\xef\x2c\x2a\x7b\x7b\x73\xd0\x1d\x33\x62\xbe\xd2\x91\x4e\x55\x9f\x11\x5e\x61\xca\xa4\xca\xd8\xf7\x46\xe6\xf5\xa4\x12\x29\x51\xa9\x80\x87\x24\xe9\xb0\x31\x17\x4e\xce\xde\x5a\x2f\x2d\x7a\x0d\xd6\xfd\xc6\xc2\xf7\x9b\xb8\xe9\x5d\x9e\xaf\xcb\xf3\xbd\xb2\x3c\xdf\xaf\x94\xe0\x2b\xd5\x33\x41\xe1\x03\xab\x1e\x4f\x8d\xf4\x4a\xd1\xda\x41\x89\xab\xcc\x17\x6e\x84\xad\x45\xd2\xca\x7a\x35\x05\x60\xc3\x89\xcc\x06\x5b\xff\xc7\x59\x6a\x16\x22\x91\x32\x45\x37\xd0\xee\x93\x7d\x26\xfe\xda\x88\x3d\x68\x75\x6c\xc3\xbe\xc3\xab\xb0\xe9\x7d\x1e\xe8\xa0\x16\x07\x34\xa6\x6a\x57\xfa\x8e\xb5\x31\xc5\x44\xf5\x83\x94\xdc\x69\xf3\x58\xc9\x6b\x55\x6a\x24\x5b\xa2\x1b\x46\x80\xf5\x6c\xd3\x8f\x62\xbc\x2a\x85\xc6\xfd\x18\x70\x08\xa2\x0f\x31\x10\x3d\xda\xd6\xe8\xe7\xd7\x08\x7b\x5e\x99\x48\x55\xc3\xab\xa7\xcb\xd3\x2b\x92\x9a\xd6\x09\xb3\x1d\x95\x2c\xad\xf2\x2c\x52\x85\x18\x36\x9c\xa1\x0d\xbe\x83\x96\xf8\x28\xe6\x3c\xd1\x06\x38\x8f\x72\x41\x28\x39\x30\xee\x08\x6c\xa1\x48\x19\x1b\x2b\x69\xd0\xc8\x7d\x13\xaa\x2a\xbe\x47\xd5\xe3\xc8\xbc\xc5\x62\x6e\x2b\x7b\x1a\x39\x58\xa9\xb8\x9e\x68\x28\x2b\x75\xf0\x04\x1e\x3d\x48\x98\x36\xdf\xe4\xe1\xb0\xc0\x8e\xfa\x71\xf7\xa1\xdb\x05\xd1\x79\x0f\xbf\x96\xf7\xf0\xe0\xf6\x9e\x1b\x9b\x4e\x3d\xb5\xe9\xd4\x2f\xea\x28\x64\x5a\xf0\x05\x70\x79\xda\xf4\xd2\xe9\x64\xa7\x93\xaf\x40\x27\x9f\xe4\x6c\xd5\x37\x2b\x95\x72\xd5\xdf\x52\x16\x52\xb6\x7a\x9e\xee\xb6\x25\xa2\x65\x6a\x93\x63\x99\x07\xf6\xa0\x76\xbe\x60\x10\xc1\x63\xb8\x86\x28\x43\xbb\x3d\x1f\xff\xcc\xe1\x14\x73\xff\x1e\x12\xd7\x6c\xc2\x85\x5b\xb7\x69\x92\xd9\x26\x1a\x93\x18\x13\x08\xad\x5e\x62\x54\xe7\x82\x71\x70\xac\x13\x52\x02\x80\xee\x79\x1a\x87\x28\x80\x82\x1a\xe1\xc0\x98\x1b\xe7\x79\x86\x20\x89\xa0\x81\x51\xac\x02\xce\xe1\x0b\x6f\xcd\xce\x1c\x94\x00\x90\x24\x3c\xc9\xb4\x32\xa4\x5b\x1a\xa6\x38\x46\xb6\x75\x69\xaf\x44\x73\xb2\xf8\x4d\x64\xb2\xb9\x9c\xf8\x7b\x16\xc9\xe6\x68\x9e\x2e\x91\xbf\x0d\x4b\xf6\x2c\x56\x7d\x3e\x43\x9e\x18\x12\x7e\x16\x37\xf6\x0c\xe4\x70\x5e\xec\x71\x1b\xfe\x40\x99\xc0\xce\x6d\xf8\x3d\xbb\x0d\x9f\x6d\x9b\x0f\x54\xd7\xd7\x62\xaf\x9f\x62\xa8\x7f\x65\xd3\xfc\x79\x36\xf9\x57\xe3\xc3\x0b\x98\xe9\x83\xed\xf3\xcb\x19\xe4\x3f\x50\x1c\xd7\x2d\xcd\xbc\x3a\x8b\xdc\xb6\x53\xa6\xcd\x39\x33\x3b\x61\xae\x5d\x1a\xf1\x6d\xbe\x13\x66\x8f\x39\x28\xef\xc0\x2c\x2d\xfd\xba\x3a\x31\x0e\x20\x2e\x9d\x62\x68\x59\x24\x3e\x11\x10\x03\x96\xb0\x44\xbd\x10\xb6\x10\xf7\xf2\xca\xdb\x0c\xfb\x72\x41\xf9\x48\xcb\xca\xea\x64\xa3\x57\xcd\x03\x10\x5b\xf8\x9e\xdd\x31\x7e\xcf\xbe\xa3\x10\x87\xb2\x76\x9e\x25\x5f\xe6\x2e\x90\xe3\x09\xb0\xd3\xab\x8b\x1f\xc6\x37\x66\x1d\xbf\x28\x40\x48\xed\x12\x58\xba\x2d\x1a\xa5\xcf\x47\xe8\x23\x03\x44\x30\x33\xaa\xf6\xa9\x5f\x1c\xa8\xec\x67\x38\xf4\x53\x8b\x44\x3f\x72\x58\x28\x91\x42\x05\x84\xdb\x04\x29\x38\x57\x99\x82\xb9\x8d\x04\x7f\xc2\x2c\x44\x94\x49\x1a\x82\xd9\x25\x9e\x08\x9e\x80\x50\x14\xe4\x31\xc2\xa1\xdd\x46\x8c\xe3\xab\xfc\xeb\x9f\x2b\x70\x15\x47\x2b\xb0\xb0\x95\xc0\x59\x6d\x74\x76\xfd\x16\x05\xb0\xc6\x5b\xca\x53\x61\xf7\x60\x32\x6e\xf6\xdf\x68\xe5\x48\x44\xca\x20\x3c\xd6\x91\x5f\x42\x55\x15\x51\x09\xca\x6c\x70\xd1\x2c\x18\xd4\x68\x5c\x20\xe1\xe8\x3c\x28\x37\xad\x80\xd1\x9a\x90\x99\x89\x82\x60\x5a\x20\x83\x98\xaf\x4e\xb4\xa9\x3c\x19\xfa\x27\xde\xf0\x84\x88\xb0\x9f\x6d\x58\xc0\x71\xdf\x52\xe5\xa4\x01\x8c\x4a\x99\xc2\x32\x87\xb9\xa2\x6a\x9d\x06\x03\xc2\x37\x27\x6e\x0f\xc1\x89\xd9\xa2\xc9\x56\x27\xa6\xa6\x3c\x59\x8c\xbc\x12\x90\x27\xb3\xcd\xc9\x65\xe9\x30\x94\x55\x85\xed\x08\xc7\xc9\x1a\x17\x47\x74\x0c\xa4\xb0\xc6\x72\xa9\xb8\xc0\x2b\xa8\x7c\x2d\x20\x14\x1a\xf9\x38\x80\xb2\x3c\x9b\x69\xb0\x90\xd8\x4a\x34\x72\x8b\xe5\x5d\xa1\x81\xb1\xa6\x65\x55\x6f\xf3\x42\x82\x15\xac\xb8\xa0\xd5\x83\x47\x56\xbf\x1a\x1f\x6a\x13\x2d\xb2\x11\x7d\xde\xeb\x37\xb9\x8a\x98\x49\xc7\xed\xf4\xb0\x3b\xea\xb4\x17\x90\x2d\x56\x20\xc9\x73\xd3\x32\x58\x01\x03\xbb\xb2\x92\x37\x77\x9b\xc6\x8c\xd1\x27\xc2\x1c\xd3\xca\xfa\x2b\x80\x94\xf0\xb5\x3d\x2c\xd1\x7f\xfd\xb7\xfb\x44\x38\xcb\x2c\x49\xa9\x92\xd0\x43\xdd\x2d\xd1\x8f\x95\x89\x1e\x65\xcb\x3c\x67\x31\x05\xa6\xce\xcc\x5a\x4f\xd9\x10\xb8\xcd\xbe\xe5\x4f\x07\xfa\x10\x25\x3e\x75\x99\xe0\xce\x83\xe8\x3c\x88\xdc\x83\xc8\x4f\xc4\xbc\x16\xff\xa1\xdd\x98\x66\x68\x36\x4d\x69\x3e\x80\x97\x35\xa4\xf9\xb1\xb4\xf0\xd5\xd8\xd2\x6d\xb1\x4c\x56\x9a\xeb\xf6\x19\xad\xb9\x31\x5a\x7f\x75\x9b\xf9\x3a\xab\xd5\x59\xad\xaf\xc3\x6a\xd1\x0d\x5e\x81\x1c\x10\x4c\xb4\xeb\x3c\xa0\x4c\x81\x60\x38\x1e\x38\x97\x73\x8f\x0d\x2b\xd5\x30\x1e\x2e\x65\x52\xe1\x38\x5e\xa2\x9e\x76\xf3\x1e\xb4\x52\x07\xf4\xd4\xae\x9d\x0f\xd8\xb3\x0b\x3d\x80\x86\x2d\xb3\xc3\x2a\x0c\x00\x65\xab\x34\xc6\xc2\x15\x3c\x62\xdf\x1c\x46\xfd\x0c\xc5\x52\x91\xc3\xbe\x00\xbc\xe6\x42\x5d\x56\xf1\xea\x23\xba\x59\x3d\x62\x02\x0f\xb2\x53\x9d\x13\xd5\x99\xa3\x3f\x98\x39\xca\x5d\x88\xd7\xe2\x43\x75\x39\x98\x2e\x07\xd3\xe5\x60\x96\xf9\xcd\x0e\x8d\x99\xb6\xee\xf4\x7f\xbd\x41\x43\x97\x80\xe9\x7c\x87\xce\x77\x78\xfd\xbe\x83\x48\x5f\x4f\x0a\xa6\x73\x1f\x3a\xf7\xa1\x73\x1f\x96\xe5\x8b\xa1\x1e\xf4\x20\xb4\xde\xbe\x90\x13\xf1\x50\x60\x9e\x88\xca\x8f\xc7\x7c\x8e\xb2\x64\x9b\x64\xc0\x19\x8f\xd3\x4d\x93\xdc\x37\x29\x21\x00\x61\xde\x2e\xd3\x52\x6d\xdd\x4b\xc9\x82\xff\x73\xf3\xf1\xf2\x0a\xab\xf5\x12\xf5\x06\xd6\xbb\x18\x14\x39\xd7\xbf\xff\xef\x3f\xfd\xc7\x40\x37\xfb\xf7\x7f\xff\xcf\x5e\x0e\xf0\x3f\x7b\x7f\xfe\xc9\xd5\xed\xd5\x3a\xbd\x06\x2c\x4b\xb4\x78\xd1\x1e\x85\x81\x5d\xef\xf1\x46\x7b\x50\xb7\x74\x03\xb5\x4e\x43\x5c\x52\xcf\xa2\xcb\xac\x47\x59\x6b\x96\x81\x3b\xd3\x73\x3f\x68\x54\x9e\x0e\x93\xb4\xb5\xed\x5c\xc4\xce\x45\xec\x5c\xc4\xce\x45\x44\x8f\xbb\x88\x99\x2e\xbf\x16\x3f\x71\xff\xa4\xe9\xb0\x7d\x78\xe6\xcc\x86\xf3\xd5\xc7\xe0\x4f\x5f\xb8\xeb\x2c\x59\x67\xc9\xbe\x42\x4b\xd6\x6d\x54\xec\xa2\xdc\x2e\xca\x7d\x65\x51\x6e\xeb\x0e\xc5\x2f\xb0\x35\xf1\x15\x4e\xcc\x5d\xe4\xd3\xf9\x0b\x9d\xbf\xf0\xba\xfd\x85\x2e\x31\xde\xb9\x0c\x9d\xcb\xf0\xfa\x5c\x86\xb6\xa4\x78\xa6\xaf\x2f\xe4\x38\x3c\x94\x10\x57\xa2\xf2\xa3\x4b\x88\x77\x09\xf1\xce\x2d\x6c\x71\x0b\xff\x40\xf7\x10\x74\x6e\xe1\xab\x73\x0b\x6b\x97\x79\x11\x01\xfb\x2e\xf1\xaa\xdc\x96\xf7\xcd\x61\xa2\xff\x44\x37\xd0\x38\x7b\x6e\x40\x5a\x2f\x34\x12\xc6\x35\xe1\x49\x1a\x9b\x0b\x8b\xb1\x16\x55\xb3\x21\xdc\x3c\x11\x34\xa8\xe8\x56\xc5\xe5\xdd\x73\xf1\x75\x8b\xeb\xfb\x43\x7e\x75\xb2\x33\x1e\x67\xe5\x7b\x0c\xf7\x50\xe5\xb0\x0b\xa1\x3f\x8f\x18\x0e\x70\x71\xc3\x66\x36\xb6\x6b\xd8\x52\xb8\xff\xa1\xe1\x94\x54\x9d\x09\xd2\x6a\xf7\x5a\xac\xde\xc1\x36\xef\x71\xb6\x47\x98\xc6\xa9\x00\xfb\x2e\xdf\x12\x7d\x87\x69\xb6\xdd\x5d\xbb\xb4\xe7\x51\x64\x2e\x39\x40\x97\x9c\xc1\x73\xc9\xf9\x02\x5c\xff\xe0\xee\x6a\x7f\x22\xcf\x3b\x46\x7f\x3e\xa3\x7f\x05\xee\x3e\x5b\xa7\x1f\xbf\xba\xbd\x63\xf3\x81\x6c\x3e\x98\x94\x39\x0a\x37\xe6\xd2\x63\x5e\x7a\xbf\x72\x83\x15\x59\x9f\xdb\x39\xb4\x4c\x17\x73\xa6\x06\x76\xcb\x7d\x64\x2f\x0d\x52\x47\xa9\x58\x03\x46\xe7\x9f\xa8\x54\x5d\xca\xae\xf3\xcd\x5e\xb1\x6f\xf6\xe8\x45\xde\xed\x37\xaa\xed\xbd\x94\x08\xaf\x56\x02\x56\x58\x41\x1f\x42\xaa\x1e\xb0\x61\x0f\x77\x5c\x34\x57\xdc\x40\xa8\x9c\xc9\x7b\x42\x5b\x6d\xef\x58\xa5\xf1\x03\x17\x88\xe7\x06\xb0\x61\x2e\x1a\x41\x62\xbf\xb2\x20\xd2\xaf\x27\x3a\xfa\x8d\x03\x05\xfd\xb6\x0d\x82\xfd\x07\xf7\x3e\xf4\x9b\xe7\x98\xed\x75\xd3\x45\xb9\x79\xb5\x24\xff\x69\x5f\xb2\xaa\xfd\x24\x3c\x76\x57\xba\xe7\x05\x2b\x50\xf9\xdf\x31\x95\xc5\x0f\xf3\x58\x52\xfe\xcb\xde\x65\x9d\xff\xbc\xcf\x0b\x3b\x0b\xd6\x59\xb0\x3f\xa0\x05\xd3\x8e\xd2\xe7\x59\x30\x0d\xe1\xab\x33\x42\x0f\x5a\x93\xce\x60\x74\x06\xe3\x95\x1b\x8c\xaa\x59\xc8\x1e\xcb\x7f\x34\x4e\xab\x3f\xbb\x52\x0f\x28\x1e\x88\x69\x8e\x10\x2a\x20\x1e\x39\xac\x39\xa9\xbc\xd3\xb9\x22\x12\x59\xa0\xd9\xf3\xd0\x5a\xf4\xcd\x53\x75\xae\xcf\x6c\x49\xa6\x80\x92\xc1\x58\xa2\xde\x4a\x0b\x99\x6d\x6f\x9e\x6a\xed\x95\xfb\xd2\x1f\xf2\x45\x3f\xfb\xb4\x86\x59\x91\xbb\xa7\x71\x6c\xde\xce\xc1\xee\x59\x0c\x22\x20\x04\xa6\x28\x8e\x65\xc1\x10\x1b\xec\x21\x5c\xba\x11\xd2\xc1\xb5\x57\x5c\x97\x9f\x46\x41\x25\xba\x98\x4a\xf6\xf7\xc0\x01\x19\x38\x20\x03\xf7\xfe\x86\xa1\x6d\x19\xa2\x36\x09\x77\xb0\xab\x3e\xd3\x51\x28\x65\xae\x0e\x35\xa4\xd0\x3f\xb3\x65\x93\x03\x3a\xd5\x71\x5c\x67\x9d\x3a\xeb\xf4\x15\x5b\xa7\x64\x4b\x0e\x36\x4d\x55\xcb\x74\x84\x24\xfd\x25\x37\x16\x57\x3f\x9c\x21\xfb\x42\x72\x5e\x41\x17\x2f\xd1\xf4\x1d\xad\x09\xd3\x51\x66\x9e\x10\x89\xb1\xa6\xe9\xc3\x10\x6c\xbd\x33\x5d\xed\xd2\xa0\xef\xbe\xf4\x4d\x4b\x63\xbe\x3a\xfd\xec\xf4\xf3\xeb\xd3\xcf\xec\xad\xd9\x03\x75\xb3\x0c\xe7\x67\xf7\x62\xe9\x12\xfd\xab\x9f\x47\x1f\x47\x8f\xfc\x2b\x2a\xa2\xbd\xff\x6a\x15\xcf\xff\xef\xe9\x87\xab\xf7\xe7\x9a\x3b\xdf\x5d\xbc\xfb\xfe\xfa\x54\x73\xa8\xad\xe2\x41\x10\x0f\xc1\xb1\x04\xd6\xbc\xe0\x1b\xc4\x9c\x98\x67\x7b\xb5\xde\xe5\xaf\xa2\x47\x29\x23\x6e\x7b\x51\xe5\xe9\xaf\xe3\x52\xf3\x20\x55\x76\x3f\x8b\x5d\xf2\x8f\x75\xc4\x27\xb2\xe7\xac\xf1\x16\xd3\xd8\xa8\x00\x69\xa4\xe7\x6d\x73\x9e\xa8\x4c\x3e\x50\xc8\x49\xba\x01\x66\xf6\x36\x6d\x90\x51\xdb\x7b\xbc\xb3\x9e\x53\xfe\x6c\x3b\x0d\x62\x28\xb5\x57\x5c\x9b\x09\xe1\x5e\xd4\xff\xc7\x5d\x1a\x00\x51\x31\x82\x90\xaa\x7f\x58\xe3\x61\x7b\x46\x1b\x9c\x14\x7b\x97\x8e\x2a\xe3\x07\x09\x48\x1a\x66\xd7\xde\x92\xcd\x90\xd3\x96\x24\xd0\x85\x09\xd5\x4a\x97\x2a\xc4\xa3\x32\x0e\xba\x1b\x27\x2e\x8e\x92\xe6\x29\x70\x46\x99\x76\xee\xac\xca\x07\x90\xf9\x58\x66\xe1\xd3\x54\xab\x8e\x23\x27\xbb\x7d\x85\xcd\xfa\x87\xcd\x77\x11\x4b\x8d\x9c\x78\xf7\x15\xdd\x00\x4f\x55\x7f\x43\x59\xea\x5e\xb8\xd6\x66\xcf\x3e\xde\xe6\x2a\x21\x96\x6e\x02\x10\x55\xcc\xb3\x06\x96\x8a\x46\xa9\xdd\xe6\x22\x33\x82\xd2\x09\xdc\x63\x44\x23\xc4\x38\x33\x26\xc8\x29\x7d\xf1\x28\x33\x7a\x08\x95\x25\xea\xf9\xc3\x9e\xee\xca\x1f\x66\xbd\x3d\x34\x0a\xe7\x3d\xf6\x33\x17\xb3\x75\x14\x75\x3f\x34\x9f\xbb\xca\xe2\xf0\x12\x03\xa9\x61\x63\x76\x2b\x9a\x92\xde\x43\xf8\x6f\x30\xc3\x2b\x08\xfb\xc1\xae\x6f\xb2\x16\xfd\x2d\x8e\x53\x68\x1f\x86\x2d\x5a\xd1\x2d\x30\xe7\xc8\x97\x00\xf6\x70\x92\x0c\xaa\x5b\xe8\x0a\xd8\x3d\x9b\x12\xb1\xd3\x8b\x15\x2d\x1c\xc7\xe8\x8a\x87\xd2\x65\x29\x4d\x10\x53\x16\xf0\xec\xa9\x65\x74\x11\x21\x6c\xd4\xe5\x8d\x9d\xaa\x40\xea\xda\x19\xa1\x32\x62\x68\x65\xe4\x66\x3a\xb0\x58\x5a\x53\x4f\x65\x09\xa4\x41\xe1\xd8\x8c\xa8\x0a\x0e\xc9\x34\x01\xf3\x92\xbc\x6c\x92\xb4\x9d\x40\x4b\xd4\x7c\x02\xf8\x21\x12\x27\x3c\xec\x2b\xd8\x24\xb1\x79\x63\xbf\x8d\xb0\x09\x0f\x51\x5e\xc3\x4a\x43\x93\x14\x07\xca\x84\xa5\x57\x19\x62\x09\x54\xb9\xe6\xf1\xc3\x28\x50\x89\xe8\x8a\x71\x51\x16\xb1\xf6\xf1\x74\x21\x52\xe7\x82\x7d\x4d\x2e\x58\xe5\xf9\xdb\x67\x38\x60\x7a\xc0\x76\x63\xb2\x15\xc0\x18\xaf\x34\xdf\x6c\x4e\xd7\x66\x52\x12\x01\x5b\xed\x2f\x38\x95\xd0\xa3\x16\xd4\xbc\x0b\xb6\xe3\x69\x71\x0b\xb7\xd6\xfa\xcc\x5a\x18\x63\xf5\x3f\xfe\xf2\xf1\xc3\x39\x02\xb6\xa5\x82\x33\xe3\x71\x6c\xb1\xa0\x5a\x4e\xf2\xe7\xe8\x8b\xc6\x25\xcd\x2e\xf6\x57\x93\x54\x08\x60\x2a\xde\x69\x71\x30\xb3\x8d\x45\x41\xf1\x0c\x0b\x28\x19\xf5\xa3\x3d\x3d\x1a\x07\xca\x8c\xd0\x26\x87\xec\xdc\x4f\x8d\x54\xa5\x09\xe1\x9b\x62\x2b\xea\x11\x72\x0b\xdf\x4d\x2c\xcb\x9b\xaf\x4b\x1b\xa5\x2d\x91\x49\x78\x92\x91\x39\xdb\x2a\xed\x0d\x47\x63\x83\xf7\x86\x8b\x02\x4b\xca\x22\x9e\xc1\x0e\xa9\xd4\xe8\xf5\xd7\x7c\x03\x7d\x60\xdb\xbe\x1e\x97\xd6\x18\x6d\xb0\xcd\x16\xe5\xde\x97\x67\xd4\x3d\x17\x77\xba\x56\x48\x85\xd9\x37\xb0\xfb\x52\xfc\x69\x74\x64\x67\x04\x65\x9e\xc9\x0f\x76\xf9\x4c\xd7\xca\xad\x12\xfd\xca\x5c\x7b\x39\x6e\x8d\xe6\x63\xff\x30\x6e\xb9\x71\xf4\xf3\x71\x3c\xc4\xb6\x3d\xf3\x8c\x37\xac\xcd\x31\xe8\xfd\xfb\xb3\x6e\x9e\xe9\xe6\x99\xd7\x39\xcf\xb4\xbf\xaa\xfe\xac\x09\xe7\x94\x21\xca\x30\x31\xd7\x75\x6b\x45\x37\x1b\x16\x6b\x31\x61\xc4\xe3\x98\xdf\xcb\x7f\x43\x12\x20\x0b\xfa\x32\x2d\xcc\x96\xf4\xde\x73\x72\xb7\x44\x3d\xa3\xfd\xf9\x91\x03\xf3\xeb\xad\x03\xb3\x44\xbd\xd1\x34\x2f\x12\xc0\xe0\xfe\x2d\xe0\x50\x23\xa6\x8b\x86\xa5\x22\x25\x76\x57\x20\x28\x0f\x97\xa8\xe7\xc9\x47\xb5\x57\x7b\x89\x9d\xf6\x76\xda\xfb\x3b\xd3\x5e\xfb\xa2\xfe\xb3\xb4\xf6\x8c\x6f\x36\x9c\x35\xd4\x54\x98\xb8\xd8\x1d\x15\x43\x84\x87\x10\x14\x7b\x06\x7f\xc1\x89\xe9\x13\x44\xdf\xb6\x5b\xa2\x7f\xe5\xf1\xd9\x7f\xe5\x7f\x21\xad\xc4\x5b\x88\x7b\x4b\xd4\xd3\xd3\x6d\xef\xb8\x5c\x64\x36\x7d\xf2\x44\xbb\x73\x3d\x77\x6c\xab\x52\x6e\x72\x4a\x94\xad\x7a\xcb\x0a\x48\xa4\x61\x51\x45\xb1\x06\x3b\x1a\x0e\x8f\xab\x65\x9a\xbf\x80\x23\x05\xc2\x16\x97\x4a\xff\xbb\x02\x9e\xa7\x2a\x49\xd5\x15\x56\x6b\xd9\x33\x2f\xe6\xab\x90\xa7\xaa\xf7\x53\xa5\x12\x08\xc1\xc5\xc7\x66\x4d\x10\xa2\x5e\x93\x11\x1e\x5a\x6c\x7b\xff\x94\x9c\xf5\x9a\xa5\x20\x2c\x4b\x9b\x03\x52\x74\x03\x7f\x85\x9d\x6e\xdb\xab\x8d\xc7\x10\xd0\x95\x59\x62\xd6\x2a\x68\x6e\x67\xe5\x86\x25\xf5\x0a\x04\xc7\x31\x08\x57\xc5\xfe\xa8\x57\xd9\x80\x94\x78\x95\x81\xd9\xc8\x55\xbd\x82\x54\x98\xdc\x29\x81\x49\x56\xa7\xf8\xd0\x40\x98\x32\x38\x67\x19\x29\x5a\x87\x73\x6e\xa9\xd1\x56\xae\x29\xb1\xa7\x38\x74\x02\xba\xa7\x8a\x1d\x61\xb9\x42\x59\x04\xbe\xa9\xff\x75\x84\xde\xf3\x15\x32\x68\xe5\xbe\x6d\x7e\x40\x83\xaf\x4c\xc1\xa0\x78\x58\x31\x93\xe4\x7a\x0d\xb7\x8b\xb7\x52\xdc\x25\x23\xba\x69\xe6\xab\x9a\x66\x78\x20\x41\x6c\x71\x40\x63\xaa\x76\xcf\x98\x6c\x4a\x8b\x42\xb9\x1e\x76\x6b\x42\xdd\x9a\xd0\x2b\x5a\x13\xda\x80\x12\x94\xc8\x41\x80\xc9\x1d\xb0\xb0\x1f\x82\x54\x94\x39\xa7\x88\x42\x1c\x96\xf2\xfc\x46\x61\x77\x52\xc1\x26\x6b\x86\x4a\xd5\xcb\x09\xeb\x0b\x93\xd9\x4f\xb8\x50\x32\xb3\x0d\x89\xe0\x1b\x50\x6b\x48\xa5\xb5\xdd\x2e\x09\xf3\x67\x6d\x2c\xcc\xe4\x1a\x0a\xba\x05\x51\x86\x72\xc9\x15\x2c\xd1\xf7\x52\x1b\x83\x9b\xa2\x8a\xcd\xa5\x50\x46\x52\x51\x3a\x2d\xae\x87\x2c\x56\xe5\x75\x84\x3d\x43\x5b\x96\xd0\x79\x88\x22\x25\xa4\xfa\x89\xe0\xff\x04\xa2\xfa\x34\x6c\x25\x4a\x19\x39\x57\x15\x5d\xbc\x1d\x18\xbd\x28\x41\xb6\x4d\xa9\x74\x42\x81\xe3\x01\xfa\x71\x0d\x0c\x89\x94\x19\x83\xc7\x19\x7a\x77\x76\x7e\x9c\x99\x7e\xc3\x83\x2c\x57\x55\xde\xf9\x65\xc6\x1f\x94\x45\xd8\xec\x45\xd3\x02\x93\xf1\xc5\x55\x41\xd2\xe8\x82\xdd\xfa\xe5\xde\x31\x7c\x23\x73\x1c\x69\x94\x4d\x90\x16\xb1\x12\x44\xad\xc4\x89\xe0\x5b\x1a\x96\x97\x22\xf6\xd3\x66\x89\x7a\xff\x73\xc7\xd3\x0a\x3f\x8b\xbe\xc2\xff\xd5\x58\xa7\xc9\xc0\x61\x1d\x23\xf7\xcb\x40\x89\xb9\x7e\xa4\x9f\x0d\x87\xb2\x50\x13\x04\x24\xba\x5f\x83\x11\x27\x6a\xf5\x59\x37\x2c\x5d\x01\x60\x94\x40\x42\x89\x10\x8a\x57\xb8\x93\x1a\x61\xea\xad\x62\x1e\xe0\xb8\x97\x47\xe0\xe6\x00\xbc\x21\xa0\xed\xb8\x81\xa3\xa9\x30\xd8\x9b\xb9\x24\x3c\x8d\x43\x44\xb0\x76\x3b\xe0\x93\x12\xb8\xdc\x71\x09\xa0\x95\xd3\x01\x42\x17\xd1\x5e\xed\x73\x96\xb4\x04\xe4\xd8\xf6\x5b\xe1\x53\x63\xb5\xe8\x50\x9a\x1e\x9c\xdf\xfb\x03\xb9\x6e\x9d\xe7\xf6\xda\x3c\x37\x9c\x24\xb2\x7a\x46\xe0\x2d\x24\x31\xdf\xe5\xd7\x44\x7c\xa9\x47\xf0\x9b\x47\x09\x9a\xab\xec\xed\x7d\xed\x69\xa0\xc5\x97\x33\x60\xca\x78\x98\x55\x84\x1e\xbb\x15\x69\x3b\x1c\x8c\xbc\xc1\xa8\xed\x5e\xa4\x4a\x51\xf9\x66\x24\x01\x46\x12\xe5\x12\x65\xe7\x45\x65\xfb\x89\xca\xf7\xb5\xb1\x1a\xe4\x0f\x21\x63\x75\x35\xba\x8d\x21\x06\x18\x63\xdc\x71\xbe\x7a\xb3\x86\x9b\x92\xfa\x38\x55\x5c\x12\x1c\x83\xa8\x51\x4c\xe2\xc8\x1e\x31\xdb\x52\xa2\xea\x2b\x49\x6d\x5c\x7a\x02\xee\xcf\x64\xeb\x73\x58\x6b\x15\xa0\xc9\x57\xb7\x65\x42\xf3\x09\x13\x08\xad\x51\xa3\x2c\x49\x95\x1c\x24\x58\xe0\x8d\x1c\x38\x36\xdf\xe2\x95\x36\x1e\xf9\x52\x8f\x85\x75\x92\xa4\x41\x4c\xe5\x7a\xb0\xc3\x9b\xb8\xd2\xdb\x13\x85\x69\x8f\x40\xd5\x85\x0a\x15\x07\x8c\x4f\xed\x66\x97\xcb\x03\x75\x0e\xd9\xdb\x5a\xec\x72\x5d\x85\x67\xfd\x83\xf5\xd6\xfe\x33\x4f\xec\x2d\xd1\x8a\x08\x4d\x7b\xd7\xca\x0d\x4f\xee\x5f\x23\x23\x9b\xf0\xa4\x94\xde\x70\x43\xfd\x0f\xb9\xc6\xde\xd4\x5f\x0e\x09\x9e\xfb\x40\xfc\xc8\x9b\xf8\xd1\x64\x41\x46\x98\xf8\x93\xf1\x78\x3a\x8b\xc2\x11\x99\xc3\x6c\x8c\x27\xb3\x09\xc6\xd8\x1b\xf9\xd3\xc9\x7c\x3e\x9c\x05\x23\x6f\xe4\xe3\x45\x14\x8d\xbc\x68\x16\x40\x55\x4a\xc4\x4a\x2e\xd1\xdf\x2b\xdf\x8a\x70\xc1\xbe\x1f\x88\xb0\x00\x14\xa4\x34\x56\x88\xb3\x7e\x08\x1b\xed\x84\x04\x3b\xf4\x8f\x3b\x6e\xbc\x93\x78\x0b\xff\x30\x8e\x89\xae\x97\xc9\x49\x03\x62\xb0\xb3\xe0\x90\x80\x08\x04\x30\x02\x52\x7f\x0b\xe9\x0a\xa4\x1a\xd4\xaa\xf7\xcc\x4d\x57\x2e\xc4\x35\x6b\x7d\xa2\x6f\x5a\xf7\x8e\x51\xef\xb9\x24\x2d\x40\x5a\x88\x75\xc2\xfa\xb3\x90\x84\x93\xc9\x2c\x18\x12\xdf\x9b\x04\x10\xe1\x91\x37\x9f\x8c\x09\x2c\x08\x19\x06\x24\x22\xd3\xa1\x37\x9a\x2d\x82\x30\xf0\xe6\xe1\x74\x31\xf5\xc7\xe1\xcc\x1f\xe1\x59\x34\x5e\xf8\xf3\xe9\x70\x51\xcb\x7a\xe9\x51\x68\x97\x58\x7e\x3e\xe6\x0e\x0c\xa3\xaa\x81\xb3\xe7\xfb\xe1\xd0\x1b\x8f\x66\x5e\x18\xc2\x2c\xc2\x13\x6f\x14\xf9\x9e\x3f\x9c\x4d\x82\x09\xcc\x00\x42\x32\x0b\x67\xe1\x34\x8a\xa6\xfe\x88\x0c\xc7\xe3\xd0\x0f\xfd\x31\x04\x51\x14\x4d\xb0\x17\x79\x2d\x38\xaf\xa8\xfa\x7c\x8c\x0d\x90\x16\x7c\xc3\xb9\x47\x66\x73\x6f\x3e\xf7\x17\x8b\x30\xf4\x01\x26\x43\x32\x9f\x7a\xc1\x68\xe2\x93\x60\x1c\x84\xf3\x45\x30\xf6\xbc\x60\xe2\x45\xc1\x38\x20\x93\x08\x80\x78\x73\xc0\xd3\x49\x10\xcc\x82\xb1\x4f\x5a\xf0\x05\xa6\xc4\x2e\xe1\x94\xbd\x00\xda\x05\xac\x3a\xe2\xb3\x68\x1c\x06\x0b\x6f\x1a\xcd\x7c\x7f\xe8\xcf\xc6\x78\x36\x09\x86\x30\x1a\x8e\x87\xe0\x4f\xa7\x93\x21\x0e\x01\x8f\xfd\x08\xc6\xfe\x08\x07\xb3\x68\xe8\x47\xd3\x60\x4a\x26\x33\x12\x12\x88\x26\xb3\xb0\x05\x71\x83\xad\x95\xff\x3e\x7c\xd2\x91\xe8\x4b\x48\x79\x09\x6a\x06\xb4\x3e\x16\x98\x47\xc3\xb9\x37\x9a\x04\x18\x87\x8b\xa1\xa6\x2d\x84\xb3\x00\xbc\x60\xee\x8f\x66\xc4\x5f\xf8\x93\x60\x81\x47\x64\x3a\x19\x4e\x49\x34\x5d\x00\x0e\x08\x19\x87\x63\xcf\x9f\x61\xdf\x9b\x8e\xa3\x96\xb1\x24\x2f\x80\x79\x92\xc6\xb1\xdb\x74\xd7\x2a\x3b\xb3\x11\x0c\x3d\xcf\x1f\x4f\x7c\x18\x86\xe3\x70\x3a\xc3\xd1\x8c\x8c\xa7\x81\x4f\x82\xc5\x70\x16\x4e\xbc\x70\x3c\x9a\x78\x30\x9f\x4c\x83\x61\x34\xf7\xa7\x01\x1e\x92\xb9\x1f\x8e\xc1\x9b\xcc\xa3\x31\x09\x5a\xd0\xd6\x56\x2d\xec\xaf\x88\xec\x47\xa0\xc8\xfa\x73\xe9\xbf\x05\x16\x72\x51\xae\xb3\xe2\x7c\x15\x03\x89\x79\x1a\x6a\x27\x24\xe2\x62\x73\x62\x7e\xd9\xae\x41\xc8\x93\x52\xef\x56\x75\x8a\xdf\x75\x0a\x60\x98\xce\xbc\x51\x10\x0d\xc3\xf9\x7c\xbc\x98\xcc\xc8\x98\x8c\xc6\xd1\x74\xb4\x20\xd8\xf3\x47\xde\x62\xb6\xf0\xa2\xc9\x70\x3a\x0f\xa7\xd1\x22\x8c\x20\x0c\xa7\xc3\xd1\x6c\x12\x2e\x60\x86\x49\xe0\x05\x4d\xed\x69\x31\xf4\x9a\x0b\x10\xa2\x48\xf0\x0d\x7a\xcb\xc9\x1d\x88\x75\x1a\x1c\x17\xa6\xfa\x18\x61\x73\x32\xe3\x34\x11\x34\x46\xa3\xe9\xb1\xd9\x5f\xd1\x34\xe0\x8c\x27\x05\x31\x15\xc5\x8c\xb3\x13\x1d\x85\xe6\xd3\xd8\x70\x41\x08\x4c\xbc\xd1\x70\xe1\xfb\x8b\xf9\x7c\xec\x4d\x17\x53\x02\xc3\xf1\x02\xe3\xd1\x38\xc2\x93\xc9\xd8\x9b\x85\x0b\x3f\x00\x08\x83\xf9\xc4\x9b\x7b\xd8\x5f\x84\xe3\x30\x20\xd1\x14\xcf\x47\x6d\xdc\x94\x6b\x88\xe3\xa2\xd3\x20\x95\xbb\x80\x7f\xca\x89\xe7\x4d\x16\x43\x02\x64\x32\x99\x4f\x00\x7c\x32\x1a\xfa\xf3\x00\x8f\xf1\x70\x1a\xcd\x17\x8b\xf1\x64\x38\x32\xa6\x68\xe2\x45\xb3\xb1\xef\xcd\x87\xc1\x78\x3a\x9e\x8c\x27\xf3\x71\xe0\x79\xfe\x24\xf0\xdb\x4c\xa5\x4c\x15\x2d\xf5\x68\xb9\xed\x18\x2c\xc3\xbb\xdc\x4a\xc3\xdc\x9f\xf9\x13\x7f\x42\x66\xd3\xf9\x28\xf0\x42\x32\xf7\x26\x8b\xa9\x1f\x8c\x46\x1e\xf6\x47\x64\x31\x85\xc9\x68\x32\xf1\x27\x5e\x00\x64\x38\x9e\x82\x1f\x06\xe3\x39\x8c\xc7\xf3\x09\xc1\xe0\x41\xef\xa7\xaa\x17\x64\x0e\xc3\x7c\xd0\x7e\x4d\xcd\xad\xec\xef\x5b\x83\xcc\xfe\x6d\x74\x43\x7b\x6f\xde\x09\x28\x72\xb2\xa7\x32\xb0\x6d\x7b\x07\x37\x7f\xbb\xb9\x3d\xff\xf0\xf3\xe5\xe9\x87\xf3\x9b\xab\xd3\xb3\xf3\x5a\x17\xc6\x67\xfc\x4e\xf0\xcd\xb2\x56\x80\x6c\x22\xe7\x1a\xa2\x66\x89\x2b\xb3\x98\xe5\xf7\xf1\xe5\x51\x50\x0d\x8f\x23\x74\x11\x99\x80\x5c\x0b\xad\xc9\x25\xda\xac\x87\x16\x67\xd3\xe6\xd8\x94\x9a\x2c\x13\x8e\x25\x47\xcc\xc5\xb7\x95\xab\x09\xb2\x7f\x47\x59\x1e\xd2\x39\x5d\x6f\x24\xba\xe6\xb1\x49\x66\x7a\xc3\x61\x5f\xf0\x18\x8c\x03\xeb\x2e\x01\x8c\x53\xbb\xdb\x0a\xb1\xfc\x58\x77\x01\xc9\x0c\x5e\x66\x79\xd0\x9e\x25\xef\x06\x27\xb2\x87\x7a\x2b\x50\x3d\x73\x72\xbb\xae\x30\xee\xee\x43\x93\x1a\xff\xf9\xfd\xc7\x77\xef\x2e\x2e\xdf\x19\xf2\xb6\x51\x76\x2f\x83\xfb\x55\x58\x1f\xbf\xbd\x39\xbf\xfe\xe1\xf4\xdb\x8b\xf7\x17\xb7\x7f\x7b\x1c\x62\xdb\x7a\x42\x2b\xdc\xd3\xeb\xdb\x8b\xef\x4e\xcf\x6e\x7f\xfe\xf6\xfb\xb3\xbf\x9e\xdf\x3e\x0e\xb9\xfd\xdc\xeb\x7e\xd8\x57\x3f\x9c\x3d\x01\x70\x71\x64\xad\x15\xea\x77\xe7\xa7\xb7\xdf\x5f\x9f\xff\xfc\xdd\xfb\xd3\x77\x37\x7b\xc0\xb6\x6d\xf3\x6c\x05\xf8\xfe\xfc\xf4\xed\xf9\xf5\xf9\xfb\xf3\xb3\xdb\x8b\x8f\x97\x07\x70\xab\x75\x43\x4f\x15\xf4\x87\xf3\xdb\xeb\x8b\xb3\x9b\x9f\xdf\x7e\xfc\x70\x7a\x71\xd9\x0e\xae\x14\x2f\x25\xf5\x07\x49\x33\xeb\xd0\x1a\xbb\xec\x91\x1a\x92\x2d\x39\xd5\xf5\xf2\xc1\x96\x6d\x57\x5e\xd5\xae\xe8\x33\x11\x58\x6b\x0e\xa4\x25\x73\x71\x58\x68\xf5\x32\x19\x88\x97\x4b\xc2\x94\xc3\x4e\xb3\x98\x50\xbf\x04\x76\xad\x54\x92\x25\x34\x8b\x51\x70\xa1\x96\x68\x31\x5c\x14\x7b\x21\x12\xc1\x15\x27\x3c\x5e\xa2\xdb\xb3\xab\xfc\xab\xc2\x62\x05\xea\xaa\x5e\xbd\x99\x23\x39\x88\x80\x5d\x0a\xb5\x5b\xfd\xfe\x3a\x72\xa8\xd9\xca\x9b\x06\x5d\x54\xb5\xb7\x14\x6c\x52\xa9\x50\x60\x87\x23\xf5\x07\xec\x96\xc0\xdc\x41\x2b\x53\x29\x3f\xe9\x82\x68\x71\xd1\xaf\xb5\x72\x27\x93\xe1\x30\xbb\x75\x2e\x3b\x34\x65\x26\xff\x41\xe6\x72\xe4\x4b\x97\x54\x1a\x68\xd6\xd5\x30\xfd\x6a\x57\xa3\x80\x57\x2c\x71\xda\x2c\x1a\x8f\xd0\x8f\xe7\xdf\xfe\xe5\xe3\xc7\xbf\xfe\xac\x67\xe5\x8b\xb3\x73\x33\x6b\xa0\x00\x62\x7e\x3f\xd8\x6f\x9a\xaa\xf7\xe0\xfd\xd6\xc9\xe1\xfc\x7a\xd5\xdf\x65\x92\xb8\x79\xa7\xa0\xf6\xf0\xf2\x51\xfd\xfe\x32\xc7\x6d\x17\x3d\xb7\x0d\xe9\x99\xa2\xf0\x39\xe2\xf0\x0c\x91\xd8\x23\x16\x75\xd1\x40\x87\xa6\x7a\x9b\x74\xd8\x9f\xe7\x6d\xa3\x9b\xdb\x46\x42\xad\x3d\x79\xc7\xb5\x11\xe5\x42\xa1\x04\xab\x75\x6e\x31\x03\xca\xb0\x28\xf6\x73\xe4\xbd\xd0\x5f\x6a\x59\xd1\x23\x93\x34\x95\x69\x20\x15\x55\xc6\xdc\xaf\x41\xd4\x02\x83\xcf\xcc\x22\x67\x5b\xe0\xea\x59\xc3\x45\x34\xf4\xa7\xe1\x64\x31\xf6\x26\x93\x30\x08\xa7\xc3\xa9\x3f\x0e\x16\x7e\x34\x9d\xcc\x26\x41\xe4\x4f\x17\xc3\xb9\x37\xc2\xfe\x78\x38\x8f\xc2\x39\xf1\x17\x64\x0a\xd1\xd0\x27\xd1\x24\x5c\xcc\xe7\x81\xf7\x47\x8e\x11\x1d\x49\xbb\x00\xf1\xb1\x00\xf1\x4b\x44\x47\x6d\x93\xe7\xbe\x18\xe9\x11\x0b\xd9\x04\x7b\x76\xbd\x37\x94\x6d\xbb\x54\xbc\x0a\xea\xf3\xe3\x37\x6d\xcf\x48\x2a\xa8\xda\x9d\x71\xa6\xe0\x93\xaa\x6b\x81\xd9\x36\x71\x25\xe8\x96\xc6\xb0\x82\x73\x3d\xb5\xb8\x1d\x43\xe5\x77\x36\xec\xbf\x4a\x48\x52\xc5\xb4\x1e\x93\xd8\x7f\xb9\xb9\x6a\xc4\x1c\xd5\xd6\x89\xe0\x11\x8d\x9b\xc9\xa5\x5a\xfb\xf9\x70\x38\x6f\x6d\x6f\x1c\xf1\x56\xa6\x34\x61\x4c\x26\xe3\x5f\x2b\xe2\xac\x23\xd4\x3e\x87\xee\x9d\xce\x9c\xad\x7d\x76\x08\xfa\x34\x3f\x6f\x4f\xfc\x79\x84\xcc\x9b\x48\x90\x6f\x34\xd2\xd3\x4d\xce\xb7\x6c\xbe\xda\xb8\x6d\x7b\xc5\xbe\x43\x13\x74\x51\x96\x5f\x4c\xb0\x01\xb9\x2e\x6f\x5a\x7b\x5a\x5c\xfb\x40\x04\x5b\x09\x8f\x9b\xc2\x94\x34\xa5\xa7\x0c\xa8\x54\xb0\x5f\xa0\x2c\x9c\x4c\x80\x1a\x60\x8a\x82\x43\x23\xea\xc7\x05\x24\x17\xd5\x6f\xca\xfe\xe3\x8a\xdf\x66\xbf\x9c\x9a\xfe\xff\x00\x00\x00\xff\xff\x10\xf7\x0c\x29\xb0\xd3\x00\x00") func stacksTektonStackYamlBytes() ([]byte, error) { return bindataRead( @@ -367,7 +367,7 @@ func stacksTektonStackYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "stacks/tekton-stack.yaml", size: 18299, mode: os.FileMode(420), modTime: time.Unix(1557785965, 0)} + info := bindataFileInfo{name: "stacks/tekton-stack.yaml", size: 54192, mode: os.FileMode(420), modTime: time.Unix(1557785965, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/stacks/rio-bootstrap-stack.yaml b/stacks/rio-bootstrap-stack.yaml index 090298859..822d819ba 100644 --- a/stacks/rio-bootstrap-stack.yaml +++ b/stacks/rio-bootstrap-stack.yaml @@ -201,6 +201,12 @@ kubernetes: - podsecuritypolicies verbs: - use + - apiGroups: + - networking.istio.io + resources: + - '*' + verbs: + - '*' --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -241,6 +247,20 @@ kubernetes: selector: rio-controller: "true" --- + apiVersion: v1 + kind: Service + metadata: + name: rio-acme-solver + namespace: ${NAMESPACE} + spec: + ports: + - name: http-8080 + port: 8080 + protocol: TCP + targetPort: 8080 + selector: + rio-controller: "true" + --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: @@ -307,6 +327,12 @@ kubernetes: - "*" verbs: - "*" + - apiGroups: + - networking.istio.io + resources: + - '*' + verbs: + - '*' --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/stacks/rio-controller-stack.yaml b/stacks/rio-controller-stack.yaml index 631ca23e5..28ae1af93 100644 --- a/stacks/rio-controller-stack.yaml +++ b/stacks/rio-controller-stack.yaml @@ -30,6 +30,8 @@ kubernetes: {{- if eq .Values.RIO_DEBUG "true" }} - --debug {{- end}} + - --mesh-mode + - ${MESH_MODE} - --features - "" env: @@ -50,3 +52,4 @@ template: description: "namespace to deploy to" - variable: RIO_DEBUG - variable: RUN_API_VALIDATOR + - variable: MESH_MODE diff --git a/stacks/smi-stack.yaml b/stacks/smi-stack.yaml index e2956c110..2317189be 100644 --- a/stacks/smi-stack.yaml +++ b/stacks/smi-stack.yaml @@ -7,7 +7,10 @@ kubernetes: name: trafficsplits.split.smi-spec.io spec: group: split.smi-spec.io - version: v1alpha1 + versions: + - name: v1alpha1 + storage: true + served: true scope: Namespaced names: kind: TrafficSplit diff --git a/stacks/tekton-stack.yaml b/stacks/tekton-stack.yaml index 34610f0cd..892089259 100644 --- a/stacks/tekton-stack.yaml +++ b/stacks/tekton-stack.yaml @@ -1,400 +1,901 @@ kubernetes: manifest: | + # Copyright 2019 The Tekton Authors + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: v1 kind: Namespace metadata: name: tekton-pipelines --- + # Copyright 2019 The Tekton Authors + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: tekton-pipelines spec: + privileged: false allowPrivilegeEscalation: false - fsGroup: - ranges: - - max: 65535 - min: 1 - rule: MustRunAs - hostIPC: false + volumes: + - 'emptyDir' + - 'configMap' + - 'secret' hostNetwork: false + hostIPC: false hostPID: false - privileged: false runAsUser: - rule: RunAsAny + rule: 'RunAsAny' seLinux: - rule: RunAsAny + rule: 'RunAsAny' supplementalGroups: + rule: 'MustRunAs' ranges: - - max: 65535 - min: 1 - rule: MustRunAs - volumes: - - emptyDir - - configMap - - secret + - min: 1 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + - min: 1 + max: 65535 --- + # Copyright 2020 The Tekton 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 + # + # https://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. + + kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 + metadata: + name: tekton-pipelines-controller-cluster-access + rules: + - apiGroups: [""] + # Namespace access is required because the controller timeout handling logic + # iterates over all namespaces and times out any PipelineRuns that have expired. + # Pod access is required because the taskrun controller wants to be updated when + # a Pod underlying a TaskRun changes state. + resources: ["namespaces", "pods"] + verbs: ["list", "watch"] + # Controller needs cluster access to all of the CRDs that it is responsible for + # managing. + - apiGroups: ["tekton.dev"] + resources: ["tasks", "clustertasks", "taskruns", "pipelines", "pipelineruns", "pipelineresources", + "conditions"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: ["tekton.dev"] + resources: ["taskruns/finalizers", "pipelineruns/finalizers"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: ["tekton.dev"] + resources: ["tasks/status", "clustertasks/status", "taskruns/status", "pipelines/status", + "pipelineruns/status", "pipelineresources/status"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: ["tekton-pipelines"] + verbs: ["use"] + --- kind: ClusterRole + apiVersion: rbac.authorization.k8s.io/v1 metadata: - name: tekton-pipelines-admin + # This is the access that the controller needs on a per-namespace basis. + name: tekton-pipelines-controller-tenant-access rules: - - apiGroups: - - "" - resources: - - pods - - pods/log - - namespaces - - secrets - - events - - serviceaccounts - - configmaps - - persistentvolumeclaims - verbs: - - get - - list - - create - - update - - delete - - patch - - watch - - apiGroups: - - apps - resources: - - deployments - verbs: - - get - - list - - create - - update - - delete - - patch - - watch - - apiGroups: - - apps - resources: - - deployments/finalizers - verbs: - - get - - list - - create - - update - - delete - - patch - - watch - - apiGroups: - - admissionregistration.k8s.io - resources: - - mutatingwebhookconfigurations - verbs: - - get - - list - - create - - update - - delete - - patch - - watch - - apiGroups: - - tekton.dev - resources: - - tasks - - clustertasks - - taskruns - - pipelines - - pipelineruns - - pipelineresources - - conditions - verbs: - - get - - list - - create - - update - - delete - - patch - - watch - - apiGroups: - - tekton.dev - resources: - - taskruns/finalizers - - pipelineruns/finalizers - verbs: - - get - - list - - create - - update - - delete - - patch - - watch - - apiGroups: - - tekton.dev - resources: - - tasks/status - - clustertasks/status - - taskruns/status - - pipelines/status - - pipelineruns/status - - pipelineresources/status - verbs: - - get - - list - - create - - update - - delete - - patch - - watch - - apiGroups: - - policy - resourceNames: - - tekton-pipelines - resources: - - podsecuritypolicies - verbs: - - use + - apiGroups: [""] + resources: ["pods", "pods/log", "secrets", "events", "serviceaccounts", "configmaps", + "persistentvolumeclaims", "limitranges"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + # Unclear if this access is actually required. Simply a hold-over from the previous + # incarnation of the controller's ClusterRole. + - apiGroups: ["apps"] + resources: ["deployments"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: ["apps"] + resources: ["deployments/finalizers"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + --- + kind: ClusterRole + apiVersion: rbac.authorization.k8s.io/v1 + metadata: + name: tekton-pipelines-webhook-cluster-access + rules: + - # The webhook needs to be able to list and update customresourcedefinitions, + # mainly to update the webhook certificates. + apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions", "customresourcedefinitions/status"] + verbs: ["get", "list", "update", "patch", "watch"] + - apiGroups: ["admissionregistration.k8s.io"] + # The webhook performs a reconciliation on these two resources and continuously + # updates configuration. + resources: ["mutatingwebhookconfigurations", "validatingwebhookconfigurations"] + # knative starts informers on these things, which is why we need get, list and watch. + verbs: ["list", "watch"] + - apiGroups: ["admissionregistration.k8s.io"] + resources: ["mutatingwebhookconfigurations"] + # This mutating webhook is responsible for applying defaults to tekton objects + # as they are received. + resourceNames: ["webhook.pipeline.tekton.dev"] + # When there are changes to the configs or secrets, knative updates the mutatingwebhook config + # with the updated certificates or the refreshed set of rules. + verbs: ["get", "update"] + - apiGroups: ["admissionregistration.k8s.io"] + resources: ["validatingwebhookconfigurations"] + # validation.webhook.pipeline.tekton.dev performs schema validation when you, for example, create TaskRuns. + # config.webhook.pipeline.tekton.dev validates the logging configuration against knative's logging structure + resourceNames: ["validation.webhook.pipeline.tekton.dev", "config.webhook.pipeline.tekton.dev"] + # When there are changes to the configs or secrets, knative updates the validatingwebhook config + # with the updated certificates or the refreshed set of rules. + verbs: ["get", "update"] + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: ["tekton-pipelines"] + verbs: ["use"] --- + # Copyright 2020 The Tekton 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 + # + # https://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. + + kind: Role + apiVersion: rbac.authorization.k8s.io/v1 + metadata: + name: tekton-pipelines-controller + namespace: tekton-pipelines + rules: + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["list", "watch"] + - # The controller needs access to these configmaps for logging information and runtime configuration. + apiGroups: [""] + resources: ["configmaps"] + verbs: ["get"] + resourceNames: ["config-logging", "config-observability", "config-artifact-bucket", + "config-artifact-pvc", "feature-flags", "config-leader-election"] + --- + kind: Role + apiVersion: rbac.authorization.k8s.io/v1 + metadata: + name: tekton-pipelines-webhook + namespace: tekton-pipelines + rules: + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["list", "watch"] + - # The webhook needs access to these configmaps for logging information. + apiGroups: [""] + resources: ["configmaps"] + verbs: ["get"] + resourceNames: ["config-logging", "config-observability"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["list", "watch"] + - # The webhook daemon makes a reconciliation loop on webhook-certs. Whenever + # the secret changes it updates the webhook configurations with the certificates + # stored in the secret. + apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "update"] + resourceNames: ["webhook-certs"] + + --- + # Copyright 2019 The Tekton Authors + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. apiVersion: v1 kind: ServiceAccount metadata: name: tekton-pipelines-controller namespace: tekton-pipelines + --- + apiVersion: v1 + kind: ServiceAccount + metadata: + name: tekton-pipelines-webhook + namespace: tekton-pipelines --- + # Copyright 2019 The Tekton Authors + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: - name: tekton-pipelines-controller-admin + name: tekton-pipelines-controller-cluster-access + subjects: + - kind: ServiceAccount + name: tekton-pipelines-controller + namespace: tekton-pipelines roleRef: + kind: ClusterRole + name: tekton-pipelines-controller-cluster-access apiGroup: rbac.authorization.k8s.io + --- + # If this ClusterRoleBinding is replaced with a RoleBinding + # then the ClusterRole would be namespaced. The access described by + # the tekton-pipelines-controller-tenant-access ClusterRole would + # be scoped to individual tenant namespaces. + apiVersion: rbac.authorization.k8s.io/v1beta1 + kind: ClusterRoleBinding + metadata: + name: tekton-pipelines-controller-tenant-access + subjects: + - kind: ServiceAccount + name: tekton-pipelines-controller + namespace: tekton-pipelines + roleRef: kind: ClusterRole - name: tekton-pipelines-admin + name: tekton-pipelines-controller-tenant-access + apiGroup: rbac.authorization.k8s.io + --- + apiVersion: rbac.authorization.k8s.io/v1beta1 + kind: ClusterRoleBinding + metadata: + name: tekton-pipelines-webhook-cluster-access subjects: - kind: ServiceAccount + name: tekton-pipelines-webhook + namespace: tekton-pipelines + roleRef: + kind: ClusterRole + name: tekton-pipelines-webhook-cluster-access + apiGroup: rbac.authorization.k8s.io + + --- + # Copyright 2020 The Tekton Authors + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + + apiVersion: rbac.authorization.k8s.io/v1beta1 + kind: RoleBinding + metadata: name: tekton-pipelines-controller namespace: tekton-pipelines + subjects: + - kind: ServiceAccount + name: tekton-pipelines-controller + namespace: tekton-pipelines + roleRef: + kind: Role + name: tekton-pipelines-controller + apiGroup: rbac.authorization.k8s.io + --- + apiVersion: rbac.authorization.k8s.io/v1beta1 + kind: RoleBinding + metadata: + name: tekton-pipelines-webhook + namespace: tekton-pipelines + subjects: + - kind: ServiceAccount + name: tekton-pipelines-webhook + namespace: tekton-pipelines + roleRef: + kind: Role + name: tekton-pipelines-webhook + apiGroup: rbac.authorization.k8s.io --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: clustertasks.tekton.dev + labels: + pipeline.tekton.dev/release: "devel" + version: "devel" spec: group: tekton.dev + preserveUnknownFields: false + validation: + openAPIV3Schema: + type: object + # One can use x-kubernetes-preserve-unknown-fields: true + # at the root of the schema (and inside any properties, additionalProperties) + # to get the traditional CRD behaviour that nothing is pruned, despite + # setting spec.preserveUnknownProperties: false. + # + # See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/ + # See issue: https://github.com/knative/serving/issues/912 + x-kubernetes-preserve-unknown-fields: true + versions: + - name: v1alpha1 + served: true + storage: true + - name: v1beta1 + served: true + storage: false names: - categories: - - all - - tekton-pipelines kind: ClusterTask plural: clustertasks + categories: + - tekton + - tekton-pipelines scope: Cluster + # Opt into the status subresource so metadata.generation + # starts to increment subresources: status: {} - version: v1alpha1 + conversion: + strategy: Webhook + webhookClientConfig: + service: + name: tekton-pipelines-webhook + namespace: tekton-pipelines --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: conditions.tekton.dev + labels: + pipeline.tekton.dev/release: "devel" + version: "devel" spec: group: tekton.dev names: - categories: - - all - - tekton-pipelines kind: Condition plural: conditions + categories: + - tekton + - tekton-pipelines scope: Namespaced + # Opt into the status subresource so metadata.generation + # starts to increment subresources: status: {} version: v1alpha1 --- + # Copyright 2018 The Knative 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: + name: images.caching.internal.knative.dev labels: knative.dev/crd-install: "true" - name: images.caching.internal.knative.dev spec: group: caching.internal.knative.dev + version: v1alpha1 names: + kind: Image + plural: images + singular: image categories: - knative-internal - caching - kind: Image - plural: images shortNames: - img - singular: image scope: Namespaced subresources: status: {} - version: v1alpha1 --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: pipelines.tekton.dev + labels: + pipeline.tekton.dev/release: "devel" + version: "devel" spec: group: tekton.dev + preserveUnknownFields: false + validation: + openAPIV3Schema: + type: object + # One can use x-kubernetes-preserve-unknown-fields: true + # at the root of the schema (and inside any properties, additionalProperties) + # to get the traditional CRD behaviour that nothing is pruned, despite + # setting spec.preserveUnknownProperties: false. + # + # See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/ + # See issue: https://github.com/knative/serving/issues/912 + x-kubernetes-preserve-unknown-fields: true + versions: + - name: v1alpha1 + served: true + storage: true + - name: v1beta1 + served: true + storage: false names: - categories: - - all - - tekton-pipelines kind: Pipeline plural: pipelines + categories: + - tekton + - tekton-pipelines scope: Namespaced + # Opt into the status subresource so metadata.generation + # starts to increment subresources: status: {} - version: v1alpha1 + conversion: + strategy: Webhook + webhookClientConfig: + service: + name: tekton-pipelines-webhook + namespace: tekton-pipelines --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: pipelineruns.tekton.dev + labels: + pipeline.tekton.dev/release: "devel" + version: "devel" spec: - additionalPrinterColumns: - - JSONPath: .status.conditions[?(@.type=="Succeeded")].status - name: Succeeded - type: string - - JSONPath: .status.conditions[?(@.type=="Succeeded")].reason - name: Reason - type: string - - JSONPath: .status.startTime - name: StartTime - type: date - - JSONPath: .status.completionTime - name: CompletionTime - type: date group: tekton.dev + preserveUnknownFields: false + validation: + openAPIV3Schema: + type: object + # One can use x-kubernetes-preserve-unknown-fields: true + # at the root of the schema (and inside any properties, additionalProperties) + # to get the traditional CRD behaviour that nothing is pruned, despite + # setting spec.preserveUnknownProperties: false. + # + # See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/ + # See issue: https://github.com/knative/serving/issues/912 + x-kubernetes-preserve-unknown-fields: true + versions: + - name: v1alpha1 + served: true + storage: true + - name: v1beta1 + served: true + storage: false names: - categories: - - all - - tekton-pipelines kind: PipelineRun plural: pipelineruns + categories: + - tekton + - tekton-pipelines shortNames: - pr - prs scope: Namespaced + additionalPrinterColumns: + - name: Succeeded + type: string + JSONPath: ".status.conditions[?(@.type==\"Succeeded\")].status" + - name: Reason + type: string + JSONPath: ".status.conditions[?(@.type==\"Succeeded\")].reason" + - name: StartTime + type: date + JSONPath: .status.startTime + - name: CompletionTime + type: date + JSONPath: .status.completionTime + # Opt into the status subresource so metadata.generation + # starts to increment subresources: status: {} - version: v1alpha1 + conversion: + strategy: Webhook + webhookClientConfig: + service: + name: tekton-pipelines-webhook + namespace: tekton-pipelines --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: pipelineresources.tekton.dev + labels: + pipeline.tekton.dev/release: "devel" + version: "devel" spec: group: tekton.dev names: - categories: - - all - - tekton-pipelines kind: PipelineResource plural: pipelineresources + categories: + - tekton + - tekton-pipelines scope: Namespaced + # Opt into the status subresource so metadata.generation + # starts to increment subresources: status: {} version: v1alpha1 --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: tasks.tekton.dev + labels: + pipeline.tekton.dev/release: "devel" + version: "devel" spec: group: tekton.dev + preserveUnknownFields: false + validation: + openAPIV3Schema: + type: object + # One can use x-kubernetes-preserve-unknown-fields: true + # at the root of the schema (and inside any properties, additionalProperties) + # to get the traditional CRD behaviour that nothing is pruned, despite + # setting spec.preserveUnknownProperties: false. + # + # See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/ + # See issue: https://github.com/knative/serving/issues/912 + x-kubernetes-preserve-unknown-fields: true + versions: + - name: v1alpha1 + served: true + storage: true + - name: v1beta1 + served: true + storage: false names: - categories: - - all - - tekton-pipelines kind: Task plural: tasks + categories: + - tekton + - tekton-pipelines scope: Namespaced + # Opt into the status subresource so metadata.generation + # starts to increment subresources: status: {} - version: v1alpha1 + conversion: + strategy: Webhook + webhookClientConfig: + service: + name: tekton-pipelines-webhook + namespace: tekton-pipelines --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: taskruns.tekton.dev + labels: + pipeline.tekton.dev/release: "devel" + version: "devel" spec: - additionalPrinterColumns: - - JSONPath: .status.conditions[?(@.type=="Succeeded")].status - name: Succeeded - type: string - - JSONPath: .status.conditions[?(@.type=="Succeeded")].reason - name: Reason - type: string - - JSONPath: .status.startTime - name: StartTime - type: date - - JSONPath: .status.completionTime - name: CompletionTime - type: date group: tekton.dev + preserveUnknownFields: false + validation: + openAPIV3Schema: + type: object + # One can use x-kubernetes-preserve-unknown-fields: true + # at the root of the schema (and inside any properties, additionalProperties) + # to get the traditional CRD behaviour that nothing is pruned, despite + # setting spec.preserveUnknownProperties: false. + # + # See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/ + # See issue: https://github.com/knative/serving/issues/912 + x-kubernetes-preserve-unknown-fields: true + versions: + - name: v1alpha1 + served: true + storage: true + - name: v1beta1 + served: true + storage: false names: - categories: - - all - - tekton-pipelines kind: TaskRun plural: taskruns + categories: + - tekton + - tekton-pipelines shortNames: - tr - trs scope: Namespaced + additionalPrinterColumns: + - name: Succeeded + type: string + JSONPath: ".status.conditions[?(@.type==\"Succeeded\")].status" + - name: Reason + type: string + JSONPath: ".status.conditions[?(@.type==\"Succeeded\")].reason" + - name: StartTime + type: date + JSONPath: .status.startTime + - name: CompletionTime + type: date + JSONPath: .status.completionTime + # Opt into the status subresource so metadata.generation + # starts to increment subresources: status: {} - version: v1alpha1 + conversion: + strategy: Webhook + webhookClientConfig: + service: + name: tekton-pipelines-webhook + namespace: tekton-pipelines --- + # Copyright 2020 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: v1 - kind: Service + kind: Secret metadata: - labels: - app: tekton-pipelines-controller - name: tekton-pipelines-controller + name: webhook-certs namespace: tekton-pipelines - spec: - ports: - - name: metrics - port: 9090 - protocol: TCP - targetPort: 9090 - selector: - app: tekton-pipelines-controller - + labels: + pipeline.tekton.dev/release: devel + # The data is populated at install time. --- - apiVersion: v1 - kind: Service + apiVersion: admissionregistration.k8s.io/v1beta1 + kind: ValidatingWebhookConfiguration metadata: + name: validation.webhook.pipeline.tekton.dev labels: - app: tekton-pipelines-webhook - name: tekton-pipelines-webhook - namespace: tekton-pipelines - spec: - ports: - - port: 443 - targetPort: 8443 - selector: - app: tekton-pipelines-webhook + pipeline.tekton.dev/release: devel + webhooks: + - admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: tekton-pipelines-webhook + namespace: tekton-pipelines + failurePolicy: Fail + sideEffects: None + name: validation.webhook.pipeline.tekton.dev + --- + apiVersion: admissionregistration.k8s.io/v1beta1 + kind: MutatingWebhookConfiguration + metadata: + name: webhook.pipeline.tekton.dev + labels: + pipeline.tekton.dev/release: devel + webhooks: + - admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: tekton-pipelines-webhook + namespace: tekton-pipelines + failurePolicy: Fail + sideEffects: None + name: webhook.pipeline.tekton.dev + --- + apiVersion: admissionregistration.k8s.io/v1beta1 + kind: ValidatingWebhookConfiguration + metadata: + name: config.webhook.pipeline.tekton.dev + labels: + pipeline.tekton.dev/release: devel + webhooks: + - admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: tekton-pipelines-webhook + namespace: tekton-pipelines + failurePolicy: Fail + sideEffects: None + name: config.webhook.pipeline.tekton.dev + namespaceSelector: + matchExpressions: + - key: pipeline.tekton.dev/release + operator: Exists --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + name: tekton-aggregate-edit labels: - rbac.authorization.k8s.io/aggregate-to-admin: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" - name: tekton-aggregate-edit + rbac.authorization.k8s.io/aggregate-to-admin: "true" rules: - apiGroups: - tekton.dev @@ -416,12 +917,26 @@ kubernetes: - watch --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + name: tekton-aggregate-view labels: rbac.authorization.k8s.io/aggregate-to-view: "true" - name: tekton-aggregate-view rules: - apiGroups: - tekton.dev @@ -438,25 +953,83 @@ kubernetes: - watch --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: v1 - data: null kind: ConfigMap metadata: name: config-artifact-bucket namespace: tekton-pipelines + # data: + # # location of the gcs bucket to be used for artifact storage + # location: "gs://bucket-name" + # # name of the secret that will contain the credentials for the service account + # # with access to the bucket + # bucket.service.account.secret.name: + # # The key in the secret with the required service account json + # bucket.service.account.secret.key: --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: v1 - data: null kind: ConfigMap metadata: name: config-artifact-pvc namespace: tekton-pipelines + # data: + # # size of the PVC volume + # size: 5Gi + # + # # storage class of the PVC volume + # storageClassName: storage-class-name --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: v1 + kind: ConfigMap + metadata: + name: config-defaults + namespace: tekton-pipelines data: - _example: | + _example: |- ################################ # # # EXAMPLE CONFIGURATION # @@ -479,16 +1052,109 @@ kubernetes: # default-service-account contains the default service account name # to use for TaskRun and PipelineRun, if none is specified. default-service-account: "default" + + # default-managed-by-label-value contains the default value given to the + # "app.kubernetes.io/managed-by" label applied to all Pods created for + # TaskRuns. If a user's requested TaskRun specifies another value for this + # label, the user's request supercedes. + default-managed-by-label-value: "tekton-pipelines" + + # default-pod-template contains the default pod template to use + # TaskRun and PipelineRun, if none is specified. If a pod template + # is specified, the default pod template is ignored. + # default-pod-template: + + --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + + apiVersion: v1 kind: ConfigMap metadata: - name: config-defaults + name: feature-flags namespace: tekton-pipelines + data: + # Setting this flag to "true" will prevent Tekton overriding your + # Task container's $HOME environment variable. + # + # The default behaviour currently is for Tekton to override the + # $HOME environment variable but this will change in an upcoming + # release. + # + # See https://github.com/tektoncd/pipeline/issues/2013 for more + # info. + disable-home-env-overwrite: "false" + # Setting this flag to "true" will prevent Tekton overriding your + # Task container's working directory. + # + # The default behaviour currently is for Tekton to override the + # working directory if not set by the user but this will change + # in an upcoming release. + # + # See https://github.com/tektoncd/pipeline/issues/1836 for more + # info. + disable-working-directory-overwrite: "false" --- + # Copyright 2020 Tekton Authors LLC + # + # 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: v1 + kind: ConfigMap + metadata: + name: config-leader-election + namespace: tekton-pipelines data: - loglevel.controller: info - loglevel.webhook: info + # An inactive but valid configuration follows; see example. + resourceLock: "leases" + leaseDuration: "15s" + renewDeadline: "10s" + retryPeriod: "2s" + + --- + # Copyright 2019 Tekton Authors LLC + # + # 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + + apiVersion: v1 + kind: ConfigMap + metadata: + name: config-logging + namespace: tekton-pipelines + data: + # Common configuration for all knative codebase zap-logger-config: | { "level": "info", @@ -514,13 +1180,30 @@ kubernetes: "callerEncoder": "" } } - kind: ConfigMap - metadata: - name: config-logging - namespace: tekton-pipelines + # Log level overrides + loglevel.controller: "info" + loglevel.webhook: "info" --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: v1 + kind: ConfigMap + metadata: + name: config-observability + namespace: tekton-pipelines data: _example: | ################################ @@ -555,20 +1238,32 @@ kubernetes: # charge. If metrics.backend-destination is not Stackdriver, this is # ignored. metrics.allow-stackdriver-custom-metrics: "false" - kind: ConfigMap - metadata: - name: config-observability - namespace: tekton-pipelines --- + # Copyright 2019 The Tekton Authors + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: apps/v1 kind: Deployment metadata: - labels: - app.kubernetes.io/component: controller - app.kubernetes.io/name: tekton-pipelines name: tekton-pipelines-controller namespace: tekton-pipelines + labels: + app.kubernetes.io/name: tekton-pipelines + app.kubernetes.io/component: controller + pipeline.tekton.dev/release: "v0.12.1" + version: "v0.12.1" spec: replicas: 1 selector: @@ -580,95 +1275,185 @@ kubernetes: cluster-autoscaler.kubernetes.io/safe-to-evict: "false" labels: app: tekton-pipelines-controller - app.kubernetes.io/component: controller app.kubernetes.io/name: tekton-pipelines + app.kubernetes.io/component: controller + # tekton.dev/release value replaced with inputs.params.versionTag in pipeline/tekton/publish.yaml + pipeline.tekton.dev/release: "v0.12.1" + version: "v0.12.1" spec: + serviceAccountName: tekton-pipelines-controller containers: - - args: - - -logtostderr - - -stderrthreshold - - INFO - - -kubeconfig-writer-image - - gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/kubeconfigwriter@sha256:912d30334e63899f3875806b0633b5ddf3470d64fbd2333fc2c534afcfa9872d - - -creds-image - - gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/creds-init@sha256:8f8c43a115984e90db3b0cb3fcd46e1699ec15515ca7d258571a44c7d76040ca - - -git-image - - gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init@sha256:00466e8ec7d8a289140893523d33261ba5006dfb1bd9b96aee2736fc739dba5a - - -nop-image - - gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/nop@sha256:b77955ba2711e1ba30ab48670bcafd725ddc01a105d173256e158053914dc42c - - -bash-noop-image - - gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/bash@sha256:a96b5840cdeb2a6598a8566a8607b925732286a8fdf15147be3591b7c7fb41f7 - - -gsutil-image - - gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/gsutil@sha256:0130ec562b897c5929123d4e14cd3271cd58102f1f411f52cb6f415088bf5944 - - -entrypoint-image - - gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/entrypoint@sha256:5c2a7261d923b8af29ad3be34a9c9a3abd1ed11a030ca1cc207293d203755ab4 - - -imagedigest-exporter-image - - gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/imagedigestexporter@sha256:23e2de68c86de494aba98dabf02b175efc051827c52350bdd9a89f6a3d969ea9 - - -pr-image - - gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/pullrequest-init@sha256:26a181a89c00ab840599508e905d1cfeed5db2b4ea41fbcc63c22979389e4a46 - - -build-gcs-fetcher-image - - gcr.io/tekton-releases/github.com/tektoncd/pipeline/vendor/github.com/googlecloudplatform/cloud-builders/gcs-fetcher/cmd/gcs-fetcher@sha256:5be2e14ed6b986198beca21a93af34e807586dcf9155babeca7f5971a2fa0311 + - name: tekton-pipelines-controller + image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/controller:v0.12.1@sha256:0ca86ec6f246f49c1ac643357fd1c8e73a474aaa216548807b1216a9ff12f7be + args: [ + # These images are built on-demand by `ko resolve` and are replaced + # by image references by digest. + "-kubeconfig-writer-image", "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/kubeconfigwriter:v0.12.1@sha256:67dcd447b0c624befa12843ce9cc0bcfc502179bdb28d59563d761a7f3968509", + "-creds-image", "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/creds-init:v0.12.1@sha256:6266d023172dde7fa421f626074b4e7eedc7d7d5ff561c033d6d63ebfff4a2f2", + "-git-image", "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init:v0.12.1@sha256:d82c78288699dd6ee40c852b146cb3bd89b322b42fb3bc4feec28ea54bb7b36c", + "-entrypoint-image", "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/entrypoint:v0.12.1@sha256:7f3db925f7660673a74b0e1030e65540adea36fe361ab7f06f5b5c47cdcef47d", + "-imagedigest-exporter-image", "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/imagedigestexporter:v0.12.1@sha256:e8f08214baad9054bbed7be2b8617c6964b9a1c5405cf59eabcc3d3267a6253f", + "-pr-image", "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/pullrequest-init:v0.12.1@sha256:71e0226346e0d3d57af7c35b6cb907d42d3142e845b0f865ba0c86d3e248f3cb", + "-build-gcs-fetcher-image", "gcr.io/tekton-releases/github.com/tektoncd/pipeline/vendor/github.com/googlecloudplatform/cloud-builders/gcs-fetcher/cmd/gcs-fetcher:v0.12.1@sha256:ae5721bf0d883947c3c13f519ca26129792f4058d5f9dfedd50174d9e7acb2bc", + # These images are pulled from Dockerhub, by digest, as of April 15, 2020. + "-nop-image", "tianon/true@sha256:009cce421096698832595ce039aa13fa44327d96beedb84282a69d3dbcf5a81b", + "-shell-image", "busybox@sha256:a2490cec4484ee6c1068ba3a05f89934010c85242f736280b35343483b2264b6", + "-gsutil-image", "google/cloud-sdk@sha256:6e8676464c7581b2dc824956b112a61c95e4144642bec035e6db38e3384cae2e"] + volumeMounts: + - name: config-logging + mountPath: /etc/config-logging env: - name: SYSTEM_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - - name: CONFIG_LOGGING_NAME + - # If you are changing these names, you will also need to update + # the controller's Role in 200-role.yaml to include the new + # values in the "configmaps" "get" rule. + name: CONFIG_LOGGING_NAME value: config-logging - name: CONFIG_OBSERVABILITY_NAME value: config-observability + - name: CONFIG_ARTIFACT_BUCKET_NAME + value: config-artifact-bucket + - name: CONFIG_ARTIFACT_PVC_NAME + value: config-artifact-pvc + - name: CONFIG_FEATURE_FLAGS_NAME + value: feature-flags + - name: CONFIG_LEADERELECTION_NAME + value: config-leader-election - name: METRICS_DOMAIN value: tekton.dev/pipeline - image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/controller@sha256:72a2bda21b5bc23550e94fdf7cee8a6e5bd82601f5d81a6237fc2b8c42321a59 - name: tekton-pipelines-controller - volumeMounts: - - mountPath: /etc/config-logging - name: config-logging - serviceAccountName: tekton-pipelines-controller volumes: - - configMap: + - name: config-logging + configMap: name: config-logging - name: config-logging + --- + apiVersion: v1 + kind: Service + metadata: + labels: + app: tekton-pipelines-controller + pipeline.tekton.dev/release: "v0.12.1" + version: "v0.12.1" + name: tekton-pipelines-controller + namespace: tekton-pipelines + spec: + ports: + - name: http-metrics + port: 9090 + protocol: TCP + targetPort: 9090 + selector: + app: tekton-pipelines-controller --- + # Copyright 2019 The Tekton 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 + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + apiVersion: apps/v1 kind: Deployment metadata: - labels: - app.kubernetes.io/component: webhook-controller - app.kubernetes.io/name: tekton-pipelines + # Note: the Deployment name must be the same as the Service name specified in + # config/400-webhook-service.yaml. If you change this name, you must also + # change the value of WEBHOOK_SERVICE_NAME below. name: tekton-pipelines-webhook namespace: tekton-pipelines + labels: + app.kubernetes.io/name: tekton-pipelines + app.kubernetes.io/component: webhook-controller + pipeline.tekton.dev/release: "v0.12.1" + version: "v0.12.1" spec: replicas: 1 selector: matchLabels: app: tekton-pipelines-webhook + role: webhook template: metadata: annotations: cluster-autoscaler.kubernetes.io/safe-to-evict: "false" labels: app: tekton-pipelines-webhook - app.kubernetes.io/component: webhook-controller + role: webhook app.kubernetes.io/name: tekton-pipelines + app.kubernetes.io/component: webhook-controller + pipeline.tekton.dev/release: "v0.12.1" + version: "v0.12.1" spec: + serviceAccountName: tekton-pipelines-webhook containers: - - env: + - name: webhook + # This is the Go import path for the binary that is containerized + # and substituted here. + image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/webhook:v0.12.1@sha256:69f065d493244dbd50563b96f5474bf6590821a6308fd8c69c5ef06cf4d988b2 + env: - name: SYSTEM_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/webhook@sha256:1d6336f2748cb8e5c19b17191a54c6adbbc77e2d1c60818f93282ec482bb2957 - name: webhook - volumeMounts: - - mountPath: /etc/config-logging - name: config-logging - serviceAccountName: tekton-pipelines-controller - volumes: - - configMap: - name: config-logging - name: config-logging + - # If you are changing these names, you will also need to update + # the webhook's Role in 200-role.yaml to include the new + # values in the "configmaps" "get" rule. + name: CONFIG_LOGGING_NAME + value: config-logging + - name: CONFIG_OBSERVABILITY_NAME + value: config-observability + - name: CONFIG_LEADERELECTION_NAME + value: config-leader-election + - name: WEBHOOK_SERVICE_NAME + value: tekton-pipelines-webhook + - name: WEBHOOK_SECRET_NAME + value: webhook-certs + - name: METRICS_DOMAIN + value: tekton.dev/pipeline + securityContext: + allowPrivilegeEscalation: false + ports: + - name: metrics + containerPort: 9090 + - name: profiling + containerPort: 8008 + - name: https-webhook + containerPort: 8443 + --- + apiVersion: v1 + kind: Service + metadata: + labels: + app: tekton-pipelines-webhook + role: webhook + pipeline.tekton.dev/release: v0.12.1 + version: "v0.12.1" + name: tekton-pipelines-webhook + namespace: tekton-pipelines + spec: + ports: + - # Define metrics and profiling for them to be accessible within service meshes. + name: http-metrics + port: 9090 + targetPort: 9090 + - name: http-profiling + port: 8008 + targetPort: 8008 + - name: https-webhook + port: 443 + targetPort: 8443 + selector: + app: tekton-pipelines-webhook + role: webhook --- diff --git a/tests/integration/suite_test.go b/tests/integration/suite_test.go index f2b4681a1..eea528e34 100644 --- a/tests/integration/suite_test.go +++ b/tests/integration/suite_test.go @@ -18,10 +18,11 @@ func TestMain(m *testing.M) { func TestSuite(t *testing.T) { suite := spec.New("integration suite", spec.Report(report.Terminal{}), spec.Parallel()) specs := map[string]func(t *testing.T, when spec.G, it spec.S){ - "attach": attachTests, - "build": buildTests, - "config": configTests, - "domain": domainTests, + "attach": attachTests, + "build": buildTests, + "config": configTests, + // todo: figure out how to enable domain test with random public domain + //"domain": domainTests, "export": exportTests, "externalService": externalServiceTests, "log": logTests, diff --git a/tests/testutil/service.go b/tests/testutil/service.go index 7a11da072..136bfd561 100644 --- a/tests/testutil/service.go +++ b/tests/testutil/service.go @@ -11,11 +11,12 @@ import ( "testing" "time" + "github.com/rancher/wrangler/pkg/condition" + riov1 "github.com/rancher/rio/pkg/apis/rio.cattle.io/v1" tektonv1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" - apis "knative.dev/pkg/apis" ) type TestService struct { @@ -743,7 +744,7 @@ func (ts *TestService) waitForBuild() error { if ts.Service.Spec.Template { return true, nil } - if ts.Build.Status.GetCondition(apis.ConditionSucceeded) != nil && ts.Build.Status.GetCondition(apis.ConditionSucceeded).IsTrue() { + if condition.Cond("Succeeded").IsTrue(&ts.Build) { return true, nil } } diff --git a/types/context.go b/types/context.go index 2cb67ecb5..473e7fd39 100644 --- a/types/context.go +++ b/types/context.go @@ -4,18 +4,19 @@ import ( "context" webhookinator "github.com/rancher/gitwatcher/pkg/generated/controllers/gitwatcher.cattle.io" + config2 "github.com/rancher/rio/pkg/config" "github.com/rancher/rio/pkg/generated/controllers/admin.rio.cattle.io" - gateway "github.com/rancher/rio/pkg/generated/controllers/gateway.solo.io" - gloo "github.com/rancher/rio/pkg/generated/controllers/gloo.solo.io" + "github.com/rancher/rio/pkg/generated/controllers/gateway.solo.io" + "github.com/rancher/rio/pkg/generated/controllers/gloo.solo.io" + istio "github.com/rancher/rio/pkg/generated/controllers/networking.istio.io" "github.com/rancher/rio/pkg/generated/controllers/rio.cattle.io" + smi "github.com/rancher/rio/pkg/generated/controllers/split.smi-spec.io" "github.com/rancher/wrangler-api/pkg/generated/controllers/apiextensions.k8s.io" "github.com/rancher/wrangler-api/pkg/generated/controllers/apps" "github.com/rancher/wrangler-api/pkg/generated/controllers/batch" - certmanager "github.com/rancher/wrangler-api/pkg/generated/controllers/cert-manager.io" "github.com/rancher/wrangler-api/pkg/generated/controllers/core" extensionsv1beta1 "github.com/rancher/wrangler-api/pkg/generated/controllers/extensions" "github.com/rancher/wrangler-api/pkg/generated/controllers/rbac" - smi "github.com/rancher/wrangler-api/pkg/generated/controllers/split.smi-spec.io" "github.com/rancher/wrangler-api/pkg/generated/controllers/storage" build "github.com/rancher/wrangler-api/pkg/generated/controllers/tekton.dev" "github.com/rancher/wrangler/pkg/apply" @@ -36,9 +37,9 @@ type Context struct { Apps *apps.Factory Batch *batch.Factory Build *build.Factory - CertManager *certmanager.Factory Core *core.Factory Ext *apiextensions.Factory + Istio *istio.Factory K8sNetworking *extensionsv1beta1.Factory Admin *admin.Factory K8s kubernetes.Interface @@ -65,7 +66,6 @@ func NewContext(namespace string, config *rest.Config) *Context { Apps: apps.NewFactoryFromConfigOrDie(config), Batch: batch.NewFactoryFromConfigOrDie(config), Build: build.NewFactoryFromConfigOrDie(config), - CertManager: certmanager.NewFactoryFromConfigOrDie(config), Core: core.NewFactoryFromConfigOrDie(config), Ext: apiextensions.NewFactoryFromConfigOrDie(config), K8sNetworking: extensionsv1beta1.NewFactoryFromConfigOrDie(config), @@ -76,9 +76,14 @@ func NewContext(namespace string, config *rest.Config) *Context { SMI: smi.NewFactoryFromConfigOrDie(config), Webhook: webhookinator.NewFactoryFromConfigOrDie(config), K8s: kubernetes.NewForConfigOrDie(config), - Gateway: gateway.NewFactoryFromConfigOrDie(config), - Gloo: gloo.NewFactoryFromConfigOrDie(config), - RestConfig: config, + + RestConfig: config, + } + if config2.ConfigController.MeshMode == "istio" { + context.Istio = istio.NewFactoryFromConfigOrDie(config) + } else if config2.ConfigController.MeshMode == "linkerd" { + context.Gloo = gloo.NewFactoryFromConfigOrDie(config) + context.Gateway = gateway.NewFactoryFromConfigOrDie(config) } context.Apply = apply.New(context.K8s.Discovery(), apply.NewClientFactory(config)).WithRateLimiting(20.0) @@ -86,11 +91,10 @@ func NewContext(namespace string, config *rest.Config) *Context { } func (c *Context) Start(ctx context.Context) error { - return start.All(ctx, 5, + starters := []start.Starter{ c.Apps, c.Batch, c.Build, - c.CertManager, c.Core, c.Ext, c.K8sNetworking, @@ -100,8 +104,14 @@ func (c *Context) Start(ctx context.Context) error { c.Storage, c.SMI, c.Webhook, - c.Gateway, - c.Gloo) + } + if config2.ConfigController.MeshMode == "istio" { + starters = append(starters, c.Istio) + } else if config2.ConfigController.MeshMode == "linkerd" { + starters = append(starters, c.Gloo) + starters = append(starters, c.Gateway) + } + return start.All(ctx, 5, starters...) } func BuildContext(ctx context.Context, namespace string, config *rest.Config) (context.Context, *Context) {