├── .circleci └── config.yml ├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── cmd └── rudder │ └── rudder.go ├── code-of-conduct.md ├── e2e ├── Dockerfile ├── e2e_suite_test.go ├── e2e_test.go ├── helm_client.go └── utils.go ├── examples └── wp-values.yaml ├── glide.lock ├── glide.yaml ├── manifests └── ruddered-tiller.yaml ├── pkg ├── federation │ ├── federation.go │ └── federation_test.go └── releaseutil │ ├── manifest.go │ └── manifest_test.go ├── rootfs ├── Dockerfile └── README.md ├── scripts ├── federation-clean.sh ├── federation.sh ├── import.sh └── portforward.sh ├── utils └── populate-configmap.py └── versioning.mk /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Golang CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-go/ for more details 4 | version: 2 5 | jobs: 6 | build: 7 | docker: 8 | - image: nebril/rudder-fed-e2e 9 | working_directory: /go/src/github.com/kubernetes-helm/rudder-federation 10 | steps: 11 | - checkout 12 | - setup_remote_docker: 13 | version: 17.05.0-ce 14 | - run: make e2e 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | vendor 3 | rootfs/rudder 4 | manifests/fed-credentials.yaml 5 | rudder 6 | e2e.test 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: go 3 | services: 4 | - docker 5 | go: 6 | - 1.7 7 | script: 8 | - make bootstrap 9 | - make e2e -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DOCKER_REGISTRY ?= gcr.io 2 | IMAGE_PREFIX ?= kubernetes-helm-rudder-federation 3 | SHORT_NAME ?= rudder 4 | DIST_DIRS = find * -type d -exec 5 | APP = rudder 6 | PACKAGE = github.com/kubernetes-helm/rudder-federation 7 | IMAGE_REPO = nebril/rudder-fed 8 | 9 | # go option 10 | GO ?= go 11 | PKG := $(shell glide novendor) 12 | TAGS := 13 | TESTS := . 14 | TESTFLAGS := 15 | LDFLAGS := 16 | GOFLAGS := 17 | BINDIR := $(CURDIR)/bin 18 | BINARIES := rudder 19 | 20 | # dind options 21 | K8S_VERSION ?= v1.7 22 | HELM_BINARY_PATH ?= /tmp/ 23 | GOPATH ?= /tmp/ 24 | 25 | 26 | # Required for globs to work correctly 27 | SHELL=/bin/bash 28 | 29 | .PHONY: all 30 | all: build 31 | 32 | .PHONY: build 33 | build: 34 | GOBIN=$(BINDIR) $(GO) install $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ${PACKAGE}/cmd/... 35 | 36 | .PHONY: docker-binary 37 | docker-binary: BINDIR = ./rootfs 38 | docker-binary: GOFLAGS += -a -installsuffix cgo 39 | docker-binary: 40 | GOOS=linux GOARCH=amd64 CGO_ENABLED=0 $(GO) build -o $(BINDIR)/rudder $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ${PACKAGE}/cmd/rudder 41 | 42 | .PHONY: docker-build 43 | docker-build: docker-binary 44 | docker build --rm -t $(IMAGE_REPO) rootfs 45 | 46 | .PHONY: clean 47 | clean: 48 | @rm -rf $(BINDIR) ./rootfs/rudder 49 | -rm e2e.test 50 | 51 | HAS_GLIDE := $(shell command -v glide;) 52 | HAS_GOX := $(shell command -v gox;) 53 | HAS_GIT := $(shell command -v git;) 54 | 55 | .PHONY: bootstrap 56 | bootstrap: 57 | ifndef HAS_GLIDE 58 | go get -u github.com/Masterminds/glide 59 | endif 60 | ifndef HAS_GOX 61 | go get -u github.com/mitchellh/gox 62 | endif 63 | 64 | ifndef HAS_GIT 65 | $(error You must install Git) 66 | endif 67 | glide install --strip-vendor 68 | go build -o bin/protoc-gen-go ./vendor/github.com/golang/protobuf/protoc-gen-go 69 | 70 | include versioning.mk 71 | 72 | 73 | .PHONY: img-in-dind 74 | img-in-dind: docker-build 75 | IMAGE_REPO=$(IMAGE_REPO) ./scripts/import.sh 76 | 77 | .PHONY: e2e 78 | e2e: bootstrap federation img-in-dind prepare-helm 79 | go test -c -o e2e.test ./e2e/ 80 | PATH=$(HELM_BINARY_PATH)/linux-amd64:$(GOPATH)/src/k8s.io/kubernetes/_output/bin ./e2e.test --cluster-url="$(shell kubectl cluster-info | head -n1 | cut -f6 -d' ' | sed -r 's/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g')" --use-rudder 81 | 82 | .PHONY: clean-all 83 | clean-all: clean clean-federation 84 | 85 | .PHONY: clean-federation 86 | clean-federation: 87 | ./scripts/federation-clean.sh 88 | 89 | .PHONY: federation 90 | federation: 91 | ./scripts/federation.sh 92 | 93 | .PHONE: prepare-helm 94 | prepare-helm: 95 | pushd $(HELM_BINARY_PATH) \ 96 | && curl https://kubernetes-helm.storage.googleapis.com/helm-v2.5.1-linux-amd64.tar.gz > helm.tar.gz \ 97 | && tar xf helm.tar.gz && popd 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | |![](https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/Warning.svg/156px-Warning.svg.png) | Federation rudder is no longer supported. 2 | |---|---| 3 | 4 | # Federation rudder (helm release plugin) which enables federation support for your Helm charts. 5 | 6 | ## Usage 7 | You need two things - Tiller with `--experimental-release` flag enabled, and this rudder available to Tiller. The easiest way to do it is to add container with federated rudder to Tiller deployment - check out [ruddered-tiller.yaml](manifests/ruddered-tiller.yaml). 8 | 9 | ## Federation DNS 10 | If you want your deployments distributed across federation to be able to reach pods in other clusters (and you probably do), you need to take a few steps: 11 | - You need to have dns configured in your federation so that proper dns entries are created for federated deployments. 12 | - You need to override hostnames in your charts so they may be expended to federation dns name instead of local cluster name. You can do this by either: 13 | - Changing your charts/overriding the hostname if the chart provides this option 14 | - Using additional replacement logic provided by this rudder. Refer to examples/wp-values.yaml file. You need to provide a regular expression which will match the context of the hostname (this can be tricky, as usual with regexes). The `to` part of the `replace` is being rendered by go template with Federation Controller Deployment object retrieved using data in `fed-namespace` and `fed-controller-name`. You may avoid it if you know your federation name ahead of time. 15 | 16 | ## Test Environment 17 | To setup federation with two clusters: 18 | - `git clone https://github.com/kubernetes/kubernetes $GOPATH/src/k8s.io/kubernetes` 19 | - `cd $GOPATH/src/k8s.io/kubernetes` 20 | - `git checkout release-1.7` 21 | - `make quick-release` 22 | - `git clone https://github.com/lukaszo/kubernetes-dind-federation dind` 23 | - `dind/dind-up-cluster.sh` 24 | - `CLUSTER_NAME=dind2 IP_RANGE=172.128.0.0/16 APISERVER_ADDRESS=172.128.0.1 dind/dind-up-cluster.sh` 25 | - `kubectl config use-context dind` 26 | - `dind/dind-deploy-federation.sh` 27 | - `kubefed join dind2 --host-cluster-context=dind --context=federation` 28 | 29 | Populate configmap manifest with generated tls data: 30 | - `git clone https://github.com/kubernetes-helm/rudder-federation.git $GOPATH/src/github.com/kubernetes-helm/rudder-federation` 31 | - `cd $GOPATH/src/github.com/kubernetes-helm/rudder-federation` 32 | - `python utils/populate-configmap.py > manifests/fed-credentials` 33 | 34 | Create modified tiller deployment and configmap with tls data: 35 | - `kubectl create -f manifests/` 36 | 37 | Run Helm install: 38 | - `helm install stable/wordpress` 39 | -------------------------------------------------------------------------------- /cmd/rudder/rudder.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | "net" 23 | "strings" 24 | 25 | "golang.org/x/net/context" 26 | "google.golang.org/grpc" 27 | "google.golang.org/grpc/grpclog" 28 | "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" 29 | 30 | "k8s.io/helm/pkg/kube" 31 | releaseAPI "k8s.io/helm/pkg/proto/hapi/release" 32 | rudderAPI "k8s.io/helm/pkg/proto/hapi/rudder" 33 | "k8s.io/helm/pkg/rudder" 34 | "k8s.io/helm/pkg/tiller" 35 | "k8s.io/helm/pkg/version" 36 | 37 | fedlocal "github.com/kubernetes-helm/rudder-federation/pkg/federation" 38 | ) 39 | 40 | var kubeClient *kube.Client 41 | var clientset internalclientset.Interface 42 | 43 | var grpcAddr = fmt.Sprintf("127.0.0.1:%d", rudder.GrpcPort) 44 | 45 | func main() { 46 | var err error 47 | kubeClient = kube.New(nil) 48 | clientset, err = kubeClient.ClientSet() 49 | if err != nil { 50 | grpclog.Fatalf("Cannot initialize Kubernetes connection: %s", err) 51 | } 52 | 53 | lis, err := net.Listen("tcp", grpcAddr) 54 | if err != nil { 55 | grpclog.Fatalf("failed to listen: %v", err) 56 | } 57 | grpcServer := grpc.NewServer() 58 | rudderAPI.RegisterReleaseModuleServiceServer(grpcServer, &ReleaseModuleServiceServer{}) 59 | 60 | grpclog.Info("Federation Rudder started") 61 | grpcServer.Serve(lis) 62 | } 63 | 64 | // ReleaseModuleServiceServer provides implementation for rudderAPI.ReleaseModuleServiceServer 65 | type ReleaseModuleServiceServer struct{} 66 | 67 | // Version is not yet implemented 68 | func (r *ReleaseModuleServiceServer) Version(ctx context.Context, in *rudderAPI.VersionReleaseRequest) (*rudderAPI.VersionReleaseResponse, error) { 69 | grpclog.Info("version") 70 | return &rudderAPI.VersionReleaseResponse{ 71 | Name: "helm-rudder-native", 72 | Version: version.Version, 73 | }, nil 74 | } 75 | 76 | // InstallRelease creates a release in federation and federated clusters 77 | func (r *ReleaseModuleServiceServer) InstallRelease(ctx context.Context, in *rudderAPI.InstallReleaseRequest) (*rudderAPI.InstallReleaseResponse, error) { 78 | grpclog.Info("install") 79 | 80 | manifest := in.Release.Manifest 81 | replacements := fedlocal.GetReplacements(in) 82 | 83 | if len(replacements) > 0 { 84 | fedController, err := fedlocal.GetFederationControllerDeployment(in) 85 | if err != nil { 86 | grpclog.Infof("error getting federation controller") 87 | return &rudderAPI.InstallReleaseResponse{}, err 88 | } 89 | manifest, err = fedlocal.ReplaceWithFederationDeployment(manifest, replacements, fedController) 90 | if err != nil { 91 | grpclog.Infof("error replacing replacements") 92 | return &rudderAPI.InstallReleaseResponse{}, err 93 | } 94 | } 95 | 96 | federated, local, err := fedlocal.SplitManifestForFed(manifest) 97 | 98 | if err != nil { 99 | grpclog.Infof("error splitting manifests: %v", err) 100 | return &rudderAPI.InstallReleaseResponse{}, err 101 | } 102 | 103 | _, _, clients, err := fedlocal.GetAllClients() 104 | 105 | if err != nil { 106 | grpclog.Infof("error getting clients: %v", err) 107 | return &rudderAPI.InstallReleaseResponse{}, err 108 | } 109 | 110 | err = fedlocal.CreateInFederation(federated, in) 111 | if err != nil { 112 | grpclog.Infof("error creating federated objects: %v", err) 113 | return &rudderAPI.InstallReleaseResponse{}, err 114 | } 115 | 116 | for _, c := range clients { 117 | config, _ := c.ClientConfig() 118 | grpclog.Infof("installing in %s", config.Host) 119 | err := c.Create(in.Release.Namespace, bytes.NewBufferString(local), 500, false) 120 | if err != nil { 121 | grpclog.Infof("error when creating release: %v", err) 122 | return &rudderAPI.InstallReleaseResponse{}, err 123 | } 124 | } 125 | 126 | return &rudderAPI.InstallReleaseResponse{}, err 127 | } 128 | 129 | // DeleteRelease deletes a release in federation and federated clusters 130 | func (r *ReleaseModuleServiceServer) DeleteRelease(ctx context.Context, in *rudderAPI.DeleteReleaseRequest) (*rudderAPI.DeleteReleaseResponse, error) { 131 | grpclog.Info("delete") 132 | resp := &rudderAPI.DeleteReleaseResponse{ 133 | Release: &releaseAPI.Release{}, 134 | } 135 | 136 | federated, local, err := fedlocal.SplitManifestForFed(in.Release.Manifest) 137 | 138 | if err != nil { 139 | grpclog.Infof("error splitting manifests to delete: %v", err) 140 | return resp, err 141 | } 142 | 143 | _, fedClient, clients, err := fedlocal.GetAllClients() 144 | 145 | if err != nil { 146 | grpclog.Infof("Error getting clients: %v", err) 147 | return resp, err 148 | } 149 | 150 | errChan := make(chan error) 151 | doneChan := make(chan bool) 152 | 153 | deleter := func(client *kube.Client, manifest string) { 154 | errs := make([]error, 0) 155 | host := "" 156 | 157 | defer func() { 158 | grpclog.Infof("deletion in %v complete", host) 159 | doneChan <- true 160 | for _, err := range errs { 161 | go func(e error) { 162 | grpclog.Infof("error during deletion in %v: %s", host, err) 163 | errChan <- e 164 | }(err) 165 | } 166 | }() 167 | 168 | config, err := client.ClientConfig() 169 | if err != nil { 170 | errs = append(errs, err) 171 | return 172 | } 173 | host = config.Host 174 | grpclog.Infof("Deleting in %v", host) 175 | 176 | clientset, err := client.ClientSet() 177 | if err != nil { 178 | errs = append(errs, err) 179 | return 180 | } 181 | 182 | versionset, err := tiller.GetVersionSet(clientset.Discovery()) 183 | if err != nil { 184 | errs = append(errs, err) 185 | return 186 | } 187 | 188 | release := *in.Release 189 | release.Manifest = manifest 190 | _, errs = tiller.DeleteRelease(&release, versionset, client) 191 | } 192 | 193 | go deleter(fedClient, federated) 194 | 195 | for _, client := range clients { 196 | go deleter(client, local) 197 | } 198 | 199 | //Waiting for all upgraders to finish (successful or not) 200 | grpclog.Infof("Waiting for deletions to finish") 201 | for i := 0; i < len(clients)+1; i++ { 202 | <-doneChan 203 | } 204 | 205 | select { 206 | case err = <-errChan: 207 | grpclog.Infof("Error while deleting: %v", err) 208 | return resp, err 209 | default: 210 | } 211 | grpclog.Infof("Finished deletion") 212 | return resp, err 213 | } 214 | 215 | // RollbackRelease rolls back the release 216 | func (r *ReleaseModuleServiceServer) RollbackRelease(ctx context.Context, in *rudderAPI.RollbackReleaseRequest) (*rudderAPI.RollbackReleaseResponse, error) { 217 | grpclog.Info("rollback") 218 | 219 | err := updateRelease(in.Current.Manifest, in.Target.Manifest, in.Target.Namespace, in.Force, in.Recreate, in.Wait, in.Timeout) 220 | if err != nil { 221 | grpclog.Warningf("Error rolling back release: %v", err) 222 | } 223 | return &rudderAPI.RollbackReleaseResponse{}, err 224 | } 225 | 226 | // UpgradeRelease upgrades manifests using kubernetes client 227 | func (r *ReleaseModuleServiceServer) UpgradeRelease(ctx context.Context, in *rudderAPI.UpgradeReleaseRequest) (*rudderAPI.UpgradeReleaseResponse, error) { 228 | grpclog.Info("upgrade") 229 | 230 | err := updateRelease(in.Current.Manifest, in.Target.Manifest, in.Target.Namespace, in.Force, in.Recreate, in.Wait, in.Timeout) 231 | if err != nil { 232 | grpclog.Warningf("Error updating release: %v", err) 233 | } 234 | return &rudderAPI.UpgradeReleaseResponse{}, err 235 | } 236 | 237 | func updateRelease(current, target, namespace string, force, recreate, wait bool, timeout int64) error { 238 | federatedCurrent, localCurrent, err := fedlocal.SplitManifestForFed(current) 239 | 240 | if err != nil { 241 | grpclog.Warningf("Error splitting manifest: %v", err) 242 | return err 243 | } 244 | 245 | federatedTarget, localTarget, err := fedlocal.SplitManifestForFed(target) 246 | 247 | if err != nil { 248 | grpclog.Warningf("Error splitting manifest: %v", err) 249 | return err 250 | } 251 | 252 | _, fedClient, clients, err := fedlocal.GetAllClients() 253 | 254 | if err != nil { 255 | grpclog.Warningf("Error getting clients: %v", err) 256 | return err 257 | } 258 | 259 | //We don't want errors to block goroutine 260 | errChan := make(chan error, len(clients)) 261 | doneChan := make(chan bool) 262 | 263 | upgrader := func(client *kube.Client, current, target string) { 264 | config, _ := client.ClientConfig() 265 | grpclog.Infof("Updating in %v", config.Host) 266 | err := client.Update(namespace, bytes.NewBufferString(current), bytes.NewBufferString(target), force, recreate, timeout, wait) 267 | doneChan <- true 268 | if err != nil { 269 | grpclog.Warningf("Error updating in %s: %v", config.Host, err) 270 | errChan <- err 271 | } 272 | } 273 | 274 | go upgrader(fedClient, federatedCurrent, federatedTarget) 275 | 276 | for _, client := range clients { 277 | go upgrader(client, localCurrent, localTarget) 278 | } 279 | 280 | //Waiting for all upgraders to finish (successful or not) 281 | for i := 0; i < len(clients)+1; i++ { 282 | <-doneChan 283 | } 284 | 285 | select { 286 | case err = <-errChan: 287 | return err 288 | default: 289 | } 290 | 291 | return nil 292 | } 293 | 294 | func (r *ReleaseModuleServiceServer) ReleaseStatus(ctx context.Context, in *rudderAPI.ReleaseStatusRequest) (*rudderAPI.ReleaseStatusResponse, error) { 295 | grpclog.Info("status") 296 | 297 | federated, local, err := fedlocal.SplitManifestForFed(in.Release.Manifest) 298 | 299 | if err != nil { 300 | grpclog.Infof("error splitting manifests: %v", err) 301 | return &rudderAPI.ReleaseStatusResponse{}, err 302 | } 303 | 304 | _, fedClient, clients, err := fedlocal.GetAllClients() 305 | if err != nil { 306 | grpclog.Infof("Error getting clients: %v", err) 307 | return &rudderAPI.ReleaseStatusResponse{}, err 308 | } 309 | 310 | responses := make([]string, 0, len(clients)+1) 311 | resps := make(chan string) 312 | 313 | fedResponse, err := fedClient.Get(in.Release.Namespace, bytes.NewBufferString(federated)) 314 | if err != nil { 315 | grpclog.Infof("Error getting response from federation: %v", err) 316 | return &rudderAPI.ReleaseStatusResponse{}, err 317 | } 318 | fedResponse = "Federation resources:\n" + fedResponse 319 | go func() { resps <- fedResponse }() 320 | 321 | errchan := make(chan error) 322 | for _, client := range clients { 323 | go func(client *kube.Client) { 324 | var resp string 325 | resp, err := client.Get(in.Release.Namespace, bytes.NewBufferString(local)) 326 | config, _ := client.ClientConfig() 327 | resp = config.Host + " resources:\n" + resp 328 | resps <- resp 329 | if err != nil { 330 | errchan <- err 331 | } 332 | }(client) 333 | } 334 | 335 | for i := 0; i < len(clients)+1; i++ { 336 | responses = append(responses, <-resps) 337 | } 338 | 339 | select { 340 | case err = <-errchan: 341 | grpclog.Infof("Error getting response from federated cluster: %v", err) 342 | return &rudderAPI.ReleaseStatusResponse{}, err 343 | default: 344 | } 345 | 346 | separator := "#########\n" 347 | finalResponse := strings.Join(responses, separator) 348 | 349 | in.Release.Info.Status.Resources = finalResponse 350 | return &rudderAPI.ReleaseStatusResponse{ 351 | Release: in.Release, 352 | Info: in.Release.Info, 353 | }, err 354 | } 355 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Kubernetes Community Code of Conduct 2 | 3 | Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) 4 | -------------------------------------------------------------------------------- /e2e/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM circleci/golang:1.8 2 | 3 | RUN sudo apt-get install python-pip 4 | RUN sudo pip install pyyaml 5 | -------------------------------------------------------------------------------- /e2e/e2e_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Mirantis 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package e2e_test 16 | 17 | import ( 18 | . "github.com/onsi/ginkgo" 19 | . "github.com/onsi/gomega" 20 | 21 | "testing" 22 | ) 23 | 24 | func TestE2e(t *testing.T) { 25 | RegisterFailHandler(Fail) 26 | RunSpecs(t, "E2e Suite") 27 | } 28 | -------------------------------------------------------------------------------- /e2e/e2e_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Mirantis 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package e2e 16 | 17 | import ( 18 | . "github.com/onsi/ginkgo" 19 | . "github.com/onsi/gomega" 20 | 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | "k8s.io/client-go/kubernetes" 23 | "k8s.io/client-go/pkg/api/v1" 24 | ) 25 | 26 | var _ = Describe("Basic Suite", func() { 27 | 28 | var helm HelmManager 29 | var namespace *v1.Namespace 30 | var clientset kubernetes.Interface 31 | 32 | BeforeEach(func() { 33 | var err error 34 | clientset, err = KubeClient() 35 | Expect(err).NotTo(HaveOccurred()) 36 | By("Creating namespace and initializing test framework") 37 | namespaceObj := &v1.Namespace{ 38 | ObjectMeta: metav1.ObjectMeta{ 39 | GenerateName: "e2e-federation-rudder-", 40 | }, 41 | } 42 | namespace, err = clientset.Core().Namespaces().Create(namespaceObj) 43 | Expect(err).NotTo(HaveOccurred()) 44 | helm = &BinaryHelmManager{ 45 | Namespace: namespace.Name, 46 | Clientset: clientset, 47 | HelmBin: "helm", 48 | KubectlBin: "kubectl", 49 | } 50 | Expect(helm.InstallTiller()).NotTo(HaveOccurred()) 51 | }) 52 | 53 | AfterEach(func() { 54 | By("Removing namespace") 55 | DeleteNS(clientset, namespace) 56 | }) 57 | 58 | It("Should be possible to create/delete/upgrade/rollback and check status of wordpress chart", func() { 59 | chartName := "stable/wordpress" 60 | By("Install chart stable/wordpress") 61 | releaseName, err := helm.Install(chartName, nil) 62 | Expect(err).NotTo(HaveOccurred()) 63 | By("Check status of release " + releaseName) 64 | Expect(helm.Status(releaseName)).NotTo(HaveOccurred()) 65 | By("Upgrading release " + releaseName) 66 | Expect(helm.Upgrade(chartName, releaseName, map[string]string{"image": "bitnami/wordpress:4.7.3-r1"})).NotTo(HaveOccurred()) 67 | By("Rolling back release " + releaseName + "to a first revision") 68 | Expect(helm.Rollback(releaseName, 1)).NotTo(HaveOccurred()) 69 | By("Deleting release " + releaseName) 70 | //Timeout in delete in federation is a known issue. It can be flaky, so not checking it for now 71 | helm.Delete(releaseName) 72 | }) 73 | }) 74 | -------------------------------------------------------------------------------- /e2e/helm_client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Mirantis 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package e2e 16 | 17 | import ( 18 | "fmt" 19 | "os/exec" 20 | "regexp" 21 | "strconv" 22 | 23 | "time" 24 | 25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | "k8s.io/client-go/kubernetes" 27 | "k8s.io/client-go/pkg/api/v1" 28 | 29 | "strings" 30 | 31 | "bytes" 32 | 33 | . "github.com/onsi/ginkgo" 34 | . "github.com/onsi/gomega" 35 | ) 36 | 37 | const ( 38 | experimentalTillerImage string = "nebril/tiller" 39 | rudderPodName string = "rudder" 40 | rudderImage string = "nebril/rudder-fed" 41 | ) 42 | 43 | // HelmManager provides functionality to install client/server helm and use it 44 | type HelmManager interface { 45 | // InstallTiller will bootstrap tiller pod in k8s 46 | InstallTiller() error 47 | // DeleteTiller removes tiller pod from k8s 48 | DeleteTiller(removeHelmHome bool) error 49 | // Install chart, returns releaseName and error 50 | Install(chartName string, values map[string]string) (string, error) 51 | // Status verifies state of installed release 52 | Status(releaseName string) error 53 | // Delete release 54 | Delete(releaseName string) error 55 | // Upgrade release 56 | Upgrade(chartName, releaseName string, values map[string]string) error 57 | // Rollback release 58 | Rollback(releaseName string, revision int) error 59 | } 60 | 61 | // BinaryHelmManager uses helm binary to work with helm server 62 | type BinaryHelmManager struct { 63 | Clientset kubernetes.Interface 64 | Namespace string 65 | HelmBin string 66 | KubectlBin string 67 | } 68 | 69 | func (m *BinaryHelmManager) InstallTiller() error { 70 | arg := make([]string, 0, 5) 71 | var err error 72 | if enableRudder { 73 | arg = append(arg, "create", "-f", "manifests/", "-n", m.Namespace) 74 | _, err = m.executeUsingKubectl(arg...) 75 | } else { 76 | arg = append(arg, "init", "--tiller-namespace", m.Namespace) 77 | _, err = m.executeUsingHelm(arg...) 78 | } 79 | if err != nil { 80 | return err 81 | } 82 | By("Waiting for tiller pod") 83 | waitTillerPod(m.Clientset, m.Namespace) 84 | return nil 85 | } 86 | 87 | func (m *BinaryHelmManager) DeleteTiller(removeHelmHome bool) error { 88 | arg := make([]string, 0, 4) 89 | arg = append(arg, "reset", "--tiller-namespace", m.Namespace, "--force") 90 | if removeHelmHome { 91 | arg = append(arg, "--remove-helm-home") 92 | } 93 | _, err := m.executeUsingHelm(arg...) 94 | if err != nil { 95 | return err 96 | } 97 | return nil 98 | } 99 | 100 | func (m *BinaryHelmManager) Install(chartName string, values map[string]string) (string, error) { 101 | stdout, err := m.executeCommandWithValues(chartName, "install", values) 102 | if err != nil { 103 | return "", err 104 | } 105 | return getNameFromHelmOutput(stdout), nil 106 | } 107 | 108 | // Status reports nil if release is considered to be succesfull 109 | func (m *BinaryHelmManager) Status(releaseName string) error { 110 | stdout, err := m.executeUsingHelm("status", releaseName, "--tiller-namespace", m.Namespace) 111 | if err != nil { 112 | return err 113 | } 114 | status := getStatusFromHelmOutput(stdout) 115 | if status == "DEPLOYED" { 116 | return nil 117 | } 118 | return fmt.Errorf("Expected status is DEPLOYED. But got %v for release %v.", status, releaseName) 119 | } 120 | 121 | func (m *BinaryHelmManager) Delete(releaseName string) error { 122 | _, err := m.executeUsingHelm("delete", releaseName, "--tiller-namespace", m.Namespace) 123 | return err 124 | } 125 | 126 | func (m *BinaryHelmManager) Upgrade(chartName, releaseName string, values map[string]string) error { 127 | arg := make([]string, 0, 9) 128 | arg = append(arg, "upgrade", releaseName, chartName) 129 | if len(values) > 0 { 130 | arg = append(arg, "--set", prepareArgsFromValues(values)) 131 | } 132 | _, err := m.executeUsingHelmInNamespace(arg...) 133 | return err 134 | } 135 | 136 | func (m *BinaryHelmManager) Rollback(releaseName string, revision int) error { 137 | arg := make([]string, 0, 6) 138 | arg = append(arg, "rollback", releaseName, strconv.Itoa(revision), "--tiller-namespace", m.Namespace) 139 | _, err := m.executeUsingHelm(arg...) 140 | return err 141 | } 142 | 143 | func (m *BinaryHelmManager) executeUsingHelmInNamespace(arg ...string) (string, error) { 144 | arg = append(arg, "--namespace", m.Namespace, "--tiller-namespace", m.Namespace) 145 | return m.executeUsingHelm(arg...) 146 | } 147 | 148 | func (m *BinaryHelmManager) executeUsingHelm(arg ...string) (string, error) { 149 | return m.executeUsingBinary(m.HelmBin, arg...) 150 | } 151 | 152 | func (m *BinaryHelmManager) executeUsingKubectl(arg ...string) (string, error) { 153 | return m.executeUsingBinary(m.KubectlBin, arg...) 154 | } 155 | 156 | func (m *BinaryHelmManager) executeUsingBinary(binary string, arg ...string) (string, error) { 157 | cmd := exec.Command(binary, arg...) 158 | Logf("Running command %+v\n", cmd.Args) 159 | stdout, err := cmd.Output() 160 | if err != nil { 161 | switch err.(type) { 162 | case *exec.ExitError: 163 | stderr := err.(*exec.ExitError) 164 | Logf("Command %+v, Err %s\n", cmd.Args, stderr.Stderr) 165 | case *exec.Error: 166 | Logf("Command %+v, Err %s\n", cmd.Args, err) 167 | } 168 | return "", err 169 | } 170 | return string(stdout), nil 171 | } 172 | 173 | func (m *BinaryHelmManager) executeCommandWithValues(releaseName, command string, values map[string]string) (string, error) { 174 | arg := make([]string, 0, 8) 175 | arg = append(arg, command, releaseName) 176 | if len(values) > 0 { 177 | var b bytes.Buffer 178 | for key, val := range values { 179 | b.WriteString(key) 180 | b.WriteString("=") 181 | b.WriteString(val) 182 | b.WriteString(",") 183 | } 184 | arg = append(arg, "--set", b.String()) 185 | } 186 | return m.executeUsingHelmInNamespace(arg...) 187 | } 188 | 189 | func regexpKeyFromStructuredOutput(key, output string) string { 190 | r := regexp.MustCompile(fmt.Sprintf("%v:[[:space:]]*(.*)", key)) 191 | // key will be captured in group with index 1 192 | result := r.FindStringSubmatch(output) 193 | if len(result) < 2 { 194 | return "" 195 | } 196 | return result[1] 197 | } 198 | 199 | func prepareRudder(clientset kubernetes.Interface, namespace string) { 200 | rudder := &v1.Pod{ 201 | ObjectMeta: metav1.ObjectMeta{ 202 | Name: rudderPodName, 203 | }, 204 | Spec: v1.PodSpec{ 205 | RestartPolicy: "Always", 206 | Containers: []v1.Container{ 207 | { 208 | Name: "rudder-appcontroller", 209 | Image: "helm/rudder-appcontroller", 210 | ImagePullPolicy: v1.PullNever, 211 | }, 212 | }, 213 | }, 214 | } 215 | _, err := clientset.Core().Pods(namespace).Create(rudder) 216 | Expect(err).NotTo(HaveOccurred()) 217 | WaitForPod(clientset, namespace, rudderPodName, v1.PodRunning) 218 | } 219 | 220 | func deleteRudder(clientset kubernetes.Interface, namespace string) error { 221 | return clientset.Core().Pods(namespace).Delete(rudderPodName, nil) 222 | } 223 | 224 | func getNameFromHelmOutput(output string) string { 225 | return regexpKeyFromStructuredOutput("NAME", output) 226 | } 227 | 228 | func getStatusFromHelmOutput(output string) string { 229 | return regexpKeyFromStructuredOutput("STATUS", output) 230 | } 231 | 232 | func waitTillerPod(clientset kubernetes.Interface, namespace string) { 233 | Eventually(func() bool { 234 | pods, err := clientset.Core().Pods(namespace).List(metav1.ListOptions{}) 235 | if err != nil { 236 | return false 237 | } 238 | for _, pod := range pods.Items { 239 | if !strings.Contains(pod.Name, "tiller") { 240 | continue 241 | } 242 | Logf("Found tiller pod. Phase %v\n", pod.Status.Phase) 243 | if pod.Status.Phase != v1.PodRunning { 244 | return false 245 | } 246 | for _, cond := range pod.Status.Conditions { 247 | if cond.Type != v1.PodReady { 248 | continue 249 | } 250 | return cond.Status == v1.ConditionTrue 251 | } 252 | } 253 | return false 254 | }, 2*time.Minute, 5*time.Second).Should(BeTrue(), "tiller pod is not running in namespace "+namespace) 255 | } 256 | 257 | func prepareArgsFromValues(values map[string]string) string { 258 | var b bytes.Buffer 259 | for key, val := range values { 260 | b.WriteString(key) 261 | b.WriteString("=") 262 | b.WriteString(val) 263 | b.WriteString(",") 264 | } 265 | return b.String() 266 | } 267 | -------------------------------------------------------------------------------- /e2e/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Mirantis 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package e2e 16 | 17 | import ( 18 | "flag" 19 | "fmt" 20 | 21 | . "github.com/onsi/ginkgo" 22 | . "github.com/onsi/gomega" 23 | 24 | "time" 25 | 26 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | "k8s.io/client-go/kubernetes" 28 | "k8s.io/client-go/pkg/api/v1" 29 | "k8s.io/client-go/rest" 30 | "k8s.io/client-go/tools/clientcmd" 31 | ) 32 | 33 | // TODO move this variables under single object TestContext 34 | var url string 35 | var enableRudder bool 36 | 37 | func init() { 38 | flag.StringVar(&url, "cluster-url", "http://127.0.0.1:8080", "apiserver address to use with restclient") 39 | flag.BoolVar(&enableRudder, "use-rudder", false, "Use to enable rudder") 40 | } 41 | 42 | func LoadConfig() *rest.Config { 43 | config, err := clientcmd.BuildConfigFromFlags(url, "") 44 | Expect(err).NotTo(HaveOccurred()) 45 | return config 46 | } 47 | 48 | func KubeClient() (*kubernetes.Clientset, error) { 49 | config := LoadConfig() 50 | clientset, err := kubernetes.NewForConfig(config) 51 | Expect(err).NotTo(HaveOccurred()) 52 | return clientset, nil 53 | } 54 | 55 | func DeleteNS(clientset kubernetes.Interface, namespace *v1.Namespace) { 56 | defer GinkgoRecover() 57 | pods, err := clientset.Core().Pods(namespace.Name).List(metav1.ListOptions{}) 58 | Expect(err).NotTo(HaveOccurred()) 59 | for _, pod := range pods.Items { 60 | clientset.Core().Pods(namespace.Name).Delete(pod.Name, nil) 61 | } 62 | clientset.Core().Namespaces().Delete(namespace.Name, nil) 63 | } 64 | 65 | func Logf(format string, a ...interface{}) { 66 | fmt.Fprintf(GinkgoWriter, format, a...) 67 | } 68 | 69 | func WaitForPod(clientset kubernetes.Interface, namespace string, name string, phase v1.PodPhase) *v1.Pod { 70 | defer GinkgoRecover() 71 | var podUpdated *v1.Pod 72 | Eventually(func() error { 73 | podUpdated, err := clientset.Core().Pods(namespace).Get(name, metav1.GetOptions{}) 74 | if err != nil { 75 | return err 76 | } 77 | if phase != "" && podUpdated.Status.Phase != phase { 78 | return fmt.Errorf("pod %v is not %v phase: %v", podUpdated.Name, phase, podUpdated.Status.Phase) 79 | } 80 | return nil 81 | }, 1*time.Minute, 3*time.Second).Should(BeNil()) 82 | return podUpdated 83 | } 84 | -------------------------------------------------------------------------------- /examples/wp-values.yaml: -------------------------------------------------------------------------------- 1 | replace: 2 | - from: "(- name: MARIADB_HOST.*\n)( * value: )(.*)(\n)" 3 | to: "$1$2$3.{{ index .ObjectMeta.Annotations \"federation.alpha.kubernetes.io/federation-name\" }}\n" 4 | fed-namespace: federation-system 5 | fed-controller-name: federation-controller-manager 6 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: 63102d1defe588fde5efd036c124764fbec4896cdf56c478bd46cec97ae6cd40 2 | updated: 2017-07-27T13:16:26.824353994+02:00 3 | imports: 4 | - name: bitbucket.org/ww/goautoneg 5 | version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675 6 | - name: cloud.google.com/go 7 | version: 3b1ae45394a234c385be014e9a488f2bb6eef821 8 | subpackages: 9 | - compute/metadata 10 | - internal 11 | - name: github.com/aokoli/goutils 12 | version: 3391d3790d23d03408670993e957e8f408993c34 13 | - name: github.com/Azure/go-ansiterm 14 | version: 70b2c90b260171e829f1ebd7c17f600c11858dbe 15 | subpackages: 16 | - winterm 17 | - name: github.com/beorn7/perks 18 | version: 3ac7bf7a47d159a033b107610db8a1b6575507a4 19 | subpackages: 20 | - quantile 21 | - name: github.com/BurntSushi/toml 22 | version: b26d9c308763d68093482582cea63d69be07a0f0 23 | - name: github.com/coreos/go-oidc 24 | version: be73733bb8cc830d0205609b95d125215f8e9c70 25 | subpackages: 26 | - http 27 | - jose 28 | - key 29 | - oauth2 30 | - oidc 31 | - name: github.com/coreos/pkg 32 | version: fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8 33 | subpackages: 34 | - capnslog 35 | - dlopen 36 | - health 37 | - httputil 38 | - timeutil 39 | - name: github.com/davecgh/go-spew 40 | version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d 41 | subpackages: 42 | - spew 43 | - name: github.com/dgrijalva/jwt-go 44 | version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20 45 | - name: github.com/docker/distribution 46 | version: 03efb43768979f4d2ea5187bef995656441829e5 47 | subpackages: 48 | - digest 49 | - reference 50 | - name: github.com/docker/docker 51 | version: b9f10c951893f9a00865890a5232e85d770c1087 52 | subpackages: 53 | - pkg/jsonlog 54 | - pkg/jsonmessage 55 | - pkg/longpath 56 | - pkg/mount 57 | - pkg/stdcopy 58 | - pkg/symlink 59 | - pkg/system 60 | - pkg/term 61 | - pkg/term/windows 62 | - name: github.com/docker/engine-api 63 | version: dea108d3aa0c67d7162a3fd8aa65f38a430019fd 64 | subpackages: 65 | - client 66 | - client/transport 67 | - client/transport/cancellable 68 | - types 69 | - types/blkiodev 70 | - types/container 71 | - types/filters 72 | - types/network 73 | - types/reference 74 | - types/registry 75 | - types/strslice 76 | - types/time 77 | - types/versions 78 | - name: github.com/docker/go-connections 79 | version: f549a9393d05688dff0992ef3efd8bbe6c628aeb 80 | subpackages: 81 | - nat 82 | - sockets 83 | - tlsconfig 84 | - name: github.com/docker/go-units 85 | version: e30f1e79f3cd72542f2026ceec18d3bd67ab859c 86 | - name: github.com/docker/spdystream 87 | version: 449fdfce4d962303d702fec724ef0ad181c92528 88 | subpackages: 89 | - spdy 90 | - name: github.com/emicklei/go-restful 91 | version: 09691a3b6378b740595c1002f40c34dd5f218a22 92 | subpackages: 93 | - log 94 | - swagger 95 | - name: github.com/evanphx/json-patch 96 | version: ba18e35c5c1b36ef6334cad706eb681153d2d379 97 | - name: github.com/exponent-io/jsonpath 98 | version: d6023ce2651d8eafb5c75bb0c7167536102ec9f5 99 | - name: github.com/facebookgo/symwalk 100 | version: 42004b9f322246749dd73ad71008b1f3160c0052 101 | - name: github.com/ghodss/yaml 102 | version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee 103 | - name: github.com/go-openapi/jsonpointer 104 | version: 46af16f9f7b149af66e5d1bd010e3574dc06de98 105 | - name: github.com/go-openapi/jsonreference 106 | version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272 107 | - name: github.com/go-openapi/spec 108 | version: 6aced65f8501fe1217321abf0749d354824ba2ff 109 | - name: github.com/go-openapi/swag 110 | version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72 111 | - name: github.com/gobwas/glob 112 | version: bea32b9cd2d6f55753d94a28e959b13f0244797a 113 | subpackages: 114 | - compiler 115 | - match 116 | - syntax 117 | - syntax/ast 118 | - syntax/lexer 119 | - util/runes 120 | - util/strings 121 | - name: github.com/gogo/protobuf 122 | version: e18d7aa8f8c624c915db340349aad4c49b10d173 123 | subpackages: 124 | - gogoproto 125 | - plugin/compare 126 | - plugin/defaultcheck 127 | - plugin/description 128 | - plugin/embedcheck 129 | - plugin/enumstringer 130 | - plugin/equal 131 | - plugin/face 132 | - plugin/gostring 133 | - plugin/marshalto 134 | - plugin/oneofcheck 135 | - plugin/populate 136 | - plugin/size 137 | - plugin/stringer 138 | - plugin/testgen 139 | - plugin/union 140 | - plugin/unmarshal 141 | - proto 142 | - protoc-gen-gogo/descriptor 143 | - protoc-gen-gogo/generator 144 | - protoc-gen-gogo/grpc 145 | - protoc-gen-gogo/plugin 146 | - sortkeys 147 | - vanity 148 | - vanity/command 149 | - name: github.com/golang/glog 150 | version: 44145f04b68cf362d9c4df2182967c2275eaefed 151 | - name: github.com/golang/groupcache 152 | version: 02826c3e79038b59d737d3b1c0a1d937f71a4433 153 | subpackages: 154 | - lru 155 | - name: github.com/golang/protobuf 156 | version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef 157 | subpackages: 158 | - proto 159 | - ptypes/any 160 | - ptypes/timestamp 161 | - name: github.com/google/gofuzz 162 | version: 44d81051d367757e1c7c6a5a86423ece9afcf63c 163 | - name: github.com/grpc-ecosystem/go-grpc-prometheus 164 | version: 0c1b191dbfe51efdabe3c14b9f6f3b96429e0722 165 | - name: github.com/howeyc/gopass 166 | version: 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d 167 | - name: github.com/huandu/xstrings 168 | version: 3959339b333561bf62a38b424fd41517c2c90f40 169 | - name: github.com/imdario/mergo 170 | version: 6633656539c1639d9d78127b7d47c622b5d7b6dc 171 | - name: github.com/inconshreveable/mousetrap 172 | version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 173 | - name: github.com/jonboulle/clockwork 174 | version: 72f9bd7c4e0c2a40055ab3d0f09654f730cce982 175 | - name: github.com/juju/ratelimit 176 | version: 77ed1c8a01217656d2080ad51981f6e99adaa177 177 | - name: github.com/mailru/easyjson 178 | version: d5b7844b561a7bc640052f1b935f7b800330d7e0 179 | subpackages: 180 | - buffer 181 | - jlexer 182 | - jwriter 183 | - name: github.com/Masterminds/semver 184 | version: 3f0ab6d4ab4bed1c61caf056b63a6e62190c7801 185 | - name: github.com/Masterminds/sprig 186 | version: 9526be0327b26ad31aa70296a7b10704883976d5 187 | - name: github.com/matttproud/golang_protobuf_extensions 188 | version: fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a 189 | subpackages: 190 | - pbutil 191 | - name: github.com/mitchellh/go-wordwrap 192 | version: ad45545899c7b13c020ea92b2072220eefad42b8 193 | - name: github.com/onsi/ginkgo 194 | version: bb93381d543b0e5725244abe752214a110791d01 195 | subpackages: 196 | - config 197 | - ginkgo 198 | - ginkgo/convert 199 | - ginkgo/interrupthandler 200 | - ginkgo/nodot 201 | - ginkgo/testrunner 202 | - ginkgo/testsuite 203 | - ginkgo/watch 204 | - internal/codelocation 205 | - internal/containernode 206 | - internal/failer 207 | - internal/leafnodes 208 | - internal/remote 209 | - internal/spec 210 | - internal/specrunner 211 | - internal/suite 212 | - internal/testingtproxy 213 | - internal/writer 214 | - reporters 215 | - reporters/stenographer 216 | - reporters/stenographer/support/go-colorable 217 | - reporters/stenographer/support/go-isatty 218 | - types 219 | - name: github.com/onsi/gomega 220 | version: d59fa0ac68bb5dd932ee8d24eed631cdd519efc3 221 | subpackages: 222 | - format 223 | - gstruct 224 | - gstruct/errors 225 | - internal/assertion 226 | - internal/asyncassertion 227 | - internal/oraclematcher 228 | - internal/testingtsupport 229 | - matchers 230 | - matchers/support/goraph/bipartitegraph 231 | - matchers/support/goraph/edge 232 | - matchers/support/goraph/node 233 | - matchers/support/goraph/util 234 | - types 235 | - name: github.com/pborman/uuid 236 | version: ca53cad383cad2479bbba7f7a1a05797ec1386e4 237 | - name: github.com/prometheus/client_golang 238 | version: c5b7fccd204277076155f10851dad72b76a49317 239 | subpackages: 240 | - prometheus 241 | - name: github.com/prometheus/client_model 242 | version: fa8ad6fec33561be4280a8f0514318c79d7f6cb6 243 | subpackages: 244 | - go 245 | - name: github.com/prometheus/common 246 | version: ffe929a3f4c4faeaa10f2b9535c2b1be3ad15650 247 | subpackages: 248 | - expfmt 249 | - model 250 | - name: github.com/prometheus/procfs 251 | version: 454a56f35412459b5e684fd5ec0f9211b94f002a 252 | - name: github.com/PuerkitoBio/purell 253 | version: 8a290539e2e8629dbc4e6bad948158f790ec31f4 254 | - name: github.com/PuerkitoBio/urlesc 255 | version: 5bd2802263f21d8788851d5305584c82a5c75d7e 256 | - name: github.com/satori/go.uuid 257 | version: 879c5887cd475cd7864858769793b2ceb0d44feb 258 | - name: github.com/Sirupsen/logrus 259 | version: 51fe59aca108dc5680109e7b2051cbdcfa5a253c 260 | - name: github.com/spf13/cobra 261 | version: f62e98d28ab7ad31d707ba837a966378465c7b57 262 | - name: github.com/spf13/pflag 263 | version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7 264 | - name: github.com/technosophos/moniker 265 | version: ab470f5e105a44d0c87ea21bacd6a335c4816d83 266 | - name: github.com/ugorji/go 267 | version: ded73eae5db7e7a0ef6f55aace87a2873c5d2b74 268 | subpackages: 269 | - codec 270 | - codec/codecgen 271 | - name: golang.org/x/crypto 272 | version: d172538b2cfce0c13cee31e647d0367aa8cd2486 273 | subpackages: 274 | - bcrypt 275 | - blowfish 276 | - curve25519 277 | - ed25519 278 | - ed25519/internal/edwards25519 279 | - pbkdf2 280 | - pkcs12 281 | - pkcs12/internal/rc2 282 | - scrypt 283 | - ssh 284 | - ssh/terminal 285 | - name: golang.org/x/net 286 | version: ab5485076ff3407ad2d02db054635913f017b0ed 287 | subpackages: 288 | - context 289 | - context/ctxhttp 290 | - http2 291 | - http2/hpack 292 | - idna 293 | - internal/timeseries 294 | - lex/httplex 295 | - trace 296 | - websocket 297 | - name: golang.org/x/oauth2 298 | version: 3c3a985cb79f52a3190fbc056984415ca6763d01 299 | subpackages: 300 | - google 301 | - internal 302 | - jws 303 | - jwt 304 | - name: golang.org/x/sys 305 | version: 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9 306 | subpackages: 307 | - unix 308 | - name: golang.org/x/text 309 | version: 19d0677862a6087f8898b4ae8f92aa30213be8ab 310 | subpackages: 311 | - cases 312 | - encoding 313 | - encoding/internal 314 | - encoding/internal/identifier 315 | - encoding/unicode 316 | - internal/tag 317 | - internal/utf8internal 318 | - language 319 | - runes 320 | - secure/bidirule 321 | - secure/precis 322 | - transform 323 | - unicode/bidi 324 | - unicode/norm 325 | - width 326 | - name: google.golang.org/appengine 327 | version: 4f7eeb5305a4ba1966344836ba4af9996b7b4e05 328 | subpackages: 329 | - internal 330 | - internal/app_identity 331 | - internal/base 332 | - internal/datastore 333 | - internal/log 334 | - internal/modules 335 | - internal/remote_api 336 | - internal/urlfetch 337 | - urlfetch 338 | - name: google.golang.org/genproto 339 | version: b0a3dcfcd1a9bd48e63634bd8802960804cf8315 340 | subpackages: 341 | - googleapis/rpc/status 342 | - name: google.golang.org/grpc 343 | version: 172ccacff3cfbddedcf1ba5ea06cda943bc976c1 344 | subpackages: 345 | - codes 346 | - credentials 347 | - grpclb/grpc_lb_v1 348 | - grpclog 349 | - internal 350 | - keepalive 351 | - metadata 352 | - naming 353 | - peer 354 | - stats 355 | - status 356 | - tap 357 | - transport 358 | - name: gopkg.in/inf.v0 359 | version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 360 | - name: gopkg.in/yaml.v2 361 | version: 53feefa2559fb8dfa8d81baad31be332c97d6c77 362 | - name: k8s.io/apimachinery 363 | version: 85ace5365f33b16fc735c866a12e3c765b9700f2 364 | subpackages: 365 | - pkg/api/equality 366 | - pkg/api/errors 367 | - pkg/api/meta 368 | - pkg/api/resource 369 | - pkg/api/validation 370 | - pkg/apimachinery 371 | - pkg/apimachinery/announced 372 | - pkg/apimachinery/registered 373 | - pkg/apis/meta/v1 374 | - pkg/apis/meta/v1/unstructured 375 | - pkg/apis/meta/v1/validation 376 | - pkg/conversion 377 | - pkg/conversion/queryparams 378 | - pkg/fields 379 | - pkg/labels 380 | - pkg/openapi 381 | - pkg/runtime 382 | - pkg/runtime/schema 383 | - pkg/runtime/serializer 384 | - pkg/runtime/serializer/json 385 | - pkg/runtime/serializer/protobuf 386 | - pkg/runtime/serializer/recognizer 387 | - pkg/runtime/serializer/streaming 388 | - pkg/runtime/serializer/versioning 389 | - pkg/selection 390 | - pkg/types 391 | - pkg/util/diff 392 | - pkg/util/errors 393 | - pkg/util/framer 394 | - pkg/util/httpstream 395 | - pkg/util/httpstream/spdy 396 | - pkg/util/intstr 397 | - pkg/util/json 398 | - pkg/util/mergepatch 399 | - pkg/util/net 400 | - pkg/util/rand 401 | - pkg/util/runtime 402 | - pkg/util/sets 403 | - pkg/util/strategicpatch 404 | - pkg/util/uuid 405 | - pkg/util/validation 406 | - pkg/util/validation/field 407 | - pkg/util/wait 408 | - pkg/util/yaml 409 | - pkg/version 410 | - pkg/watch 411 | - third_party/forked/golang/json 412 | - third_party/forked/golang/netutil 413 | - third_party/forked/golang/reflect 414 | - name: k8s.io/apiserver 415 | version: d7bba18199202b51f78c048c2a8404772007f0df 416 | subpackages: 417 | - pkg/authentication/authenticator 418 | - pkg/authentication/serviceaccount 419 | - pkg/authentication/user 420 | - pkg/features 421 | - pkg/server/httplog 422 | - pkg/util/feature 423 | - pkg/util/flag 424 | - pkg/util/wsstream 425 | - name: k8s.io/client-go 426 | version: 21300e3e11c918b8e6a70fb7293b310683d6c046 427 | subpackages: 428 | - discovery 429 | - dynamic 430 | - kubernetes 431 | - kubernetes/scheme 432 | - kubernetes/typed/apps/v1beta1 433 | - kubernetes/typed/authentication/v1 434 | - kubernetes/typed/authentication/v1beta1 435 | - kubernetes/typed/authorization/v1 436 | - kubernetes/typed/authorization/v1beta1 437 | - kubernetes/typed/autoscaling/v1 438 | - kubernetes/typed/autoscaling/v2alpha1 439 | - kubernetes/typed/batch/v1 440 | - kubernetes/typed/batch/v2alpha1 441 | - kubernetes/typed/certificates/v1beta1 442 | - kubernetes/typed/core/v1 443 | - kubernetes/typed/extensions/v1beta1 444 | - kubernetes/typed/policy/v1beta1 445 | - kubernetes/typed/rbac/v1alpha1 446 | - kubernetes/typed/rbac/v1beta1 447 | - kubernetes/typed/settings/v1alpha1 448 | - kubernetes/typed/storage/v1 449 | - kubernetes/typed/storage/v1beta1 450 | - pkg/api 451 | - pkg/api/install 452 | - pkg/api/v1 453 | - pkg/apis/apps 454 | - pkg/apis/apps/install 455 | - pkg/apis/apps/v1beta1 456 | - pkg/apis/authentication 457 | - pkg/apis/authentication/install 458 | - pkg/apis/authentication/v1 459 | - pkg/apis/authentication/v1beta1 460 | - pkg/apis/authorization 461 | - pkg/apis/authorization/install 462 | - pkg/apis/authorization/v1 463 | - pkg/apis/authorization/v1beta1 464 | - pkg/apis/autoscaling 465 | - pkg/apis/autoscaling/install 466 | - pkg/apis/autoscaling/v1 467 | - pkg/apis/autoscaling/v2alpha1 468 | - pkg/apis/batch 469 | - pkg/apis/batch/install 470 | - pkg/apis/batch/v1 471 | - pkg/apis/batch/v2alpha1 472 | - pkg/apis/certificates 473 | - pkg/apis/certificates/install 474 | - pkg/apis/certificates/v1beta1 475 | - pkg/apis/extensions 476 | - pkg/apis/extensions/install 477 | - pkg/apis/extensions/v1beta1 478 | - pkg/apis/policy 479 | - pkg/apis/policy/install 480 | - pkg/apis/policy/v1beta1 481 | - pkg/apis/rbac 482 | - pkg/apis/rbac/install 483 | - pkg/apis/rbac/v1alpha1 484 | - pkg/apis/rbac/v1beta1 485 | - pkg/apis/settings 486 | - pkg/apis/settings/install 487 | - pkg/apis/settings/v1alpha1 488 | - pkg/apis/storage 489 | - pkg/apis/storage/install 490 | - pkg/apis/storage/v1 491 | - pkg/apis/storage/v1beta1 492 | - pkg/util 493 | - pkg/util/parsers 494 | - pkg/version 495 | - plugin/pkg/client/auth 496 | - plugin/pkg/client/auth/gcp 497 | - plugin/pkg/client/auth/oidc 498 | - rest 499 | - rest/watch 500 | - third_party/forked/golang/template 501 | - tools/auth 502 | - tools/cache 503 | - tools/clientcmd 504 | - tools/clientcmd/api 505 | - tools/clientcmd/api/latest 506 | - tools/clientcmd/api/v1 507 | - tools/metrics 508 | - tools/portforward 509 | - tools/record 510 | - transport 511 | - util/cert 512 | - util/clock 513 | - util/flowcontrol 514 | - util/homedir 515 | - util/integer 516 | - util/jsonpath 517 | - name: k8s.io/helm 518 | version: 7cf31e8d9a026287041bae077b09165be247ae66 519 | subpackages: 520 | - pkg/chartutil 521 | - pkg/engine 522 | - pkg/hooks 523 | - pkg/ignore 524 | - pkg/kube 525 | - pkg/proto/hapi/chart 526 | - pkg/proto/hapi/release 527 | - pkg/proto/hapi/rudder 528 | - pkg/proto/hapi/services 529 | - pkg/proto/hapi/version 530 | - pkg/releasetesting 531 | - pkg/releaseutil 532 | - pkg/rudder 533 | - pkg/storage 534 | - pkg/storage/driver 535 | - pkg/tiller 536 | - pkg/tiller/environment 537 | - pkg/timeconv 538 | - pkg/version 539 | - name: k8s.io/kubernetes 540 | version: 313fd317f96265658831a576fafdd6ed85aaf428 541 | subpackages: 542 | - federation/apis/federation 543 | - federation/apis/federation/install 544 | - federation/apis/federation/v1beta1 545 | - federation/client/clientset_generated/federation_internalclientset 546 | - federation/client/clientset_generated/federation_internalclientset/scheme 547 | - federation/client/clientset_generated/federation_internalclientset/typed/autoscaling/internalversion 548 | - federation/client/clientset_generated/federation_internalclientset/typed/batch/internalversion 549 | - federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion 550 | - federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion 551 | - federation/client/clientset_generated/federation_internalclientset/typed/federation/internalversion 552 | - pkg/api 553 | - pkg/api/annotations 554 | - pkg/api/events 555 | - pkg/api/install 556 | - pkg/api/pod 557 | - pkg/api/service 558 | - pkg/api/util 559 | - pkg/api/v1 560 | - pkg/api/validation 561 | - pkg/apis/apps 562 | - pkg/apis/apps/install 563 | - pkg/apis/apps/v1beta1 564 | - pkg/apis/authentication 565 | - pkg/apis/authentication/install 566 | - pkg/apis/authentication/v1 567 | - pkg/apis/authentication/v1beta1 568 | - pkg/apis/authorization 569 | - pkg/apis/authorization/install 570 | - pkg/apis/authorization/v1 571 | - pkg/apis/authorization/v1beta1 572 | - pkg/apis/autoscaling 573 | - pkg/apis/autoscaling/install 574 | - pkg/apis/autoscaling/v1 575 | - pkg/apis/autoscaling/v2alpha1 576 | - pkg/apis/batch 577 | - pkg/apis/batch/install 578 | - pkg/apis/batch/v1 579 | - pkg/apis/batch/v2alpha1 580 | - pkg/apis/certificates 581 | - pkg/apis/certificates/install 582 | - pkg/apis/certificates/v1beta1 583 | - pkg/apis/componentconfig 584 | - pkg/apis/componentconfig/install 585 | - pkg/apis/componentconfig/v1alpha1 586 | - pkg/apis/extensions 587 | - pkg/apis/extensions/install 588 | - pkg/apis/extensions/v1beta1 589 | - pkg/apis/policy 590 | - pkg/apis/policy/install 591 | - pkg/apis/policy/v1beta1 592 | - pkg/apis/rbac 593 | - pkg/apis/rbac/install 594 | - pkg/apis/rbac/v1alpha1 595 | - pkg/apis/rbac/v1beta1 596 | - pkg/apis/settings 597 | - pkg/apis/settings/install 598 | - pkg/apis/settings/v1alpha1 599 | - pkg/apis/storage 600 | - pkg/apis/storage/install 601 | - pkg/apis/storage/util 602 | - pkg/apis/storage/v1 603 | - pkg/apis/storage/v1beta1 604 | - pkg/capabilities 605 | - pkg/client/clientset_generated/clientset 606 | - pkg/client/clientset_generated/clientset/scheme 607 | - pkg/client/clientset_generated/clientset/typed/apps/v1beta1 608 | - pkg/client/clientset_generated/clientset/typed/authentication/v1 609 | - pkg/client/clientset_generated/clientset/typed/authentication/v1beta1 610 | - pkg/client/clientset_generated/clientset/typed/authorization/v1 611 | - pkg/client/clientset_generated/clientset/typed/authorization/v1beta1 612 | - pkg/client/clientset_generated/clientset/typed/autoscaling/v1 613 | - pkg/client/clientset_generated/clientset/typed/autoscaling/v2alpha1 614 | - pkg/client/clientset_generated/clientset/typed/batch/v1 615 | - pkg/client/clientset_generated/clientset/typed/batch/v2alpha1 616 | - pkg/client/clientset_generated/clientset/typed/certificates/v1beta1 617 | - pkg/client/clientset_generated/clientset/typed/core/v1 618 | - pkg/client/clientset_generated/clientset/typed/extensions/v1beta1 619 | - pkg/client/clientset_generated/clientset/typed/policy/v1beta1 620 | - pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1 621 | - pkg/client/clientset_generated/clientset/typed/rbac/v1beta1 622 | - pkg/client/clientset_generated/clientset/typed/settings/v1alpha1 623 | - pkg/client/clientset_generated/clientset/typed/storage/v1 624 | - pkg/client/clientset_generated/clientset/typed/storage/v1beta1 625 | - pkg/client/clientset_generated/internalclientset 626 | - pkg/client/clientset_generated/internalclientset/scheme 627 | - pkg/client/clientset_generated/internalclientset/typed/apps/internalversion 628 | - pkg/client/clientset_generated/internalclientset/typed/authentication/internalversion 629 | - pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion 630 | - pkg/client/clientset_generated/internalclientset/typed/autoscaling/internalversion 631 | - pkg/client/clientset_generated/internalclientset/typed/batch/internalversion 632 | - pkg/client/clientset_generated/internalclientset/typed/certificates/internalversion 633 | - pkg/client/clientset_generated/internalclientset/typed/core/internalversion 634 | - pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion 635 | - pkg/client/clientset_generated/internalclientset/typed/policy/internalversion 636 | - pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion 637 | - pkg/client/clientset_generated/internalclientset/typed/settings/internalversion 638 | - pkg/client/clientset_generated/internalclientset/typed/storage/internalversion 639 | - pkg/client/listers/core/v1 640 | - pkg/client/listers/extensions/v1beta1 641 | - pkg/client/retry 642 | - pkg/client/unversioned 643 | - pkg/client/unversioned/remotecommand 644 | - pkg/controller 645 | - pkg/controller/deployment/util 646 | - pkg/credentialprovider 647 | - pkg/features 648 | - pkg/fieldpath 649 | - pkg/kubectl 650 | - pkg/kubectl/cmd/util 651 | - pkg/kubectl/resource 652 | - pkg/kubelet/qos 653 | - pkg/kubelet/server/remotecommand 654 | - pkg/kubelet/types 655 | - pkg/master/ports 656 | - pkg/printers 657 | - pkg/printers/internalversion 658 | - pkg/security/apparmor 659 | - pkg/serviceaccount 660 | - pkg/util 661 | - pkg/util/exec 662 | - pkg/util/hash 663 | - pkg/util/interrupt 664 | - pkg/util/labels 665 | - pkg/util/net/sets 666 | - pkg/util/node 667 | - pkg/util/parsers 668 | - pkg/util/slice 669 | - pkg/util/term 670 | - pkg/version 671 | - name: vbom.ml/util 672 | version: 256737ac55c46798123f754ab7d2c784e2c71783 673 | repo: https://github.com/fvbommel/util.git 674 | vcs: git 675 | subpackages: 676 | - sortorder 677 | testImports: [] 678 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/kubernetes-helm/rudder-federation 2 | import: 3 | - package: k8s.io/helm 4 | version: release-2.5 5 | subpackages: 6 | - pkg/kube 7 | - pkg/proto/hapi/chart 8 | - pkg/proto/hapi/release 9 | - pkg/proto/hapi/rudder 10 | - pkg/releaseutil 11 | - pkg/rudder 12 | - pkg/tiller 13 | - pkg/version 14 | - package: golang.org/x/net 15 | version: release-branch.go1.9 16 | subpackages: 17 | - context 18 | - package: google.golang.org/grpc 19 | version: v1.5.0 20 | subpackages: 21 | - grpclog 22 | - package: github.com/ghodss/yaml 23 | - package: k8s.io/client-go 24 | version: release-3.0 25 | - package: golang.org/x/text 26 | version: 19d0677862a6087f8898b4ae8f92aa30213be8ab 27 | -------------------------------------------------------------------------------- /manifests/ruddered-tiller.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | annotations: 5 | deployment.kubernetes.io/revision: "1" 6 | generation: 1 7 | labels: 8 | app: helm 9 | name: tiller 10 | name: tiller-deploy 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | app: helm 16 | name: tiller 17 | strategy: 18 | rollingUpdate: 19 | maxSurge: 1 20 | maxUnavailable: 1 21 | type: RollingUpdate 22 | template: 23 | metadata: 24 | creationTimestamp: null 25 | labels: 26 | app: helm 27 | name: tiller 28 | spec: 29 | containers: 30 | - env: 31 | - name: TILLER_NAMESPACE 32 | value: kube-system 33 | image: gcr.io/kubernetes-helm/tiller:v2.6.0 34 | imagePullPolicy: Always 35 | livenessProbe: 36 | failureThreshold: 3 37 | httpGet: 38 | path: /liveness 39 | port: 44135 40 | scheme: HTTP 41 | initialDelaySeconds: 1 42 | periodSeconds: 10 43 | successThreshold: 1 44 | timeoutSeconds: 1 45 | name: tiller 46 | ports: 47 | - containerPort: 44134 48 | name: tiller 49 | protocol: TCP 50 | readinessProbe: 51 | failureThreshold: 3 52 | httpGet: 53 | path: /readiness 54 | port: 44135 55 | scheme: HTTP 56 | initialDelaySeconds: 1 57 | periodSeconds: 10 58 | successThreshold: 1 59 | timeoutSeconds: 1 60 | resources: {} 61 | terminationMessagePath: /dev/termination-log 62 | terminationMessagePolicy: File 63 | command: ["/tiller"] 64 | args: ["--experimental-release"] 65 | - env: 66 | - name: FEDERATION_HOST 67 | value: federation-apiserver.federation-system 68 | - name: GRPC_GO_LOG_SEVERITY_LEVEL 69 | value: info 70 | - name: RUDDER_NAMESPACE 71 | valueFrom: 72 | fieldRef: 73 | fieldPath: metadata.namespace 74 | image: mirantis/rudder-federation:v0.1 75 | imagePullPolicy: Never 76 | name: rudder 77 | ports: 78 | - containerPort: 10001 79 | name: rudder 80 | protocol: TCP 81 | resources: {} 82 | restartPolicy: Always 83 | -------------------------------------------------------------------------------- /pkg/federation/federation.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package federation 18 | 19 | import ( 20 | "bytes" 21 | "os" 22 | "regexp" 23 | "strings" 24 | "text/template" 25 | 26 | "google.golang.org/grpc/grpclog" 27 | 28 | "github.com/ghodss/yaml" 29 | 30 | "k8s.io/apimachinery/pkg/apis/meta/v1" 31 | "k8s.io/client-go/kubernetes" 32 | clientrest "k8s.io/client-go/rest" 33 | rest "k8s.io/client-go/rest" 34 | "k8s.io/client-go/tools/clientcmd" 35 | clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 36 | "k8s.io/kubernetes/federation/apis/federation" 37 | fedclient "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset" 38 | "k8s.io/kubernetes/pkg/apis/extensions" 39 | 40 | //"k8s.io/helm/pkg/chartutil" 41 | "k8s.io/helm/pkg/kube" 42 | rudderAPI "k8s.io/helm/pkg/proto/hapi/rudder" 43 | 44 | "github.com/kubernetes-helm/rudder-federation/pkg/releaseutil" 45 | ) 46 | 47 | func GetFederatedClusterClients(fed *fedclient.Clientset) (clients []*kube.Client, err error) { 48 | clusters, err := fed.Federation().Clusters().List(v1.ListOptions{}) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | for _, cluster := range clusters.Items { 54 | clients = append(clients, makeClient(cluster)) 55 | } 56 | 57 | return clients, nil 58 | } 59 | 60 | func makeClient(cluster federation.Cluster) *kube.Client { 61 | config := clientcmdapi.Config{ 62 | Clusters: map[string]*clientcmdapi.Cluster{ 63 | cluster.Name: &clientcmdapi.Cluster{ 64 | Server: cluster.Spec.ServerAddressByClientCIDRs[0].ServerAddress, 65 | InsecureSkipTLSVerify: true, 66 | }, 67 | }, 68 | Contexts: map[string]*clientcmdapi.Context{ 69 | cluster.Name: &clientcmdapi.Context{ 70 | Cluster: cluster.Name, 71 | }, 72 | }, 73 | CurrentContext: cluster.Name, 74 | } 75 | 76 | clientconfig := clientcmd.NewDefaultClientConfig(config, &clientcmd.ConfigOverrides{}) 77 | 78 | c := kube.New(clientconfig) 79 | c.Log = grpclog.Infof 80 | 81 | return c 82 | } 83 | 84 | func makeFedClient() *kube.Client { 85 | config := clientcmdapi.Config{ 86 | Clusters: map[string]*clientcmdapi.Cluster{ 87 | "federation": &clientcmdapi.Cluster{ 88 | Server: federationConfig.Host, 89 | CertificateAuthorityData: federationConfig.CAData, 90 | }, 91 | }, 92 | Contexts: map[string]*clientcmdapi.Context{ 93 | "federation": &clientcmdapi.Context{ 94 | Cluster: "federation", 95 | AuthInfo: "federation", 96 | }, 97 | }, 98 | CurrentContext: "federation", 99 | AuthInfos: map[string]*clientcmdapi.AuthInfo{ 100 | "federation": &clientcmdapi.AuthInfo{ 101 | ClientCertificateData: federationConfig.CertData, 102 | ClientKeyData: federationConfig.KeyData, 103 | Username: federationConfig.Username, 104 | Password: federationConfig.Password, 105 | }, 106 | }, 107 | } 108 | 109 | clientconfig := clientcmd.NewDefaultClientConfig(config, &clientcmd.ConfigOverrides{}) 110 | 111 | c := kube.New(clientconfig) 112 | c.Log = grpclog.Infof 113 | 114 | return c 115 | } 116 | 117 | //Map all object kinds supported by Federation API. Source: https://kubernetes.io/docs/reference/federation/ 118 | var federationKinds = map[string]bool{ 119 | "Cluster": true, 120 | "ClusterList": true, 121 | "ConfigMap": true, 122 | "ConfigMapList": true, 123 | "DaemonSet": true, 124 | "DaemonSetList": true, 125 | "Deployment": true, 126 | "DeploymentList": true, 127 | "DeploymentRollback": true, 128 | "Event": true, 129 | "EventList": true, 130 | "Ingress": true, 131 | "IngressList": true, 132 | "Namespace": true, 133 | "NamespaceList": true, 134 | "ReplicaSet": true, 135 | "ReplicaSetList": true, 136 | "Scale": true, 137 | "Secret": true, 138 | "SecretList": true, 139 | "Service": true, 140 | "ServiceList": true, 141 | } 142 | 143 | func SplitManifestForFed(manifest string) (fed string, local string, err error) { 144 | 145 | objects, err := releaseutil.SplitManifestsWithHeads(manifest) 146 | if err != nil { 147 | return 148 | } 149 | 150 | fed = "---" 151 | local = "---" 152 | 153 | for _, o := range objects { 154 | if federationKinds[o.Kind] { 155 | fed += "\n" + strings.Trim(o.Content, "- \t\n") + "\n---" 156 | } else { 157 | local += "\n" + strings.Trim(o.Content, "- \t\n") + "\n---" 158 | } 159 | } 160 | 161 | return 162 | } 163 | 164 | func CreateInFederation(manifest string, req *rudderAPI.InstallReleaseRequest) error { 165 | 166 | client := makeFedClient() 167 | 168 | return client.Create(req.Release.Namespace, bytes.NewBufferString(manifest), 500, false) 169 | } 170 | 171 | var federationConfig = &rest.Config{ 172 | Host: "http://example.host", 173 | } 174 | 175 | // GetFederationClient uses federationConfig, but it can be overwritten by federation-auth secret within the same namespace 176 | func GetFederationClient() (*fedclient.Clientset, error) { 177 | return fedclient.NewForConfig(federationConfig) 178 | } 179 | 180 | // GetAllClients returns federation clientset, helm federation client and helm clients for all federated clusters 181 | func GetAllClients() (*fedclient.Clientset, *kube.Client, []*kube.Client, error) { 182 | fedClientset, err := GetFederationClient() 183 | if err != nil { 184 | return nil, nil, nil, err 185 | } 186 | 187 | fedClient := makeFedClient() 188 | 189 | clients, err := GetFederatedClusterClients(fedClientset) 190 | 191 | return fedClientset, fedClient, clients, err 192 | } 193 | 194 | func populateFederationConfig() error { 195 | kubeconfig, err := clientrest.InClusterConfig() 196 | 197 | if err != nil { 198 | return err 199 | } 200 | 201 | clientset, err := kubernetes.NewForConfig(kubeconfig) 202 | 203 | if err != nil { 204 | return err 205 | } 206 | 207 | namespace := os.Getenv("RUDDER_NAMESPACE") 208 | if namespace == "" { 209 | namespace = "kube-system" 210 | } 211 | 212 | grpclog.Infof("Taking federations credentials from %s namespace", namespace) 213 | 214 | cm, err := clientset.Core().ConfigMaps(namespace).Get("federation-credentials", v1.GetOptions{}) 215 | 216 | if err != nil { 217 | return err 218 | } 219 | 220 | if cm.Data["type"] == "basic" { 221 | federationConfig.Username = cm.Data["username"] 222 | federationConfig.Password = cm.Data["password"] 223 | federationConfig.Host = cm.Data["host"] 224 | } else if cm.Data["type"] == "tls" { 225 | federationConfig.CAData = []byte(cm.Data["cadata"]) 226 | federationConfig.CertData = []byte(cm.Data["certdata"]) 227 | federationConfig.KeyData = []byte(cm.Data["keydata"]) 228 | 229 | federationConfig.Host = cm.Data["host"] 230 | } 231 | 232 | return err 233 | } 234 | 235 | func init() { 236 | populateFederationConfig() 237 | } 238 | 239 | type Replace struct { 240 | From string `json:"from"` 241 | To string `json:"to"` 242 | } 243 | type ReplaceExtract struct { 244 | Replace []Replace `json:"replace"` 245 | } 246 | 247 | func GetReplacements(req *rudderAPI.InstallReleaseRequest) []Replace { 248 | raw := req.Release.Config.Raw 249 | extractor := ReplaceExtract{} 250 | err := yaml.Unmarshal([]byte(raw), &extractor) 251 | if err != nil { 252 | grpclog.Warningln("Error while unmarshalling raw config: ", err) 253 | } 254 | 255 | return extractor.Replace 256 | } 257 | 258 | type DeploymentExtractor struct { 259 | Namespace string `json:"fed-namespace"` 260 | Name string `json:"fed-controller-name"` 261 | } 262 | 263 | func GetFederationControllerDeployment(req *rudderAPI.InstallReleaseRequest) (*extensions.Deployment, error) { 264 | raw := req.Release.Config.Raw 265 | extractor := DeploymentExtractor{ 266 | Namespace: "federation-system", 267 | Name: "federation-controller-manager", 268 | } 269 | err := yaml.Unmarshal([]byte(raw), &extractor) 270 | if err != nil { 271 | grpclog.Warningln("Error while unmarshalling raw config: ", err) 272 | } 273 | 274 | clientset, err := kube.New(nil).ClientSet() 275 | if err != nil { 276 | grpclog.Errorf("Cannot initialize Kubernetes connection: %s", err) 277 | return nil, err 278 | } 279 | 280 | dep, err := clientset.Extensions().Deployments(extractor.Namespace).Get(extractor.Name, v1.GetOptions{}) 281 | if err != nil { 282 | grpclog.Errorf("Cannot get deployment %s from ns %s: %v", extractor.Name, extractor.Namespace, err) 283 | } 284 | return dep, err 285 | } 286 | 287 | func ReplaceWithFederationDeployment(manifest string, replacements []Replace, controller *extensions.Deployment) (string, error) { 288 | for _, rep := range replacements { 289 | var tpl bytes.Buffer 290 | t, err := template.New("").Parse(rep.To) 291 | if err != nil { 292 | grpclog.Errorf("Could not parse template %s: %v", rep.To, err) 293 | return manifest, err 294 | } 295 | err = t.Execute(&tpl, controller) 296 | if err != nil { 297 | grpclog.Errorf("Could not execute template %s: %v", rep.To, err) 298 | return manifest, err 299 | } 300 | 301 | reg, err := regexp.Compile(rep.From) 302 | if err != nil { 303 | grpclog.Errorf("Could not compile regex %s: %v", rep.From, err) 304 | return manifest, err 305 | } 306 | 307 | manifest = reg.ReplaceAllString(manifest, tpl.String()) 308 | } 309 | return manifest, nil 310 | } 311 | -------------------------------------------------------------------------------- /pkg/federation/federation_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package federation 18 | 19 | import ( 20 | "strings" 21 | "testing" 22 | 23 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | "k8s.io/kubernetes/pkg/apis/extensions" 25 | ) 26 | 27 | func TestSplitManifestForFed(t *testing.T) { 28 | 29 | manifest := `--- 30 | apiVersion: v1 31 | kind: Secret 32 | metadata: 33 | name: wp4-mariadb 34 | type: Opaque 35 | data: 36 | mariadb-root-password: "" 37 | mariadb-password: "" 38 | --- 39 | apiVersion: v1 40 | kind: ConfigMap 41 | metadata: 42 | name: wp4-mariadb 43 | data: 44 | my.cnf: "trolo" 45 | --- 46 | kind: PersistentVolumeClaim 47 | apiVersion: v1 48 | metadata: 49 | name: wp4-wordpress 50 | spec: 51 | accessModes: 52 | - "ReadWriteOnce" 53 | resources: 54 | requests: 55 | storage: "10Gi" 56 | --- 57 | apiVersion: extensions/v1beta1 58 | kind: Deployment 59 | metadata: 60 | name: wp4-wordpress 61 | spec: 62 | replicas: 1 63 | ---` 64 | 65 | expectedFederated := `--- 66 | apiVersion: v1 67 | kind: Secret 68 | metadata: 69 | name: wp4-mariadb 70 | type: Opaque 71 | data: 72 | mariadb-root-password: "" 73 | mariadb-password: "" 74 | --- 75 | apiVersion: v1 76 | kind: ConfigMap 77 | metadata: 78 | name: wp4-mariadb 79 | data: 80 | my.cnf: "trolo" 81 | --- 82 | apiVersion: extensions/v1beta1 83 | kind: Deployment 84 | metadata: 85 | name: wp4-wordpress 86 | spec: 87 | replicas: 1 88 | ---` 89 | 90 | expectedLocal := `--- 91 | kind: PersistentVolumeClaim 92 | apiVersion: v1 93 | metadata: 94 | name: wp4-wordpress 95 | spec: 96 | accessModes: 97 | - "ReadWriteOnce" 98 | resources: 99 | requests: 100 | storage: "10Gi" 101 | ---` 102 | 103 | federated, local, err := SplitManifestForFed(manifest) 104 | 105 | if err != nil { 106 | t.Errorf("error not nil, got %v", err) 107 | } 108 | 109 | if federated != expectedFederated { 110 | t.Errorf("federated other than expected. expected:\n%v\ngot:\n%v", expectedFederated, federated) 111 | } 112 | 113 | if local != expectedLocal { 114 | t.Errorf("local other than expected. expected:\n%v\ngot:\n%v", expectedLocal, local) 115 | } 116 | } 117 | 118 | var manifest = `--- 119 | apiVersion: v1 120 | kind: Secret 121 | metadata: 122 | name: wp4-mariadb 123 | type: Opaque 124 | data: 125 | mariadb-root-password: "" 126 | mariadb-password: "" 127 | --- 128 | ` 129 | 130 | var deployment = extensions.Deployment{ 131 | ObjectMeta: metav1.ObjectMeta{ 132 | Annotations: map[string]string{ 133 | "federation.alpha.kubernetes.io/federation-name": "federation", 134 | "federations": "federation=example.com", 135 | }, 136 | }, 137 | } 138 | 139 | func TestReplaceWithFederationDeploymentStaySame(t *testing.T) { 140 | replacements := []Replace{ 141 | Replace{ 142 | From: "non-existent", 143 | To: "non-existen", 144 | }, 145 | } 146 | replaced, err := ReplaceWithFederationDeployment(manifest, replacements, &deployment) 147 | 148 | if err != nil { 149 | t.Fatalf("Expected no errors, got %v", err) 150 | } 151 | 152 | if replaced != manifest { 153 | t.Fatalf("Expected replaced to be the same as manifest") 154 | } 155 | } 156 | 157 | func TestReplaceWithFederationDeploymentSimpleReplace(t *testing.T) { 158 | replacements := []Replace{ 159 | Replace{ 160 | From: `mariadb-root-password: ""`, 161 | To: `mariadb-root-password: newer-root-password`, 162 | }, 163 | } 164 | replaced, err := ReplaceWithFederationDeployment(manifest, replacements, &deployment) 165 | 166 | if err != nil { 167 | t.Fatalf("Expected no errors, got %v", err) 168 | } 169 | 170 | if replaced != strings.Replace(manifest, `mariadb-root-password: ""`, `mariadb-root-password: newer-root-password`, 1) { 171 | t.Fatalf("Replacement not as expected") 172 | } 173 | } 174 | 175 | func TestReplaceWithFederationDeploymentTemplateReplace(t *testing.T) { 176 | replacements := []Replace{ 177 | Replace{ 178 | From: `mariadb-root-password: ""`, 179 | To: `mariadb-root-password: {{ index .ObjectMeta.Annotations "federations" }}`, 180 | }, 181 | } 182 | replaced, err := ReplaceWithFederationDeployment(manifest, replacements, &deployment) 183 | 184 | if err != nil { 185 | t.Fatalf("Expected no errors, got %v", err) 186 | } 187 | 188 | if replaced != strings.Replace(manifest, `mariadb-root-password: ""`, `mariadb-root-password: federation=example.com`, 1) { 189 | t.Logf("manifest: %s\nreplaced: %s\n", manifest, replaced) 190 | t.Fatalf("Replacement not as expected") 191 | } 192 | } 193 | 194 | func TestReplaceWithFederationDeploymentMultilineTemplateReplace(t *testing.T) { 195 | replacements := []Replace{ 196 | Replace{ 197 | From: `type: Opaque 198 | data: 199 | mariadb-root-password: "" 200 | mariadb-password: ""`, 201 | To: `type: Opaque 202 | data: 203 | mariadb-root-password: {{ index .ObjectMeta.Annotations "federations" }} 204 | mariadb-password: {{ index .ObjectMeta.Annotations "federations" }}`, 205 | }, 206 | } 207 | replaced, err := ReplaceWithFederationDeployment(manifest, replacements, &deployment) 208 | 209 | if err != nil { 210 | t.Fatalf("Expected no errors, got %v", err) 211 | } 212 | 213 | if replaced != strings.Replace(manifest, `""`, `federation=example.com`, 2) { 214 | t.Logf("manifest: %s\nreplaced: %s\n", manifest, replaced) 215 | t.Fatalf("Replacement not as expected") 216 | } 217 | } 218 | 219 | func TestReplaceWithFederationDeploymentRegexFrom(t *testing.T) { 220 | replacements := []Replace{ 221 | Replace{ 222 | From: `mariadb-.*\n`, 223 | To: `{{ index .ObjectMeta.Annotations "federations" }} 224 | `, 225 | }, 226 | } 227 | replaced, err := ReplaceWithFederationDeployment(manifest, replacements, &deployment) 228 | 229 | if err != nil { 230 | t.Fatalf("Expected no errors, got %v", err) 231 | } 232 | expected := `--- 233 | apiVersion: v1 234 | kind: Secret 235 | metadata: 236 | name: wp4-mariadb 237 | type: Opaque 238 | data: 239 | federation=example.com 240 | federation=example.com 241 | --- 242 | ` 243 | 244 | if replaced != expected { 245 | t.Logf("expected: %s\nreplaced: %s\n", expected, replaced) 246 | t.Fatalf("Replacement not as expected") 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /pkg/releaseutil/manifest.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 The Kubernetes Authors All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package releaseutil 18 | 19 | import ( 20 | "fmt" 21 | "strings" 22 | 23 | "github.com/ghodss/yaml" 24 | ) 25 | 26 | // SimpleHead defines what the structure of the head of a manifest file 27 | type SimpleHead struct { 28 | Version string `json:"apiVersion"` 29 | Kind string `json:"kind,omitempty"` 30 | Metadata *struct { 31 | Name string `json:"name"` 32 | Annotations map[string]string `json:"annotations"` 33 | } `json:"metadata,omitempty"` 34 | } 35 | 36 | // SplitManifests takes a string of manifest and returns a map contains individual manifests 37 | func SplitManifests(bigfile string) map[string]string { 38 | // This is not the best way of doing things, but it's how k8s itself does it. 39 | // Basically, we're quickly splitting a stream of YAML documents into an 40 | // array of YAML docs. In the current implementation, the file name is just 41 | // a place holder, and doesn't have any further meaning. 42 | sep := "\n---\n" 43 | cutset := " \n\t" 44 | tpl := "manifest-%d" 45 | res := map[string]string{} 46 | tmp := strings.Split(bigfile, sep) 47 | for i, d := range tmp { 48 | if len(strings.Trim(d, cutset)) > 0 { 49 | res[fmt.Sprintf(tpl, i)] = d 50 | } 51 | } 52 | return res 53 | } 54 | 55 | // Manifest reperestens a single manifest content with SimpleHead added for additional metadata 56 | type Manifest struct { 57 | SimpleHead 58 | Content string 59 | } 60 | 61 | // SplitManifestsWithHeads 62 | func SplitManifestsWithHeads(bigfile string) ([]Manifest, error) { 63 | raws := SplitManifests(bigfile) 64 | 65 | result := make([]Manifest, 0, len(raws)) 66 | var err error 67 | 68 | for _, raw := range raws { 69 | var head SimpleHead 70 | err = yaml.Unmarshal([]byte(raw), &head) 71 | 72 | result = append(result, Manifest{ 73 | Content: raw, 74 | SimpleHead: head, 75 | }) 76 | } 77 | return result, err 78 | } 79 | -------------------------------------------------------------------------------- /pkg/releaseutil/manifest_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package releaseutil 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | func TestSplitManifestsWithHeads(t *testing.T) { 24 | rawManifest := `kind: TestKind 25 | apiVersion: v1 26 | metadata: 27 | name: TestName 28 | annotations: 29 | key1: value1 30 | key2: value2` 31 | 32 | rawManifests := rawManifest + "\n---\n" + rawManifest 33 | 34 | manifests, err := SplitManifestsWithHeads(rawManifests) 35 | if err != nil { 36 | t.Errorf("Expected error to be nil, got %s", err) 37 | } 38 | 39 | if len(manifests) != 2 { 40 | t.Errorf("Expected manifests length to be 2, got %d", len(manifests)) 41 | } 42 | 43 | for _, m := range manifests { 44 | if m.SimpleHead.Kind != "TestKind" { 45 | t.Errorf("Expected Kind to be TestKind, got %s", m.SimpleHead.Kind) 46 | } 47 | if m.SimpleHead.Version != "v1" { 48 | t.Errorf("Expected Version to be v1, got %s", m.SimpleHead.Version) 49 | } 50 | if m.SimpleHead.Metadata.Name != "TestName" { 51 | t.Errorf("Expected Name to be TestName, got %s", m.SimpleHead.Metadata.Name) 52 | } 53 | if val1, ok := m.SimpleHead.Metadata.Annotations["key1"]; !ok || val1 != "value1" { 54 | t.Errorf("Expected annotation key1 to be value1, got %s", val1) 55 | } 56 | if val2, ok := m.SimpleHead.Metadata.Annotations["key2"]; !ok || val2 != "value2" { 57 | t.Errorf("Expected annotation key2 to be value2, got %s", val2) 58 | } 59 | if m.Content != rawManifest { 60 | t.Errorf("Expected Content to be equal to original, got %s", m.Content) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /rootfs/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The Kubernetes Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM alpine:3.5 16 | 17 | ENV HOME /tmp 18 | 19 | COPY . / 20 | 21 | EXPOSE 10001 22 | 23 | CMD ["/rudder"] 24 | -------------------------------------------------------------------------------- /rootfs/README.md: -------------------------------------------------------------------------------- 1 | # RootFS 2 | 3 | This directory stores all files that should be copied to the rootfs of a 4 | Docker container. The files should be stored according to the correct 5 | directory structure of the destination container. For example: 6 | 7 | ``` 8 | rootfs/bin -> /bin 9 | rootfs/usr/local/share -> /usr/local/share 10 | ``` 11 | 12 | ## Dockerfile 13 | 14 | A Dockerfile in the rootfs is used to build the image. Where possible, 15 | compilation should not be done in this Dockerfile, since we are 16 | interested in deploying the smallest possible images. 17 | 18 | Example: 19 | 20 | ```Dockerfile 21 | FROM alpine:3.2 22 | 23 | COPY . / 24 | 25 | ENTRYPOINT ["/usr/local/bin/boot"] 26 | ``` 27 | -------------------------------------------------------------------------------- /scripts/federation-clean.sh: -------------------------------------------------------------------------------- 1 | cd $GOPATH/src/k8s.io/kubernetes 2 | dind/dind-down-cluster.sh 3 | CLUSTER_NAME=dind2 dind/dind-down-cluster.sh 4 | -------------------------------------------------------------------------------- /scripts/federation.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | git clone https://github.com/kubernetes/kubernetes.git $GOPATH/src/k8s.io/kubernetes 4 | cd $GOPATH/src/k8s.io/kubernetes 5 | git checkout release-1.7 6 | mkdir -p _output/bin 7 | mkdir -p _output/dockerized/bin/linux/amd64 8 | 9 | curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.7.2/kubernetes-client-linux-amd64.tar.gz 10 | tar -xzvf kubernetes-client-linux-amd64.tar.gz 11 | chmod +x kubernetes/client/bin/kubefed 12 | chmod +x kubernetes/client/bin/kubectl 13 | sudo cp kubernetes/client/bin/kubefed /usr/local/bin 14 | sudo cp kubernetes/client/bin/kubectl /usr/local/bin 15 | cp kubernetes/client/bin/kubefed _output/bin/kubefed 16 | cp kubernetes/client/bin/kubectl _output/bin/kubectl 17 | 18 | curl -LO https://dl.k8s.io/v1.7.2/kubernetes-server-linux-amd64.tar.gz 19 | tar -xzvf kubernetes-server-linux-amd64.tar.gz 20 | chmod +x kubernetes/server/bin/hyperkube 21 | cp kubernetes/server/bin/hyperkube _output/bin/hyperkube 22 | cp kubernetes/server/bin/hyperkube _output/dockerized/bin/linux/amd64/hyperkube 23 | 24 | git clone https://github.com/nebril/kubernetes-dind-federation dind 25 | pushd dind 26 | git checkout cat-instead-of-mount 27 | popd 28 | dind/dind-up-cluster.sh 29 | CLUSTER_NAME=dind2 IP_RANGE=172.128.0.0/16 APISERVER_ADDRESS=172.128.0.1 dind/dind-up-cluster.sh 30 | kubectl config use-context dind 31 | dind/dind-deploy-federation.sh 32 | kubefed join dind2 --host-cluster-context=dind --context=federation 33 | 34 | cd $GOPATH/src/github.com/kubernetes-helm/rudder-federation 35 | python utils/populate-configmap.py > manifests/fed-credentials.yaml 36 | -------------------------------------------------------------------------------- /scripts/import.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | set -o xtrace 7 | 8 | IMAGE_REPO=${IMAGE_REPO:-nebril/rudder-fed} 9 | IMAGE_TAG=${IMAGE_TAG:-latest} 10 | TMP_IMAGE_PATH=${TMP_IMAGE_PATH:-/tmp/image.tar} 11 | NODE_PATTERN=${NODE_PATTERN:-"dind.*node"} 12 | 13 | 14 | function import-image { 15 | docker save ${IMAGE_REPO}:${IMAGE_TAG} -o "${TMP_IMAGE_PATH}" 16 | 17 | for node in `docker ps --format "{{.Names}}" | grep ${NODE_PATTERN}`; 18 | do 19 | docker cp "${TMP_IMAGE_PATH}" $node:/image.tar 20 | docker exec -ti "$node" docker load -i /image.tar 21 | done 22 | 23 | set +o xtrace 24 | echo "Finished copying docker image to dind nodes" 25 | } 26 | 27 | import-image 28 | -------------------------------------------------------------------------------- /scripts/portforward.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Portforward hack for CircleCI remote docker 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | set -o errtrace 7 | 8 | if [[ ${1:-} = start ]]; then 9 | docker run -d -it \ 10 | --name portforward --net=host \ 11 | --entrypoint /bin/sh \ 12 | bobrik/socat -c "while true; do sleep 1000; done" 13 | elif [[ ${1} ]]; then 14 | socat "TCP-LISTEN:${1},reuseaddr,fork" \ 15 | EXEC:"'docker exec -i portforward socat STDIO TCP-CONNECT:localhost:${1}'" 16 | else 17 | echo "Must specify either start or the port number" >&2 18 | exit 1 19 | fi 20 | -------------------------------------------------------------------------------- /utils/populate-configmap.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | import base64 3 | from os import path 4 | 5 | config_path = path.join(path.expanduser("~"), ".kube", "config") 6 | 7 | with open(config_path) as f: 8 | a = f.read() 9 | 10 | data = yaml.load(a) 11 | 12 | config_map = { 13 | "apiVersion": "v1", 14 | "kind": "ConfigMap", 15 | "metadata": { 16 | "name": "federation-credentials", 17 | }, 18 | "data": { 19 | "type": "tls", 20 | "cadata": base64.b64decode([cluster for cluster in data["clusters"] if cluster["name"] == "federation"][0]['cluster']['certificate-authority-data'])[:-1], 21 | "certdata":base64.b64decode([user for user in data["users"] if user["name"] == "federation"][0]["user"]["client-certificate-data"])[:-1], 22 | "keydata":base64.b64decode([user for user in data["users"] if user["name"] == "federation"][0]["user"]["client-key-data"])[:-1], 23 | "host": "https://federation-apiserver.federation-system:443" 24 | } 25 | } 26 | 27 | print yaml.dump(config_map, default_flow_style=False, default_style='"') 28 | -------------------------------------------------------------------------------- /versioning.mk: -------------------------------------------------------------------------------- 1 | MUTABLE_VERSION ?= canary 2 | 3 | GIT_COMMIT ?= $(shell git rev-parse HEAD) 4 | GIT_SHA ?= $(shell git rev-parse --short HEAD) 5 | GIT_TAG ?= $(shell git describe --tags --abbrev=0 2>/dev/null) 6 | GIT_DIRTY ?= $(shell test -n "`git status --porcelain`" && echo "dirty" || echo "clean") 7 | 8 | ifdef VERSION 9 | DOCKER_VERSION = $(VERSION) 10 | BINARY_VERSION = $(VERSION) 11 | endif 12 | 13 | DOCKER_VERSION ?= git-${GIT_SHA} 14 | BINARY_VERSION ?= ${GIT_TAG}-${GIT_SHA} 15 | 16 | IMAGE := ${DOCKER_REGISTRY}/${IMAGE_PREFIX}/${SHORT_NAME}:${DOCKER_VERSION} 17 | MUTABLE_IMAGE := ${DOCKER_REGISTRY}/${IMAGE_PREFIX}/${SHORT_NAME}:${MUTABLE_VERSION} 18 | 19 | # TODO: add versioning to rudder 20 | #LDFLAGS += -X k8s.io/helm/pkg/version.Version=${GIT_TAG} 21 | #LDFLAGS += -X k8s.io/helm/pkg/version.GitCommit=${GIT_COMMIT} 22 | #LDFLAGS += -X k8s.io/helm/pkg/version.GitTreeState=${GIT_DIRTY} 23 | 24 | DOCKER_PUSH = docker push 25 | ifeq ($(DOCKER_REGISTRY),gcr.io) 26 | DOCKER_PUSH = gcloud docker push 27 | endif 28 | 29 | info: 30 | @echo "Build tag: ${DOCKER_VERSION}" 31 | @echo "Registry: ${DOCKER_REGISTRY}" 32 | @echo "Immutable tag: ${IMAGE}" 33 | @echo "Mutable tag: ${MUTABLE_IMAGE}" 34 | 35 | .PHONY: docker-push 36 | docker-push: docker-mutable-push docker-immutable-push 37 | 38 | .PHONY: docker-immutable-push 39 | docker-immutable-push: 40 | ${DOCKER_PUSH} ${IMAGE} 41 | 42 | .PHONY: docker-mutable-push 43 | docker-mutable-push: 44 | ${DOCKER_PUSH} ${MUTABLE_IMAGE} 45 | --------------------------------------------------------------------------------