├── rootfs ├── bin │ ├── .gitkeep │ └── doctor ├── Dockerfile └── README.md ├── mocks ├── id.txt ├── latest-component-version-router.txt ├── latest-component-version-builder.txt ├── latest-component-version-controller.txt ├── installed-component-version-router.json ├── installed-component-version-builder.json ├── installed-component-version-controller.json ├── latest-component-version-v2-beta.json ├── components.json ├── cluster-post.json ├── cluster.json └── mocks.go ├── .gitignore ├── codecov.yml ├── charts └── workflow-manager │ ├── templates │ ├── workflow-manager-service-account.yaml │ ├── workflow-manager-service.yaml │ └── workflow-manager-deployment.yaml │ ├── values.yaml │ └── Chart.yaml ├── MAINTAINERS.md ├── CONTRIBUTING.md ├── _tests └── README.md ├── .travis.yml ├── docs └── README.md ├── glide.yaml ├── _scripts └── deploy.sh ├── versioning.mk ├── pkg └── swagger │ ├── models │ ├── component_version.go │ ├── k8s_resource.go │ ├── component.go │ ├── error.go │ ├── version_data.go │ ├── cluster.go │ ├── version.go │ ├── doctor_info.go │ └── namespace.go │ └── client │ ├── operations │ ├── ping_parameters.go │ ├── get_clusters_count_parameters.go │ ├── get_cluster_by_id_parameters.go │ ├── get_doctor_info_parameters.go │ ├── create_cluster_details_parameters.go │ ├── get_components_by_latest_release_parameters.go │ ├── get_components_by_latest_release_for_v2_parameters.go │ ├── publish_doctor_info_parameters.go │ ├── create_cluster_details_for_v2_parameters.go │ ├── get_component_by_name_parameters.go │ ├── ping_responses.go │ ├── get_component_by_release_parameters.go │ ├── publish_doctor_info_responses.go │ ├── publish_component_release_parameters.go │ ├── get_doctor_info_responses.go │ ├── get_cluster_by_id_responses.go │ ├── get_clusters_count_responses.go │ ├── create_cluster_details_responses.go │ ├── get_component_by_release_responses.go │ ├── create_cluster_details_for_v2_responses.go │ ├── publish_component_release_responses.go │ ├── get_clusters_by_age_parameters.go │ ├── get_clusters_by_age_responses.go │ ├── get_component_by_name_responses.go │ ├── get_components_by_latest_release_responses.go │ └── get_components_by_latest_release_for_v2_responses.go │ └── workflow_manager_client.go ├── k8s ├── kube_secret_getter_creator.go ├── interfaces.go └── k8s_test.go ├── data ├── cluster_id_test.go ├── models.go ├── available_component_versions.go ├── installed_data_test.go ├── cluster_id.go ├── available_versions.go ├── installed_data.go ├── available_versions_test.go ├── data.go └── data_test.go ├── jobs ├── jobs_test.go └── jobs.go ├── LICENSE ├── config └── config.go ├── DCO ├── rest └── client.go ├── Makefile ├── boot.go ├── handlers ├── handlers.go └── handlers_test.go ├── boot_test.go ├── README.md └── glide.lock /rootfs/bin/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mocks/id.txt: -------------------------------------------------------------------------------- 1 | f91378a6-a815-4c20-9b0d-77b205cd3ba4 2 | -------------------------------------------------------------------------------- /mocks/latest-component-version-router.txt: -------------------------------------------------------------------------------- 1 | v2-beta 2 | -------------------------------------------------------------------------------- /mocks/latest-component-version-builder.txt: -------------------------------------------------------------------------------- 1 | v2-beta 2 | -------------------------------------------------------------------------------- /mocks/latest-component-version-controller.txt: -------------------------------------------------------------------------------- 1 | v2-beta 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | rootfs/bin/boot 2 | vendor/ 3 | coverage.txt 4 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | branch: master 3 | slug: "deis/workflow-manager" 4 | -------------------------------------------------------------------------------- /rootfs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/deis/base:v0.3.6 2 | 3 | COPY . / 4 | 5 | CMD ["/bin/boot"] 6 | EXPOSE 8080 7 | -------------------------------------------------------------------------------- /charts/workflow-manager/templates/workflow-manager-service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: deis-workflow-manager 5 | labels: 6 | heritage: deis 7 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Deis Maintainers 2 | 3 | This project is part of Deis. The official maintainers documentation is 4 | located [in the main project](https://github.com/deis/deis/blob/master/MAINTAINERS.md). 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | This project is part of Deis. You can find the latest contribution 4 | guidelines [at the Deis project](https://github.com/deis/deis/blob/master/CONTRIBUTING.md). 5 | 6 | -------------------------------------------------------------------------------- /_tests/README.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | This directory is used for functional tests. 4 | 5 | The directory is prefixed with an underscore (`_tests`) to prevent Go 6 | tools from automatically running Go code found in this directory. 7 | -------------------------------------------------------------------------------- /rootfs/bin/doctor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | uid=$(curl --silent --show-error -X POST localhost:8080/doctor) 4 | echo "Information sent to deis doctor is available at the following url ${DOCTOR_API_URL}/v3/doctor/${uid}" 5 | -------------------------------------------------------------------------------- /charts/workflow-manager/values.yaml: -------------------------------------------------------------------------------- 1 | org: "deisci" 2 | pull_policy: "Always" 3 | docker_tag: canary 4 | versions_api_url: https://versions-staging.deis.com 5 | doctor_api_url: https://doctor-staging.deis.com 6 | # limits_cpu: "100m" 7 | # limits_memory: "50Mi" 8 | -------------------------------------------------------------------------------- /charts/workflow-manager/Chart.yaml: -------------------------------------------------------------------------------- 1 | name: workflow-manager 2 | home: https://github.com/deis/workflow-manager 3 | version: 4 | description: "Deis Workflow Manager: Cluster First Aid" 5 | maintainers: 6 | - name: Deis Team 7 | email: engineering@deis.com 8 | -------------------------------------------------------------------------------- /mocks/installed-component-version-router.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": { 3 | "name": "router", 4 | "description": "deis router" 5 | }, 6 | "version": { 7 | "train": "stable", 8 | "version": "2.0.0", 9 | "data": { 10 | "description": "release description", 11 | "fixes": "list of bug fixes" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /mocks/installed-component-version-builder.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": { 3 | "name": "builder", 4 | "description": "deis builder" 5 | }, 6 | "version": { 7 | "train": "stable", 8 | "version": "2.0.0", 9 | "data": { 10 | "description": "release description", 11 | "fixes": "list of bug fixes" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /mocks/installed-component-version-controller.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": { 3 | "name": "controller", 4 | "description": "deis controller" 5 | }, 6 | "version": { 7 | "train": "stable", 8 | "version": "2.0.0", 9 | "data": { 10 | "description": "release description", 11 | "fixes": "list of bug fixes" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /mocks/latest-component-version-v2-beta.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": { 3 | "name": "component", 4 | "description": "mock component for v2-beta" 5 | }, 6 | "version": { 7 | "train": "stable", 8 | "version": "v2-beta", 9 | "data": { 10 | "description": "release description", 11 | "fixes": "list of bug fixes" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | branches: 3 | only: 4 | - master 5 | cache: 6 | directories: 7 | - vendor 8 | services: 9 | - docker 10 | env: 11 | - DOCKER_BUILD_FLAGS="--pull --no-cache" 12 | install: 13 | - make bootstrap 14 | script: 15 | - make test-cover build docker-build 16 | after_success: 17 | - bash <(curl -s https://codecov.io/bash) 18 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Project Docs 2 | 3 | Documentation for the project should be stored in the `_docs` directory. 4 | Since the directory is protected from Go, undecorated code samples can 5 | also be dropped inside of this directory. 6 | 7 | We need to decide on whether we'll continue using RST or switch to 8 | Markdown. 9 | 10 | I believe we are leaning toward MkDocs: http://www.mkdocs.org/ 11 | -------------------------------------------------------------------------------- /charts/workflow-manager/templates/workflow-manager-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: deis-workflow-manager 5 | labels: 6 | heritage: deis 7 | router.deis.io/routable: "true" 8 | annotations: 9 | router.deis.io/domains: deis-workflow-manager 10 | spec: 11 | selector: 12 | app: deis-workflow-manager 13 | ports: 14 | - name: http 15 | port: 80 16 | targetPort: 8080 17 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/deis/workflow-manager 2 | import: 3 | - package: github.com/gorilla/mux 4 | - package: github.com/satori/go.uuid 5 | - package: github.com/arschles/assert 6 | - package: github.com/kelseyhightower/envconfig 7 | - package: github.com/go-swagger/go-swagger 8 | version: 0.5.0 9 | - package: k8s.io/kubernetes 10 | version: ~1.2 11 | subpackages: 12 | - client/unversioned 13 | - labels 14 | - api 15 | - pkg 16 | - package: speter.net/go/exp/math/dec/inf 17 | repo: https://github.com/belua/inf 18 | vcs: git 19 | - package: github.com/arschles/testsrv 20 | - package: github.com/deis/kubeapp 21 | subpackages: 22 | - api 23 | -------------------------------------------------------------------------------- /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.1 22 | 23 | COPY . / 24 | 25 | ENTRYPOINT ["/bin/boot"] 26 | ``` 27 | -------------------------------------------------------------------------------- /_scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Build and push Docker images to Docker Hub and quay.io. 4 | # 5 | 6 | cd "$(dirname "$0")" || exit 1 7 | docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" 8 | DEIS_REGISTRY='' make -C .. docker-push 9 | 10 | # in order to not build the container again, let's do some 11 | # docker tagging trickery. 12 | version="git-$(git rev-parse --short HEAD)" 13 | docker tag deisci/workflow-manager:canary quay.io/deisci/workflow-manager:canary 14 | docker tag deisci/workflow-manager:${version} quay.io/deisci/workflow-manager:${version} 15 | 16 | docker login -e="$QUAY_EMAIL" -u="$QUAY_USERNAME" -p="$QUAY_PASSWORD" quay.io 17 | DEIS_REGISTRY=quay.io/ make -C .. docker-push 18 | -------------------------------------------------------------------------------- /versioning.mk: -------------------------------------------------------------------------------- 1 | MUTABLE_VERSION ?= canary 2 | VERSION ?= git-$(shell git rev-parse --short HEAD) 3 | IMAGE_PREFIX ?= deisci 4 | 5 | IMAGE := ${DEIS_REGISTRY}${IMAGE_PREFIX}/${SHORT_NAME}:${VERSION} 6 | MUTABLE_IMAGE := ${DEIS_REGISTRY}${IMAGE_PREFIX}/${SHORT_NAME}:${MUTABLE_VERSION} 7 | 8 | info: 9 | @echo "Build tag: ${VERSION}" 10 | @echo "Registry: ${DEIS_REGISTRY}" 11 | @echo "Immutable tag: ${IMAGE}" 12 | @echo "Mutable tag: ${MUTABLE_IMAGE}" 13 | 14 | .PHONY: docker-push 15 | docker-push: docker-immutable-push docker-mutable-push 16 | 17 | .PHONY: docker-immutable-push 18 | docker-immutable-push: 19 | docker push ${IMAGE} 20 | 21 | .PHONY: docker-mutable-push 22 | docker-mutable-push: 23 | docker push ${MUTABLE_IMAGE} 24 | -------------------------------------------------------------------------------- /mocks/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": [ 3 | { 4 | "component": { 5 | "name": "controller", 6 | "description": "deis controller" 7 | }, 8 | "version": { 9 | "train": "stable", 10 | "version": "2.0.0" 11 | } 12 | }, 13 | { 14 | "component": { 15 | "name": "router", 16 | "description": "deis router" 17 | }, 18 | "version": { 19 | "train": "stable", 20 | "version": "2.0.0" 21 | } 22 | }, 23 | { 24 | "component": { 25 | "name": "builder", 26 | "description": "deis builder" 27 | }, 28 | "version": { 29 | "train": "stable", 30 | "version": "2.0.0" 31 | } 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /mocks/cluster-post.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": [ 3 | { 4 | "component": { 5 | "name": "controller", 6 | "description": "deis controller" 7 | }, 8 | "version": { 9 | "train": "stable", 10 | "version": "2.0.0" 11 | } 12 | }, 13 | { 14 | "component": { 15 | "name": "router", 16 | "description": "deis router" 17 | }, 18 | "version": { 19 | "train": "stable", 20 | "version": "2.0.0" 21 | } 22 | }, 23 | { 24 | "component": { 25 | "name": "builder", 26 | "description": "deis builder" 27 | }, 28 | "version": { 29 | "train": "stable", 30 | "version": "2.0.0" 31 | } 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /mocks/cluster.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "f91378a6-a815-4c20-9b0d-77b205cd3ba4", 3 | "components": [ 4 | { 5 | "component": { 6 | "name": "controller", 7 | "description": "deis controller" 8 | }, 9 | "version": { 10 | "train": "stable", 11 | "version": "2.0.0" 12 | } 13 | }, 14 | { 15 | "component": { 16 | "name": "router", 17 | "description": "deis router" 18 | }, 19 | "version": { 20 | "train": "stable", 21 | "version": "2.0.0" 22 | } 23 | }, 24 | { 25 | "component": { 26 | "name": "builder", 27 | "description": "deis builder" 28 | }, 29 | "version": { 30 | "train": "stable", 31 | "version": "2.0.0" 32 | } 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /pkg/swagger/models/component_version.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "github.com/go-swagger/go-swagger/strfmt" 4 | 5 | // This file was generated by the swagger tool. 6 | // Editing this file might prove futile when you re-run the swagger generate command 7 | 8 | /*ComponentVersion component version 9 | 10 | swagger:model componentVersion 11 | */ 12 | type ComponentVersion struct { 13 | 14 | /* component 15 | */ 16 | Component *Component `json:"component,omitempty"` 17 | 18 | /* update available 19 | */ 20 | UpdateAvailable *string `json:"updateAvailable,omitempty"` 21 | 22 | /* version 23 | */ 24 | Version *Version `json:"version,omitempty"` 25 | } 26 | 27 | // Validate validates this component version 28 | func (m *ComponentVersion) Validate(formats strfmt.Registry) error { 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /k8s/kube_secret_getter_creator.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "github.com/deis/kubeapp/api/secret" 5 | ) 6 | 7 | // KubeSecretGetterCreator is a composition of secret.Getter and secret.Creator. Please refer to the Godoc for those two interfaces (https://godoc.org/github.com/arschles/kubeapp/api/secret) 8 | type KubeSecretGetterCreator interface { 9 | secret.Getter 10 | secret.Creator 11 | } 12 | 13 | // FakeKubeSecretGetterCreator is a composition of the secret.FakeGetter and secret.FakeCreator structs 14 | type FakeKubeSecretGetterCreator struct { 15 | *secret.FakeGetter 16 | *secret.FakeCreator 17 | } 18 | 19 | // NewFakeKubeSecretGetterCreator creates a new FakeKubeSecretGetterCreator from the given fakeGetter and fakeCreator 20 | func NewFakeKubeSecretGetterCreator(fakeGetter *secret.FakeGetter, fakeCreator *secret.FakeCreator) *FakeKubeSecretGetterCreator { 21 | return &FakeKubeSecretGetterCreator{FakeGetter: fakeGetter, FakeCreator: fakeCreator} 22 | } 23 | -------------------------------------------------------------------------------- /pkg/swagger/models/k8s_resource.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | strfmt "github.com/go-swagger/go-swagger/strfmt" 8 | 9 | "github.com/go-swagger/go-swagger/errors" 10 | ) 11 | 12 | /*K8sResource k8s resource 13 | 14 | swagger:model k8sResource 15 | */ 16 | type K8sResource struct { 17 | 18 | /* data 19 | 20 | Required: true 21 | */ 22 | Data interface{} `json:"data"` 23 | } 24 | 25 | // Validate validates this k8s resource 26 | func (m *K8sResource) Validate(formats strfmt.Registry) error { 27 | var res []error 28 | 29 | if err := m.validateData(formats); err != nil { 30 | // prop 31 | res = append(res, err) 32 | } 33 | 34 | if len(res) > 0 { 35 | return errors.CompositeValidationError(res...) 36 | } 37 | return nil 38 | } 39 | 40 | func (m *K8sResource) validateData(formats strfmt.Registry) error { 41 | 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /data/cluster_id_test.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | 7 | "github.com/arschles/assert" 8 | "github.com/deis/kubeapp/api/secret" 9 | "github.com/deis/workflow-manager/k8s" 10 | "github.com/satori/go.uuid" 11 | "k8s.io/kubernetes/pkg/api" 12 | ) 13 | 14 | func TestClusterIDFromPersistentStorage(t *testing.T) { 15 | sec := api.Secret{} 16 | secretGetter := &secret.FakeGetter{ 17 | Secret: &sec, 18 | } 19 | secretCreator := &secret.FakeCreator{ 20 | CreateFunc: func(sec *api.Secret) (*api.Secret, error) { 21 | return sec, nil 22 | }, 23 | } 24 | secrets := k8s.NewFakeKubeSecretGetterCreator(secretGetter, secretCreator) 25 | clusterID := clusterIDFromPersistentStorage{ 26 | rwm: new(sync.RWMutex), 27 | cache: "", 28 | secretGetterCreator: secrets, 29 | } 30 | resp, err := clusterID.Get() 31 | assert.NoErr(t, err) 32 | // verify that the secret is a UUID 33 | _, err = uuid.FromString(resp) 34 | assert.NoErr(t, err) 35 | } 36 | -------------------------------------------------------------------------------- /jobs/jobs_test.go: -------------------------------------------------------------------------------- 1 | package jobs 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/arschles/assert" 8 | ) 9 | 10 | type testPeriodic struct { 11 | t *testing.T 12 | err error 13 | i int 14 | freq time.Duration 15 | } 16 | 17 | func (t *testPeriodic) Do() error { 18 | t.t.Logf("testPeriodic Do at %s", time.Now()) 19 | t.i++ 20 | return t.err 21 | } 22 | 23 | func (t testPeriodic) Frequency() time.Duration { 24 | return t.freq 25 | } 26 | 27 | func TestDoPeriodic(t *testing.T) { 28 | interval := time.Duration(3000) * time.Millisecond 29 | p := &testPeriodic{t: t, err: nil, freq: interval} 30 | closeCh1 := DoPeriodic([]Periodic{p}) 31 | time.Sleep(interval / 2) // wait a little while for the goroutine to call the job once 32 | assert.True(t, p.i == 1, "the periodic wasn't called once") 33 | time.Sleep(interval) 34 | assert.True(t, p.i == 2, "the periodic wasn't called twice") 35 | time.Sleep(interval) 36 | assert.True(t, p.i == 3, "the periodic wasn't called thrice") 37 | close(closeCh1) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/ping_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | ) 12 | 13 | // NewPingParams creates a new PingParams object 14 | // with the default values initialized. 15 | func NewPingParams() *PingParams { 16 | 17 | return &PingParams{} 18 | } 19 | 20 | /*PingParams contains all the parameters to send to the API endpoint 21 | for the ping operation typically these are written to a http.Request 22 | */ 23 | type PingParams struct { 24 | } 25 | 26 | // WriteToRequest writes these params to a swagger request 27 | func (o *PingParams) WriteToRequest(r client.Request, reg strfmt.Registry) error { 28 | 29 | var res []error 30 | 31 | if len(res) > 0 { 32 | return errors.CompositeValidationError(res...) 33 | } 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_clusters_count_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | ) 12 | 13 | // NewGetClustersCountParams creates a new GetClustersCountParams object 14 | // with the default values initialized. 15 | func NewGetClustersCountParams() *GetClustersCountParams { 16 | 17 | return &GetClustersCountParams{} 18 | } 19 | 20 | /*GetClustersCountParams contains all the parameters to send to the API endpoint 21 | for the get clusters count operation typically these are written to a http.Request 22 | */ 23 | type GetClustersCountParams struct { 24 | } 25 | 26 | // WriteToRequest writes these params to a swagger request 27 | func (o *GetClustersCountParams) WriteToRequest(r client.Request, reg strfmt.Registry) error { 28 | 29 | var res []error 30 | 31 | if len(res) > 0 { 32 | return errors.CompositeValidationError(res...) 33 | } 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /data/models.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "github.com/deis/workflow-manager/pkg/swagger/models" 5 | ) 6 | 7 | // SparseComponentInfo is the JSON compatible struct that holds limited data about a component 8 | type SparseComponentInfo struct { 9 | Name string `json:"name"` 10 | } 11 | 12 | // SparseVersionInfo is the JSON compatible struct that holds limited data about a 13 | // component version 14 | type SparseVersionInfo struct { 15 | Train string `json:"train"` 16 | } 17 | 18 | // SparseComponentAndTrainInfo is the JSON compatible struct that holds a 19 | // SparseComponentInfo and SparseVersionInfo 20 | type SparseComponentAndTrainInfo struct { 21 | Component SparseComponentInfo `json:"component"` 22 | Version SparseVersionInfo `json:"version"` 23 | } 24 | 25 | // SparseComponentAndTrainInfoJSONWrapper is the JSON compatible struct that holds a slice of 26 | // SparseComponentAndTrainInfo structs 27 | type SparseComponentAndTrainInfoJSONWrapper struct { 28 | Data []SparseComponentAndTrainInfo `json:"data"` 29 | } 30 | 31 | // ComponentVersionsJSONWrapper is the JSON compatible struct that holds a slice of 32 | // types.ComponentVersion structs 33 | type ComponentVersionsJSONWrapper struct { 34 | Data []models.ComponentVersion `json:"data"` 35 | } 36 | -------------------------------------------------------------------------------- /pkg/swagger/models/component.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | strfmt "github.com/go-swagger/go-swagger/strfmt" 8 | 9 | "github.com/go-swagger/go-swagger/errors" 10 | "github.com/go-swagger/go-swagger/httpkit/validate" 11 | ) 12 | 13 | /*Component component 14 | 15 | swagger:model component 16 | */ 17 | type Component struct { 18 | 19 | /* description 20 | */ 21 | Description *string `json:"description,omitempty"` 22 | 23 | /* name 24 | 25 | Required: true 26 | Min Length: 1 27 | */ 28 | Name string `json:"name"` 29 | 30 | /* type 31 | */ 32 | Type *string `json:"type,omitempty"` 33 | } 34 | 35 | // Validate validates this component 36 | func (m *Component) Validate(formats strfmt.Registry) error { 37 | var res []error 38 | 39 | if err := m.validateName(formats); err != nil { 40 | // prop 41 | res = append(res, err) 42 | } 43 | 44 | if len(res) > 0 { 45 | return errors.CompositeValidationError(res...) 46 | } 47 | return nil 48 | } 49 | 50 | func (m *Component) validateName(formats strfmt.Registry) error { 51 | 52 | if err := validate.RequiredString("name", "body", string(m.Name)); err != nil { 53 | return err 54 | } 55 | 56 | if err := validate.MinLength("name", "body", string(m.Name), 1); err != nil { 57 | return err 58 | } 59 | 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | apiclient "github.com/deis/workflow-manager/pkg/swagger/client" 5 | httptransport "github.com/go-swagger/go-swagger/httpkit/client" 6 | "github.com/kelseyhightower/envconfig" 7 | "net/url" 8 | ) 9 | 10 | // Specification config struct 11 | type Specification struct { 12 | Port string `default:"8080" envconfig:"PORT"` 13 | Polling int `default:"43200" envconfig:"POLL_INTERVAL_SEC"` // 43200 seconds = 12 hours 14 | VersionsAPIURL string `envconfig:"VERSIONS_API_URL" default:"https://versions-staging.deis.com"` 15 | DoctorAPIURL string `envconfig:"DOCTOR_API_URL" default:"https://doctor-staging.deis.com"` 16 | APIVersion string `envconfig:"API_VERSION" default:"v3"` 17 | CheckVersions bool `default:"true" envconfig:"CHECK_VERSIONS"` 18 | DeisNamespace string `default:"deis" envconfig:"DEIS_NAMESPACE"` 19 | } 20 | 21 | // Spec is an exportable variable that contains workflow manager config data 22 | var Spec Specification 23 | 24 | func init() { 25 | envconfig.Process("workflow_manager", &Spec) 26 | } 27 | 28 | func GetSwaggerClient(apiURL string) (*apiclient.WorkflowManager, error) { 29 | urlDet, err := url.Parse(apiURL) 30 | if err != nil { 31 | return nil, err 32 | } 33 | // create the transport 34 | transport := httptransport.New(urlDet.Host, "", []string{urlDet.Scheme}) 35 | apiClient := apiclient.Default 36 | apiClient.SetTransport(transport) 37 | return apiClient, nil 38 | } 39 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_cluster_by_id_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | ) 12 | 13 | // NewGetClusterByIDParams creates a new GetClusterByIDParams object 14 | // with the default values initialized. 15 | func NewGetClusterByIDParams() *GetClusterByIDParams { 16 | var () 17 | return &GetClusterByIDParams{} 18 | } 19 | 20 | /*GetClusterByIDParams contains all the parameters to send to the API endpoint 21 | for the get cluster by id operation typically these are written to a http.Request 22 | */ 23 | type GetClusterByIDParams struct { 24 | 25 | /*ID*/ 26 | ID string 27 | } 28 | 29 | // WithID adds the id to the get cluster by id params 30 | func (o *GetClusterByIDParams) WithID(id string) *GetClusterByIDParams { 31 | o.ID = id 32 | return o 33 | } 34 | 35 | // WriteToRequest writes these params to a swagger request 36 | func (o *GetClusterByIDParams) WriteToRequest(r client.Request, reg strfmt.Registry) error { 37 | 38 | var res []error 39 | 40 | // path param id 41 | if err := r.SetPathParam("id", o.ID); err != nil { 42 | return err 43 | } 44 | 45 | if len(res) > 0 { 46 | return errors.CompositeValidationError(res...) 47 | } 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /data/available_component_versions.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "github.com/deis/workflow-manager/k8s" 5 | "github.com/deis/workflow-manager/pkg/swagger/models" 6 | ) 7 | 8 | // AvailableComponentVersion is an interface for managing component version data 9 | type AvailableComponentVersion interface { 10 | // will have a Get method to retrieve available component version data 11 | Get(component string, cluster models.Cluster) (models.Version, error) 12 | } 13 | 14 | // latestReleasedComponent fulfills the AvailableComponentVersion interface 15 | type latestReleasedComponent struct { 16 | k8sResources *k8s.ResourceInterfaceNamespaced 17 | availableVersions AvailableVersions 18 | } 19 | 20 | // NewLatestReleasedComponent creates a new AvailableComponentVersion that gets the latest released component using sgc as the implementation to get and create secrets 21 | func NewLatestReleasedComponent( 22 | ri *k8s.ResourceInterfaceNamespaced, 23 | availableVersions AvailableVersions, 24 | ) AvailableComponentVersion { 25 | return &latestReleasedComponent{ 26 | k8sResources: ri, 27 | availableVersions: availableVersions, 28 | } 29 | } 30 | 31 | // Get method for LatestReleasedComponent 32 | func (c *latestReleasedComponent) Get(component string, cluster models.Cluster) (models.Version, error) { 33 | version, err := GetLatestVersion( 34 | component, 35 | cluster, 36 | c.availableVersions, 37 | ) 38 | if err != nil { 39 | return models.Version{}, err 40 | } 41 | return version, nil 42 | } 43 | -------------------------------------------------------------------------------- /data/installed_data_test.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/deis/workflow-manager/k8s" 7 | "k8s.io/kubernetes/pkg/api" 8 | "k8s.io/kubernetes/pkg/api/testapi" 9 | "k8s.io/kubernetes/pkg/apis/extensions" 10 | "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" 11 | ) 12 | 13 | const namespace = "deis" 14 | 15 | func TestInstalledDeisData(t *testing.T) { 16 | client := getK8sClient(t) 17 | k := k8s.NewResourceInterfaceNamespaced(client, namespace) 18 | installedData := installedDeisData{ 19 | k8sResources: k, 20 | } 21 | _, _ = installedData.Get() 22 | //TODO: we need to create a fake client interface that is a union of api+extension fake clients 23 | } 24 | 25 | func getK8sClient(t *testing.T) *simple.Client { 26 | c := &simple.Client{ 27 | Request: simple.Request{ 28 | Method: "GET", 29 | Path: testapi.Extensions.ResourcePath("deployments", namespace, ""), 30 | }, 31 | Response: simple.Response{StatusCode: 200, 32 | Body: &extensions.DeploymentList{ 33 | Items: []extensions.Deployment{ 34 | { 35 | ObjectMeta: api.ObjectMeta{ 36 | Name: "deis-builder", 37 | }, 38 | Spec: extensions.DeploymentSpec{ 39 | Template: api.PodTemplateSpec{ 40 | Spec: api.PodSpec{ 41 | Containers: []api.Container{ 42 | { 43 | Image: "container-image", 44 | }, 45 | }, 46 | }, 47 | }, 48 | }, 49 | }, 50 | }, 51 | }, 52 | }, 53 | } 54 | return c.Setup(t) 55 | } 56 | -------------------------------------------------------------------------------- /pkg/swagger/models/error.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | strfmt "github.com/go-swagger/go-swagger/strfmt" 8 | 9 | "github.com/go-swagger/go-swagger/errors" 10 | "github.com/go-swagger/go-swagger/httpkit/validate" 11 | ) 12 | 13 | /*Error error 14 | 15 | swagger:model error 16 | */ 17 | type Error struct { 18 | 19 | /* code 20 | 21 | Required: true 22 | */ 23 | Code int64 `json:"code"` 24 | 25 | /* message 26 | 27 | Required: true 28 | */ 29 | Message string `json:"message"` 30 | } 31 | 32 | // Validate validates this error 33 | func (m *Error) Validate(formats strfmt.Registry) error { 34 | var res []error 35 | 36 | if err := m.validateCode(formats); err != nil { 37 | // prop 38 | res = append(res, err) 39 | } 40 | 41 | if err := m.validateMessage(formats); err != nil { 42 | // prop 43 | res = append(res, err) 44 | } 45 | 46 | if len(res) > 0 { 47 | return errors.CompositeValidationError(res...) 48 | } 49 | return nil 50 | } 51 | 52 | func (m *Error) validateCode(formats strfmt.Registry) error { 53 | 54 | if err := validate.Required("code", "body", int64(m.Code)); err != nil { 55 | return err 56 | } 57 | 58 | return nil 59 | } 60 | 61 | func (m *Error) validateMessage(formats strfmt.Registry) error { 62 | 63 | if err := validate.RequiredString("message", "body", string(m.Message)); err != nil { 64 | return err 65 | } 66 | 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /DCO: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 660 York Street, Suite 102, 6 | San Francisco, CA 94110 USA 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this 9 | license document, but changing it is not allowed. 10 | 11 | 12 | Developer's Certificate of Origin 1.1 13 | 14 | By making a contribution to this project, I certify that: 15 | 16 | (a) The contribution was created in whole or in part by me and I 17 | have the right to submit it under the open source license 18 | indicated in the file; or 19 | 20 | (b) The contribution is based upon previous work that, to the best 21 | of my knowledge, is covered under an appropriate open source 22 | license and I have the right under that license to submit that 23 | work with modifications, whether created in whole or in part 24 | by me, under the same open source license (unless I am 25 | permitted to submit under a different license), as indicated 26 | in the file; or 27 | 28 | (c) The contribution was provided directly to me by some other 29 | person who certified (a), (b) or (c) and I have not modified 30 | it. 31 | 32 | (d) I understand and agree that this project and the contribution 33 | are public and that a record of the contribution (including all 34 | personal information I submit with it, including my sign-off) is 35 | maintained indefinitely and may be redistributed consistent with 36 | this project or the open source license(s) involved. 37 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_doctor_info_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | ) 12 | 13 | // NewGetDoctorInfoParams creates a new GetDoctorInfoParams object 14 | // with the default values initialized. 15 | func NewGetDoctorInfoParams() *GetDoctorInfoParams { 16 | var () 17 | return &GetDoctorInfoParams{} 18 | } 19 | 20 | /*GetDoctorInfoParams contains all the parameters to send to the API endpoint 21 | for the get doctor info operation typically these are written to a http.Request 22 | */ 23 | type GetDoctorInfoParams struct { 24 | 25 | /*UUID 26 | A universal Id to represent a sepcific request or report 27 | 28 | */ 29 | UUID string 30 | } 31 | 32 | // WithUUID adds the uuid to the get doctor info params 33 | func (o *GetDoctorInfoParams) WithUUID(uuid string) *GetDoctorInfoParams { 34 | o.UUID = uuid 35 | return o 36 | } 37 | 38 | // WriteToRequest writes these params to a swagger request 39 | func (o *GetDoctorInfoParams) WriteToRequest(r client.Request, reg strfmt.Registry) error { 40 | 41 | var res []error 42 | 43 | // path param uuid 44 | if err := r.SetPathParam("uuid", o.UUID); err != nil { 45 | return err 46 | } 47 | 48 | if len(res) > 0 { 49 | return errors.CompositeValidationError(res...) 50 | } 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /pkg/swagger/client/workflow_manager_client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | httptransport "github.com/go-swagger/go-swagger/httpkit/client" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | 12 | "github.com/deis/workflow-manager/pkg/swagger/client/operations" 13 | ) 14 | 15 | // Default workflow manager HTTP client. 16 | var Default = NewHTTPClient(nil) 17 | 18 | // NewHTTPClient creates a new workflow manager HTTP client. 19 | func NewHTTPClient(formats strfmt.Registry) *WorkflowManager { 20 | if formats == nil { 21 | formats = strfmt.Default 22 | } 23 | transport := httptransport.New("localhost", "/", []string{"http"}) 24 | return New(transport, formats) 25 | } 26 | 27 | // New creates a new workflow manager client 28 | func New(transport client.Transport, formats strfmt.Registry) *WorkflowManager { 29 | cli := new(WorkflowManager) 30 | cli.Transport = transport 31 | 32 | cli.Operations = operations.New(transport, formats) 33 | 34 | return cli 35 | } 36 | 37 | // WorkflowManager is a client for workflow manager 38 | type WorkflowManager struct { 39 | Operations *operations.Client 40 | 41 | Transport client.Transport 42 | } 43 | 44 | // SetTransport changes the transport on the client and all its subresources 45 | func (c *WorkflowManager) SetTransport(transport client.Transport) { 46 | c.Transport = transport 47 | 48 | c.Operations.SetTransport(transport) 49 | 50 | } 51 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/create_cluster_details_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | 12 | "github.com/deis/workflow-manager/pkg/swagger/models" 13 | ) 14 | 15 | // NewCreateClusterDetailsParams creates a new CreateClusterDetailsParams object 16 | // with the default values initialized. 17 | func NewCreateClusterDetailsParams() *CreateClusterDetailsParams { 18 | var () 19 | return &CreateClusterDetailsParams{} 20 | } 21 | 22 | /*CreateClusterDetailsParams contains all the parameters to send to the API endpoint 23 | for the create cluster details operation typically these are written to a http.Request 24 | */ 25 | type CreateClusterDetailsParams struct { 26 | 27 | /*Body*/ 28 | Body *models.Cluster 29 | } 30 | 31 | // WithBody adds the body to the create cluster details params 32 | func (o *CreateClusterDetailsParams) WithBody(body *models.Cluster) *CreateClusterDetailsParams { 33 | o.Body = body 34 | return o 35 | } 36 | 37 | // WriteToRequest writes these params to a swagger request 38 | func (o *CreateClusterDetailsParams) WriteToRequest(r client.Request, reg strfmt.Registry) error { 39 | 40 | var res []error 41 | 42 | if o.Body == nil { 43 | o.Body = new(models.Cluster) 44 | } 45 | 46 | if err := r.SetBodyParam(o.Body); err != nil { 47 | return err 48 | } 49 | 50 | if len(res) > 0 { 51 | return errors.CompositeValidationError(res...) 52 | } 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_components_by_latest_release_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | ) 12 | 13 | // NewGetComponentsByLatestReleaseParams creates a new GetComponentsByLatestReleaseParams object 14 | // with the default values initialized. 15 | func NewGetComponentsByLatestReleaseParams() *GetComponentsByLatestReleaseParams { 16 | var () 17 | return &GetComponentsByLatestReleaseParams{} 18 | } 19 | 20 | /*GetComponentsByLatestReleaseParams contains all the parameters to send to the API endpoint 21 | for the get components by latest release operation typically these are written to a http.Request 22 | */ 23 | type GetComponentsByLatestReleaseParams struct { 24 | 25 | /*Body*/ 26 | Body GetComponentsByLatestReleaseBody 27 | } 28 | 29 | // WithBody adds the body to the get components by latest release params 30 | func (o *GetComponentsByLatestReleaseParams) WithBody(body GetComponentsByLatestReleaseBody) *GetComponentsByLatestReleaseParams { 31 | o.Body = body 32 | return o 33 | } 34 | 35 | // WriteToRequest writes these params to a swagger request 36 | func (o *GetComponentsByLatestReleaseParams) WriteToRequest(r client.Request, reg strfmt.Registry) error { 37 | 38 | var res []error 39 | 40 | if err := r.SetBodyParam(o.Body); err != nil { 41 | return err 42 | } 43 | 44 | if len(res) > 0 { 45 | return errors.CompositeValidationError(res...) 46 | } 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_components_by_latest_release_for_v2_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | ) 12 | 13 | // NewGetComponentsByLatestReleaseForV2Params creates a new GetComponentsByLatestReleaseForV2Params object 14 | // with the default values initialized. 15 | func NewGetComponentsByLatestReleaseForV2Params() *GetComponentsByLatestReleaseForV2Params { 16 | var () 17 | return &GetComponentsByLatestReleaseForV2Params{} 18 | } 19 | 20 | /*GetComponentsByLatestReleaseForV2Params contains all the parameters to send to the API endpoint 21 | for the get components by latest release for v2 operation typically these are written to a http.Request 22 | */ 23 | type GetComponentsByLatestReleaseForV2Params struct { 24 | 25 | /*Body*/ 26 | Body GetComponentsByLatestReleaseForV2Body 27 | } 28 | 29 | // WithBody adds the body to the get components by latest release for v2 params 30 | func (o *GetComponentsByLatestReleaseForV2Params) WithBody(body GetComponentsByLatestReleaseForV2Body) *GetComponentsByLatestReleaseForV2Params { 31 | o.Body = body 32 | return o 33 | } 34 | 35 | // WriteToRequest writes these params to a swagger request 36 | func (o *GetComponentsByLatestReleaseForV2Params) WriteToRequest(r client.Request, reg strfmt.Registry) error { 37 | 38 | var res []error 39 | 40 | if err := r.SetBodyParam(o.Body); err != nil { 41 | return err 42 | } 43 | 44 | if len(res) > 0 { 45 | return errors.CompositeValidationError(res...) 46 | } 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /rest/client.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "crypto/tls" 5 | "io" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | const ( 11 | // ContentType is the key for the Content-Type header 12 | ContentType = "Content-Type" 13 | // ApplicationJSON is the value for the Content-Type header for JSON 14 | ApplicationJSON = "application/json" 15 | ) 16 | 17 | var ( 18 | // JSContentTypeHeader is the http.Header for the "application/json" content type 19 | JSContentTypeHeader = http.Header(map[string][]string{ContentType: []string{ApplicationJSON}}) 20 | ) 21 | 22 | // Client is a REST client that makes requests to a given path. Implementations will prefix the path with a base URL that is specified when the client implementation is created 23 | type Client interface { 24 | Do(method string, headers http.Header, body io.Reader, path ...string) (*http.Response, error) 25 | } 26 | 27 | type realClient struct { 28 | baseURL string 29 | client *http.Client 30 | } 31 | 32 | // NewRealTLSClient creates a new Client that uses a TLS connection to make requests to baseURL 33 | func NewRealTLSClient(baseURL string) Client { 34 | return &realClient{baseURL: baseURL, client: getTLSClient()} 35 | } 36 | 37 | func (r realClient) Do(method string, headers http.Header, body io.Reader, path ...string) (*http.Response, error) { 38 | strSlice := []string{r.baseURL} 39 | strSlice = append(strSlice, path...) 40 | urlStr := strings.Join(strSlice, "/") 41 | req, err := http.NewRequest(method, urlStr, body) 42 | if err != nil { 43 | return nil, err 44 | } 45 | req.Header = headers 46 | return r.client.Do(req) 47 | } 48 | 49 | func getTLSClient() *http.Client { 50 | tr := &http.Transport{ 51 | TLSClientConfig: &tls.Config{InsecureSkipVerify: false}, 52 | } 53 | return &http.Client{Transport: tr} 54 | } 55 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHORT_NAME := workflow-manager 2 | 3 | include versioning.mk 4 | 5 | DEV_ENV_IMAGE := quay.io/deis/go-dev:0.20.0 6 | SWAGGER_IMAGE := quay.io/goswagger/swagger:0.7.3 7 | DEV_ENV_WORK_DIR := /go/src/github.com/deis/${SHORT_NAME} 8 | DEV_ENV_CMD := docker run --rm -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} ${DEV_ENV_IMAGE} 9 | SWAGGER_CMD := docker run --rm -e GOPATH=/go -v ${CURDIR}:${DEV_ENV_WORK_DIR} -w ${DEV_ENV_WORK_DIR} ${SWAGGER_IMAGE} 10 | SHELL_SCRIPTS = rootfs/bin/doctor 11 | 12 | # Common flags passed into Go's linker. 13 | LDFLAGS := "-s -X main.version=${VERSION}" 14 | 15 | # Docker Root FS 16 | BINDIR := ${CURDIR}/rootfs/bin 17 | 18 | # Legacy support for DEV_REGISTRY, plus new support for DEIS_REGISTRY. 19 | ifdef ${DEV_REGISTRY} 20 | DEIS_REGISTRY = ${DEV_REGISTRY}/ 21 | endif 22 | 23 | all: build docker-build docker-push 24 | 25 | # Containerized dependency resolution / initial workspace setup 26 | bootstrap: 27 | ${DEV_ENV_CMD} glide install 28 | 29 | # This illustrates a two-stage Docker build. docker-compile runs inside of 30 | # the Docker environment. Other alternatives are cross-compiling, doing 31 | # the build as a `docker build`. 32 | build: 33 | mkdir -p ${BINDIR} 34 | ${DEV_ENV_CMD} go build -o rootfs/bin/boot -ldflags ${LDFLAGS} boot.go 35 | 36 | swagger-clientstub: 37 | ${SWAGGER_CMD} generate client -A WorkflowManager -t pkg/swagger -f api/swagger-spec/swagger.yml 38 | 39 | test: 40 | ${SWAGGER_CMD} validate ./api/swagger-spec/swagger.yml 41 | ${DEV_ENV_CMD} sh -c 'go test -v $$(glide nv)' 42 | 43 | test-cover: 44 | ${DEV_ENV_CMD} test-cover.sh 45 | 46 | test-style: 47 | ${DEV_ENV_CMD} shellcheck $(SHELL_SCRIPTS) 48 | 49 | # For cases where we're building from local 50 | # We also alter the RC file to set the image name. 51 | docker-build: build 52 | docker build ${DOCKER_BUILD_FLAGS} -t ${IMAGE} rootfs 53 | docker tag ${IMAGE} ${MUTABLE_IMAGE} 54 | -------------------------------------------------------------------------------- /charts/workflow-manager/templates/workflow-manager-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: deis-workflow-manager 5 | labels: 6 | heritage: deis 7 | annotations: 8 | component.deis.io/version: {{ .Values.docker_tag }} 9 | spec: 10 | replicas: 1 11 | strategy: 12 | rollingUpdate: 13 | maxSurge: 1 14 | maxUnavailable: 0 15 | type: RollingUpdate 16 | selector: 17 | matchLabels: 18 | app: deis-workflow-manager 19 | template: 20 | metadata: 21 | labels: 22 | app: deis-workflow-manager 23 | spec: 24 | serviceAccount: deis-workflow-manager 25 | containers: 26 | - name: deis-workflow-manager 27 | image: quay.io/{{.Values.org}}/workflow-manager:{{.Values.docker_tag}} 28 | imagePullPolicy: {{.Values.pull_policy}} 29 | {{- if or (.Values.limits_cpu) (.Values.limits_memory)}} 30 | resources: 31 | limits: 32 | {{- if (.Values.limits_cpu) }} 33 | cpu: {{.Values.limits_cpu}} 34 | {{- end}} 35 | {{- if (.Values.limits_memory) }} 36 | memory: {{.Values.limits_memory}} 37 | {{- end}} 38 | {{- end}} 39 | env: 40 | - name: POD_NAMESPACE 41 | valueFrom: 42 | fieldRef: 43 | fieldPath: metadata.namespace 44 | - name: PORT 45 | value: "8080" 46 | - name: VERSIONS_API_URL 47 | value: {{.Values.versions_api_url}} 48 | - name: DOCTOR_API_URL 49 | value: {{.Values.doctor_api_url}} 50 | - name: POLL_INTERVAL_SEC 51 | value: "43200" 52 | - name: CHECK_VERSIONS 53 | value: "true" 54 | - name: API_VERSION 55 | value: "v2" 56 | - name: DEIS_NAMESPACE 57 | valueFrom: 58 | fieldRef: 59 | fieldPath: metadata.namespace 60 | ports: 61 | - containerPort: 8080 62 | -------------------------------------------------------------------------------- /pkg/swagger/models/version_data.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | strfmt "github.com/go-swagger/go-swagger/strfmt" 8 | "github.com/go-swagger/go-swagger/swag" 9 | 10 | "github.com/go-swagger/go-swagger/errors" 11 | "github.com/go-swagger/go-swagger/httpkit/validate" 12 | ) 13 | 14 | /*VersionData version data 15 | 16 | swagger:model versionData 17 | */ 18 | type VersionData struct { 19 | 20 | /* description 21 | 22 | Min Length: 1 23 | */ 24 | Description string `json:"description,omitempty"` 25 | 26 | /* fixes 27 | 28 | Min Length: 1 29 | */ 30 | Fixes string `json:"fixes,omitempty"` 31 | 32 | /* image 33 | */ 34 | Image *string `json:"image,omitempty"` 35 | } 36 | 37 | // Validate validates this version data 38 | func (m *VersionData) Validate(formats strfmt.Registry) error { 39 | var res []error 40 | 41 | if err := m.validateDescription(formats); err != nil { 42 | // prop 43 | res = append(res, err) 44 | } 45 | 46 | if err := m.validateFixes(formats); err != nil { 47 | // prop 48 | res = append(res, err) 49 | } 50 | 51 | if len(res) > 0 { 52 | return errors.CompositeValidationError(res...) 53 | } 54 | return nil 55 | } 56 | 57 | func (m *VersionData) validateDescription(formats strfmt.Registry) error { 58 | 59 | if swag.IsZero(m.Description) { // not required 60 | return nil 61 | } 62 | 63 | if err := validate.MinLength("description", "body", string(m.Description), 1); err != nil { 64 | return err 65 | } 66 | 67 | return nil 68 | } 69 | 70 | func (m *VersionData) validateFixes(formats strfmt.Registry) error { 71 | 72 | if swag.IsZero(m.Fixes) { // not required 73 | return nil 74 | } 75 | 76 | if err := validate.MinLength("fixes", "body", string(m.Fixes), 1); err != nil { 77 | return err 78 | } 79 | 80 | return nil 81 | } 82 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/publish_doctor_info_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | 12 | "github.com/deis/workflow-manager/pkg/swagger/models" 13 | ) 14 | 15 | // NewPublishDoctorInfoParams creates a new PublishDoctorInfoParams object 16 | // with the default values initialized. 17 | func NewPublishDoctorInfoParams() *PublishDoctorInfoParams { 18 | var () 19 | return &PublishDoctorInfoParams{} 20 | } 21 | 22 | /*PublishDoctorInfoParams contains all the parameters to send to the API endpoint 23 | for the publish doctor info operation typically these are written to a http.Request 24 | */ 25 | type PublishDoctorInfoParams struct { 26 | 27 | /*Body*/ 28 | Body *models.DoctorInfo 29 | /*UUID 30 | A universal Id to represent a sepcific request or report 31 | 32 | */ 33 | UUID string 34 | } 35 | 36 | // WithBody adds the body to the publish doctor info params 37 | func (o *PublishDoctorInfoParams) WithBody(body *models.DoctorInfo) *PublishDoctorInfoParams { 38 | o.Body = body 39 | return o 40 | } 41 | 42 | // WithUUID adds the uuid to the publish doctor info params 43 | func (o *PublishDoctorInfoParams) WithUUID(uuid string) *PublishDoctorInfoParams { 44 | o.UUID = uuid 45 | return o 46 | } 47 | 48 | // WriteToRequest writes these params to a swagger request 49 | func (o *PublishDoctorInfoParams) WriteToRequest(r client.Request, reg strfmt.Registry) error { 50 | 51 | var res []error 52 | 53 | if o.Body == nil { 54 | o.Body = new(models.DoctorInfo) 55 | } 56 | 57 | if err := r.SetBodyParam(o.Body); err != nil { 58 | return err 59 | } 60 | 61 | // path param uuid 62 | if err := r.SetPathParam("uuid", o.UUID); err != nil { 63 | return err 64 | } 65 | 66 | if len(res) > 0 { 67 | return errors.CompositeValidationError(res...) 68 | } 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /pkg/swagger/models/cluster.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | strfmt "github.com/go-swagger/go-swagger/strfmt" 8 | 9 | "github.com/go-swagger/go-swagger/errors" 10 | "github.com/go-swagger/go-swagger/httpkit/validate" 11 | ) 12 | 13 | /*Cluster cluster 14 | 15 | swagger:model cluster 16 | */ 17 | type Cluster struct { 18 | 19 | /* components 20 | 21 | Required: true 22 | */ 23 | Components []*ComponentVersion `json:"components"` 24 | 25 | /* first seen 26 | */ 27 | FirstSeen *strfmt.DateTime `json:"firstSeen,omitempty"` 28 | 29 | /* id 30 | 31 | Required: true 32 | Min Length: 1 33 | */ 34 | ID string `json:"id"` 35 | 36 | /* last seen 37 | */ 38 | LastSeen *strfmt.DateTime `json:"lastSeen,omitempty"` 39 | } 40 | 41 | // Validate validates this cluster 42 | func (m *Cluster) Validate(formats strfmt.Registry) error { 43 | var res []error 44 | 45 | if err := m.validateComponents(formats); err != nil { 46 | // prop 47 | res = append(res, err) 48 | } 49 | 50 | if err := m.validateID(formats); err != nil { 51 | // prop 52 | res = append(res, err) 53 | } 54 | 55 | if len(res) > 0 { 56 | return errors.CompositeValidationError(res...) 57 | } 58 | return nil 59 | } 60 | 61 | func (m *Cluster) validateComponents(formats strfmt.Registry) error { 62 | 63 | if err := validate.Required("components", "body", m.Components); err != nil { 64 | return err 65 | } 66 | 67 | for i := 0; i < len(m.Components); i++ { 68 | 69 | if m.Components[i] != nil { 70 | 71 | if err := m.Components[i].Validate(formats); err != nil { 72 | return err 73 | } 74 | } 75 | 76 | } 77 | 78 | return nil 79 | } 80 | 81 | func (m *Cluster) validateID(formats strfmt.Registry) error { 82 | 83 | if err := validate.RequiredString("id", "body", string(m.ID)); err != nil { 84 | return err 85 | } 86 | 87 | if err := validate.MinLength("id", "body", string(m.ID), 1); err != nil { 88 | return err 89 | } 90 | 91 | return nil 92 | } 93 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/create_cluster_details_for_v2_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | 12 | "github.com/deis/workflow-manager/pkg/swagger/models" 13 | ) 14 | 15 | // NewCreateClusterDetailsForV2Params creates a new CreateClusterDetailsForV2Params object 16 | // with the default values initialized. 17 | func NewCreateClusterDetailsForV2Params() *CreateClusterDetailsForV2Params { 18 | var () 19 | return &CreateClusterDetailsForV2Params{} 20 | } 21 | 22 | /*CreateClusterDetailsForV2Params contains all the parameters to send to the API endpoint 23 | for the create cluster details for v2 operation typically these are written to a http.Request 24 | */ 25 | type CreateClusterDetailsForV2Params struct { 26 | 27 | /*Body*/ 28 | Body *models.Cluster 29 | /*ID*/ 30 | ID string 31 | } 32 | 33 | // WithBody adds the body to the create cluster details for v2 params 34 | func (o *CreateClusterDetailsForV2Params) WithBody(body *models.Cluster) *CreateClusterDetailsForV2Params { 35 | o.Body = body 36 | return o 37 | } 38 | 39 | // WithID adds the id to the create cluster details for v2 params 40 | func (o *CreateClusterDetailsForV2Params) WithID(id string) *CreateClusterDetailsForV2Params { 41 | o.ID = id 42 | return o 43 | } 44 | 45 | // WriteToRequest writes these params to a swagger request 46 | func (o *CreateClusterDetailsForV2Params) WriteToRequest(r client.Request, reg strfmt.Registry) error { 47 | 48 | var res []error 49 | 50 | if o.Body == nil { 51 | o.Body = new(models.Cluster) 52 | } 53 | 54 | if err := r.SetBodyParam(o.Body); err != nil { 55 | return err 56 | } 57 | 58 | // path param id 59 | if err := r.SetPathParam("id", o.ID); err != nil { 60 | return err 61 | } 62 | 63 | if len(res) > 0 { 64 | return errors.CompositeValidationError(res...) 65 | } 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_component_by_name_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | ) 12 | 13 | // NewGetComponentByNameParams creates a new GetComponentByNameParams object 14 | // with the default values initialized. 15 | func NewGetComponentByNameParams() *GetComponentByNameParams { 16 | var () 17 | return &GetComponentByNameParams{} 18 | } 19 | 20 | /*GetComponentByNameParams contains all the parameters to send to the API endpoint 21 | for the get component by name operation typically these are written to a http.Request 22 | */ 23 | type GetComponentByNameParams struct { 24 | 25 | /*Component 26 | A component is a single deis component, e.g., deis-router 27 | 28 | */ 29 | Component string 30 | /*Train 31 | A train is a release cadence type, e.g., "beta" or "stable" 32 | 33 | */ 34 | Train string 35 | } 36 | 37 | // WithComponent adds the component to the get component by name params 38 | func (o *GetComponentByNameParams) WithComponent(component string) *GetComponentByNameParams { 39 | o.Component = component 40 | return o 41 | } 42 | 43 | // WithTrain adds the train to the get component by name params 44 | func (o *GetComponentByNameParams) WithTrain(train string) *GetComponentByNameParams { 45 | o.Train = train 46 | return o 47 | } 48 | 49 | // WriteToRequest writes these params to a swagger request 50 | func (o *GetComponentByNameParams) WriteToRequest(r client.Request, reg strfmt.Registry) error { 51 | 52 | var res []error 53 | 54 | // path param component 55 | if err := r.SetPathParam("component", o.Component); err != nil { 56 | return err 57 | } 58 | 59 | // path param train 60 | if err := r.SetPathParam("train", o.Train); err != nil { 61 | return err 62 | } 63 | 64 | if len(res) > 0 { 65 | return errors.CompositeValidationError(res...) 66 | } 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /k8s/interfaces.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import kcl "k8s.io/kubernetes/pkg/client/unversioned" 4 | 5 | // ResourceInterface is an interface for k8s resources 6 | type ResourceInterface interface { 7 | kcl.DaemonSetsNamespacer 8 | kcl.DeploymentsNamespacer 9 | kcl.EventNamespacer 10 | kcl.NodesInterface 11 | kcl.PodsNamespacer 12 | kcl.ReplicaSetsNamespacer 13 | kcl.ReplicationControllersNamespacer 14 | kcl.SecretsNamespacer 15 | kcl.ServicesNamespacer 16 | } 17 | 18 | // ResourceInterfaceNamespaced is a "union" of ResourceInterface+namespace 19 | type ResourceInterfaceNamespaced struct { 20 | ri ResourceInterface 21 | namespace string 22 | } 23 | 24 | // NewResourceInterfaceNamespaced constructs an instance of ResourceInterfaceNamespaced 25 | func NewResourceInterfaceNamespaced(ri ResourceInterface, ns string) *ResourceInterfaceNamespaced { 26 | return &ResourceInterfaceNamespaced{ri: ri, namespace: ns} 27 | } 28 | 29 | // DaemonSets implementation 30 | func (r *ResourceInterfaceNamespaced) DaemonSets() kcl.DaemonSetInterface { 31 | return r.ri.DaemonSets(r.namespace) 32 | } 33 | 34 | // Deployments implementation 35 | func (r *ResourceInterfaceNamespaced) Deployments() kcl.DeploymentInterface { 36 | return r.ri.Deployments(r.namespace) 37 | } 38 | 39 | // Events implementation 40 | func (r *ResourceInterfaceNamespaced) Events() kcl.EventInterface { 41 | return r.ri.Events(r.namespace) 42 | } 43 | 44 | // Nodes implementation 45 | func (r *ResourceInterfaceNamespaced) Nodes() kcl.NodeInterface { 46 | return r.ri.Nodes() 47 | } 48 | 49 | // Pods implementation 50 | func (r *ResourceInterfaceNamespaced) Pods() kcl.PodInterface { 51 | return r.ri.Pods(r.namespace) 52 | } 53 | 54 | // ReplicaSets implementation 55 | func (r *ResourceInterfaceNamespaced) ReplicaSets() kcl.ReplicaSetInterface { 56 | return r.ri.ReplicaSets(r.namespace) 57 | } 58 | 59 | // ReplicationControllers implementation 60 | func (r *ResourceInterfaceNamespaced) ReplicationControllers() kcl.ReplicationControllerInterface { 61 | return r.ri.ReplicationControllers(r.namespace) 62 | } 63 | 64 | // Services implementation 65 | func (r *ResourceInterfaceNamespaced) Services() kcl.ServiceInterface { 66 | return r.ri.Services(r.namespace) 67 | } 68 | 69 | // Secrets implementation 70 | func (r *ResourceInterfaceNamespaced) Secrets() kcl.SecretsInterface { 71 | return r.ri.Secrets(r.namespace) 72 | } 73 | -------------------------------------------------------------------------------- /boot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "time" 8 | 9 | "github.com/deis/workflow-manager/config" 10 | "github.com/deis/workflow-manager/data" 11 | "github.com/deis/workflow-manager/handlers" 12 | "github.com/deis/workflow-manager/jobs" 13 | "github.com/deis/workflow-manager/k8s" 14 | "github.com/gorilla/mux" 15 | kcl "k8s.io/kubernetes/pkg/client/unversioned" 16 | ) 17 | 18 | func main() { 19 | kubeClient, err := kcl.NewInCluster() 20 | if err != nil { 21 | log.Fatalf("Error creating new Kubernetes client (%s)", err) 22 | } 23 | apiClient, err := config.GetSwaggerClient(config.Spec.VersionsAPIURL) 24 | if err != nil { 25 | log.Fatalf("Error creating new swagger api client (%s)", err) 26 | } 27 | deisK8sResources := k8s.NewResourceInterfaceNamespaced(kubeClient, config.Spec.DeisNamespace) 28 | clusterID := data.NewClusterIDFromPersistentStorage(deisK8sResources.Secrets()) 29 | installedDeisData := data.NewInstalledDeisData(deisK8sResources) 30 | availableVersion := data.NewAvailableVersionsFromAPI( 31 | apiClient, 32 | config.Spec.VersionsAPIURL, 33 | ) 34 | availableComponentVersion := data.NewLatestReleasedComponent(deisK8sResources, availableVersion) 35 | 36 | pollDur := time.Duration(config.Spec.Polling) * time.Second 37 | // we want to do the following jobs according to our remote API interval: 38 | // 1. get latest stable deis component versions 39 | // 2. send diagnostic data, if appropriate 40 | glvdPeriodic := jobs.NewGetLatestVersionDataPeriodic( 41 | installedDeisData, 42 | clusterID, 43 | availableVersion, 44 | availableComponentVersion, 45 | pollDur, 46 | ) 47 | 48 | svPeriodic := jobs.NewSendVersionsPeriodic( 49 | apiClient, 50 | clusterID, 51 | deisK8sResources, 52 | availableVersion, 53 | pollDur, 54 | ) 55 | toDo := []jobs.Periodic{glvdPeriodic, svPeriodic} 56 | log.Printf("Starting periodic jobs at interval %s", pollDur) 57 | ch := jobs.DoPeriodic(toDo) 58 | defer close(ch) 59 | 60 | // Get a new router, with handler functions 61 | r := handlers.RegisterRoutes(mux.NewRouter(), availableVersion, deisK8sResources) 62 | // Bind to a port and pass our router in 63 | hostStr := fmt.Sprintf(":%s", config.Spec.Port) 64 | log.Printf("Serving on %s", hostStr) 65 | if err := http.ListenAndServe(hostStr, r); err != nil { 66 | close(ch) 67 | log.Println("Unable to open up TLS listener") 68 | log.Fatal("ListenAndServe: ", err) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/ping_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/go-swagger/go-swagger/client" 10 | "github.com/go-swagger/go-swagger/httpkit" 11 | 12 | strfmt "github.com/go-swagger/go-swagger/strfmt" 13 | ) 14 | 15 | // PingReader is a Reader for the Ping structure. 16 | type PingReader struct { 17 | formats strfmt.Registry 18 | } 19 | 20 | // ReadResponse reads a server response into the recieved o. 21 | func (o *PingReader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 22 | switch response.Code() { 23 | 24 | case 200: 25 | result := NewPingOK() 26 | if err := result.readResponse(response, consumer, o.formats); err != nil { 27 | return nil, err 28 | } 29 | return result, nil 30 | 31 | default: 32 | result := NewPingDefault(response.Code()) 33 | if err := result.readResponse(response, consumer, o.formats); err != nil { 34 | return nil, err 35 | } 36 | return nil, result 37 | } 38 | } 39 | 40 | // NewPingOK creates a PingOK with default headers values 41 | func NewPingOK() *PingOK { 42 | return &PingOK{} 43 | } 44 | 45 | /*PingOK handles this case with default header values. 46 | 47 | server ping success 48 | */ 49 | type PingOK struct { 50 | } 51 | 52 | func (o *PingOK) Error() string { 53 | return fmt.Sprintf("[GET /ping][%d] pingOK ", 200) 54 | } 55 | 56 | func (o *PingOK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 57 | 58 | return nil 59 | } 60 | 61 | // NewPingDefault creates a PingDefault with default headers values 62 | func NewPingDefault(code int) *PingDefault { 63 | return &PingDefault{ 64 | _statusCode: code, 65 | } 66 | } 67 | 68 | /*PingDefault handles this case with default header values. 69 | 70 | unexpected error 71 | */ 72 | type PingDefault struct { 73 | _statusCode int 74 | } 75 | 76 | // Code gets the status code for the ping default response 77 | func (o *PingDefault) Code() int { 78 | return o._statusCode 79 | } 80 | 81 | func (o *PingDefault) Error() string { 82 | return fmt.Sprintf("[GET /ping][%d] ping default ", o._statusCode) 83 | } 84 | 85 | func (o *PingDefault) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 86 | 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /pkg/swagger/models/version.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | strfmt "github.com/go-swagger/go-swagger/strfmt" 8 | "github.com/go-swagger/go-swagger/swag" 9 | 10 | "github.com/go-swagger/go-swagger/errors" 11 | "github.com/go-swagger/go-swagger/httpkit/validate" 12 | ) 13 | 14 | /*Version version 15 | 16 | swagger:model version 17 | */ 18 | type Version struct { 19 | 20 | /* data 21 | */ 22 | Data *VersionData `json:"data,omitempty"` 23 | 24 | /* released 25 | 26 | Min Length: 1 27 | */ 28 | Released string `json:"released,omitempty"` 29 | 30 | /* train 31 | 32 | Min Length: 1 33 | */ 34 | Train string `json:"train,omitempty"` 35 | 36 | /* version 37 | 38 | Min Length: 1 39 | */ 40 | Version string `json:"version,omitempty"` 41 | } 42 | 43 | // Validate validates this version 44 | func (m *Version) Validate(formats strfmt.Registry) error { 45 | var res []error 46 | 47 | if err := m.validateReleased(formats); err != nil { 48 | // prop 49 | res = append(res, err) 50 | } 51 | 52 | if err := m.validateTrain(formats); err != nil { 53 | // prop 54 | res = append(res, err) 55 | } 56 | 57 | if err := m.validateVersion(formats); err != nil { 58 | // prop 59 | res = append(res, err) 60 | } 61 | 62 | if len(res) > 0 { 63 | return errors.CompositeValidationError(res...) 64 | } 65 | return nil 66 | } 67 | 68 | func (m *Version) validateReleased(formats strfmt.Registry) error { 69 | 70 | if swag.IsZero(m.Released) { // not required 71 | return nil 72 | } 73 | 74 | if err := validate.MinLength("released", "body", string(m.Released), 1); err != nil { 75 | return err 76 | } 77 | 78 | return nil 79 | } 80 | 81 | func (m *Version) validateTrain(formats strfmt.Registry) error { 82 | 83 | if swag.IsZero(m.Train) { // not required 84 | return nil 85 | } 86 | 87 | if err := validate.MinLength("train", "body", string(m.Train), 1); err != nil { 88 | return err 89 | } 90 | 91 | return nil 92 | } 93 | 94 | func (m *Version) validateVersion(formats strfmt.Registry) error { 95 | 96 | if swag.IsZero(m.Version) { // not required 97 | return nil 98 | } 99 | 100 | if err := validate.MinLength("version", "body", string(m.Version), 1); err != nil { 101 | return err 102 | } 103 | 104 | return nil 105 | } 106 | -------------------------------------------------------------------------------- /pkg/swagger/models/doctor_info.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | strfmt "github.com/go-swagger/go-swagger/strfmt" 8 | 9 | "github.com/go-swagger/go-swagger/errors" 10 | "github.com/go-swagger/go-swagger/httpkit/validate" 11 | ) 12 | 13 | /*DoctorInfo doctor info 14 | 15 | swagger:model doctorInfo 16 | */ 17 | type DoctorInfo struct { 18 | 19 | /* namespaces 20 | 21 | Required: true 22 | */ 23 | Namespaces []*Namespace `json:"namespaces"` 24 | 25 | /* nodes 26 | 27 | Required: true 28 | */ 29 | Nodes []*K8sResource `json:"nodes"` 30 | 31 | /* workflow 32 | 33 | Required: true 34 | */ 35 | Workflow *Cluster `json:"workflow"` 36 | } 37 | 38 | // Validate validates this doctor info 39 | func (m *DoctorInfo) Validate(formats strfmt.Registry) error { 40 | var res []error 41 | 42 | if err := m.validateNamespaces(formats); err != nil { 43 | // prop 44 | res = append(res, err) 45 | } 46 | 47 | if err := m.validateNodes(formats); err != nil { 48 | // prop 49 | res = append(res, err) 50 | } 51 | 52 | if err := m.validateWorkflow(formats); err != nil { 53 | // prop 54 | res = append(res, err) 55 | } 56 | 57 | if len(res) > 0 { 58 | return errors.CompositeValidationError(res...) 59 | } 60 | return nil 61 | } 62 | 63 | func (m *DoctorInfo) validateNamespaces(formats strfmt.Registry) error { 64 | 65 | if err := validate.Required("namespaces", "body", m.Namespaces); err != nil { 66 | return err 67 | } 68 | 69 | for i := 0; i < len(m.Namespaces); i++ { 70 | 71 | if m.Namespaces[i] != nil { 72 | 73 | if err := m.Namespaces[i].Validate(formats); err != nil { 74 | return err 75 | } 76 | } 77 | 78 | } 79 | 80 | return nil 81 | } 82 | 83 | func (m *DoctorInfo) validateNodes(formats strfmt.Registry) error { 84 | 85 | if err := validate.Required("nodes", "body", m.Nodes); err != nil { 86 | return err 87 | } 88 | 89 | for i := 0; i < len(m.Nodes); i++ { 90 | 91 | if m.Nodes[i] != nil { 92 | 93 | if err := m.Nodes[i].Validate(formats); err != nil { 94 | return err 95 | } 96 | } 97 | 98 | } 99 | 100 | return nil 101 | } 102 | 103 | func (m *DoctorInfo) validateWorkflow(formats strfmt.Registry) error { 104 | 105 | if m.Workflow != nil { 106 | 107 | if err := m.Workflow.Validate(formats); err != nil { 108 | return err 109 | } 110 | } 111 | 112 | return nil 113 | } 114 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_component_by_release_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | ) 12 | 13 | // NewGetComponentByReleaseParams creates a new GetComponentByReleaseParams object 14 | // with the default values initialized. 15 | func NewGetComponentByReleaseParams() *GetComponentByReleaseParams { 16 | var () 17 | return &GetComponentByReleaseParams{} 18 | } 19 | 20 | /*GetComponentByReleaseParams contains all the parameters to send to the API endpoint 21 | for the get component by release operation typically these are written to a http.Request 22 | */ 23 | type GetComponentByReleaseParams struct { 24 | 25 | /*Component 26 | A component is a single deis component, e.g., deis-router 27 | 28 | */ 29 | Component string 30 | /*Release 31 | The release version of the deis component, eg., 2.0.0-beta2 32 | 33 | */ 34 | Release string 35 | /*Train 36 | A train is a release cadence type, e.g., "beta" or "stable" 37 | 38 | */ 39 | Train string 40 | } 41 | 42 | // WithComponent adds the component to the get component by release params 43 | func (o *GetComponentByReleaseParams) WithComponent(component string) *GetComponentByReleaseParams { 44 | o.Component = component 45 | return o 46 | } 47 | 48 | // WithRelease adds the release to the get component by release params 49 | func (o *GetComponentByReleaseParams) WithRelease(release string) *GetComponentByReleaseParams { 50 | o.Release = release 51 | return o 52 | } 53 | 54 | // WithTrain adds the train to the get component by release params 55 | func (o *GetComponentByReleaseParams) WithTrain(train string) *GetComponentByReleaseParams { 56 | o.Train = train 57 | return o 58 | } 59 | 60 | // WriteToRequest writes these params to a swagger request 61 | func (o *GetComponentByReleaseParams) WriteToRequest(r client.Request, reg strfmt.Registry) error { 62 | 63 | var res []error 64 | 65 | // path param component 66 | if err := r.SetPathParam("component", o.Component); err != nil { 67 | return err 68 | } 69 | 70 | // path param release 71 | if err := r.SetPathParam("release", o.Release); err != nil { 72 | return err 73 | } 74 | 75 | // path param train 76 | if err := r.SetPathParam("train", o.Train); err != nil { 77 | return err 78 | } 79 | 80 | if len(res) > 0 { 81 | return errors.CompositeValidationError(res...) 82 | } 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /data/cluster_id.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/deis/workflow-manager/k8s" 7 | "github.com/satori/go.uuid" 8 | "k8s.io/kubernetes/pkg/api" 9 | apierrors "k8s.io/kubernetes/pkg/api/errors" 10 | ) 11 | 12 | // ClusterID is an interface for managing cluster ID data 13 | type ClusterID interface { 14 | // will have a Get method to retrieve the cluster ID 15 | Get() (string, error) 16 | // Cached returns the internal cache of the cluster ID. returns the empty string on a miss 17 | Cached() string 18 | // StoreInCache stores the given string in the internal cluster ID cache 19 | StoreInCache(string) 20 | } 21 | 22 | // GetID gets the cluster ID from the cache. on a cache miss, uses the k8s API to get it 23 | func GetID(id ClusterID) (string, error) { 24 | // First, check to see if we have an in-memory copy 25 | data := id.Cached() 26 | // If we haven't yet cached the ID in memory, invoke the passed-in getter 27 | if data == "" { 28 | d, err := id.Get() 29 | if err != nil { 30 | return "", err 31 | } 32 | data = d 33 | } 34 | return data, nil 35 | } 36 | 37 | type clusterIDFromPersistentStorage struct { 38 | rwm *sync.RWMutex 39 | cache string 40 | secretGetterCreator k8s.KubeSecretGetterCreator 41 | } 42 | 43 | // NewClusterIDFromPersistentStorage returns a new ClusterID implementation that uses the kubernetes API to get its cluster information 44 | func NewClusterIDFromPersistentStorage(sgc k8s.KubeSecretGetterCreator) ClusterID { 45 | return &clusterIDFromPersistentStorage{ 46 | rwm: new(sync.RWMutex), 47 | cache: "", 48 | secretGetterCreator: sgc, 49 | } 50 | } 51 | 52 | // Get is the ClusterID interface implementation 53 | func (c clusterIDFromPersistentStorage) Get() (string, error) { 54 | c.rwm.Lock() 55 | defer c.rwm.Unlock() 56 | secret, err := c.secretGetterCreator.Get(wfmSecretName) 57 | //If we don't have the secret we shouldn't be returning error and instead a create a new one 58 | if err != nil && !apierrors.IsNotFound(err) { 59 | return "", err 60 | } 61 | // if we don't have secret data for the cluster ID we assume a new cluster 62 | // and create a new secret 63 | if secret.Data[clusterIDSecretKey] == nil { 64 | newSecret := new(api.Secret) 65 | newSecret.Name = wfmSecretName 66 | newSecret.Data = make(map[string][]byte) 67 | newSecret.Data[clusterIDSecretKey] = []byte(uuid.NewV4().String()) 68 | fromAPI, err := c.secretGetterCreator.Create(newSecret) 69 | if err != nil { 70 | return "", err 71 | } 72 | secret = fromAPI 73 | } 74 | return string(secret.Data[clusterIDSecretKey]), nil 75 | } 76 | 77 | // StoreInCache is the ClusterID interface implementation 78 | func (c *clusterIDFromPersistentStorage) StoreInCache(cid string) { 79 | c.rwm.Lock() 80 | defer c.rwm.Unlock() 81 | c.cache = cid 82 | } 83 | 84 | // Cached is the ClusterID interface implementation 85 | func (c clusterIDFromPersistentStorage) Cached() string { 86 | c.rwm.RLock() 87 | defer c.rwm.RUnlock() 88 | return c.cache 89 | } 90 | -------------------------------------------------------------------------------- /data/available_versions.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/deis/workflow-manager/config" 7 | apiclient "github.com/deis/workflow-manager/pkg/swagger/client" 8 | "github.com/deis/workflow-manager/pkg/swagger/client/operations" 9 | "github.com/deis/workflow-manager/pkg/swagger/models" 10 | ) 11 | 12 | // AvailableVersions is an interface for managing available component version data 13 | type AvailableVersions interface { 14 | // Cached returns the internal cache of component versions. Returns the empty slice on a miss 15 | Cached() []models.ComponentVersion 16 | // Refresh gets the latest versions of each component listed in the given cluster 17 | Refresh(models.Cluster) ([]models.ComponentVersion, error) 18 | // Store stores the given slice of models.ComponentVersion in internal storage 19 | Store([]models.ComponentVersion) 20 | } 21 | 22 | type availableVersionsFromAPI struct { 23 | cache []models.ComponentVersion 24 | rwm *sync.RWMutex 25 | baseVersionsURL string 26 | apiClient *apiclient.WorkflowManager 27 | } 28 | 29 | // NewAvailableVersionsFromAPI returns a new AvailableVersions implementation that fetches its version information from a workflow manager API. It uses baseVersionsURL as the server address. If that parameter is passed as the empty string, uses config.Spec.VersionsAPIURL 30 | func NewAvailableVersionsFromAPI( 31 | apiClient *apiclient.WorkflowManager, 32 | baseVersionsURL string, 33 | ) AvailableVersions { 34 | if baseVersionsURL == "" { 35 | baseVersionsURL = config.Spec.VersionsAPIURL 36 | } 37 | return &availableVersionsFromAPI{ 38 | rwm: new(sync.RWMutex), 39 | cache: nil, 40 | baseVersionsURL: baseVersionsURL, 41 | apiClient: apiClient, 42 | } 43 | } 44 | 45 | // Refresh method for AvailableVersionsFromAPI 46 | func (a availableVersionsFromAPI) Refresh(cluster models.Cluster) ([]models.ComponentVersion, error) { 47 | reqBody := operations.GetComponentsByLatestReleaseBody{} 48 | for _, component := range cluster.Components { 49 | cv := new(models.ComponentVersion) 50 | cv.Component = &models.Component{} 51 | cv.Version = &models.Version{} 52 | cv.Component.Name = component.Component.Name 53 | cv.Version.Train = "stable" 54 | reqBody.Data = append(reqBody.Data, cv) 55 | } 56 | 57 | resp, err := a.apiClient.Operations.GetComponentsByLatestRelease(&operations.GetComponentsByLatestReleaseParams{Body: reqBody}) 58 | if err != nil { 59 | return []models.ComponentVersion{}, err 60 | } 61 | ret := []models.ComponentVersion{} 62 | for _, cv := range resp.Payload.Data { 63 | ret = append(ret, *cv) 64 | } 65 | a.Store(ret) 66 | return ret, nil 67 | } 68 | 69 | // Cached is the AvailableVersions interface implementation 70 | func (a availableVersionsFromAPI) Cached() []models.ComponentVersion { 71 | a.rwm.RLock() 72 | defer a.rwm.RUnlock() 73 | return a.cache 74 | } 75 | 76 | // Store is the AvailableVersions interface implementation 77 | func (a *availableVersionsFromAPI) Store(c []models.ComponentVersion) { 78 | a.rwm.Lock() 79 | defer a.rwm.Unlock() 80 | a.cache = c 81 | } 82 | -------------------------------------------------------------------------------- /data/installed_data.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/deis/workflow-manager/k8s" 7 | "github.com/deis/workflow-manager/pkg/swagger/models" 8 | ) 9 | 10 | var ( 11 | daemonSetType = "Daemon Set" 12 | deploymentType = "Deployment" 13 | rcType = "Replication Controller" 14 | ) 15 | 16 | const versionAnnotation = "component.deis.io/version" 17 | 18 | // InstalledData is an interface for managing installed cluster metadata 19 | type InstalledData interface { 20 | // will have a Get method to retrieve installed data 21 | Get() ([]byte, error) 22 | } 23 | 24 | // InstalledDeisData fulfills the InstalledData interface 25 | type installedDeisData struct { 26 | k8sResources *k8s.ResourceInterfaceNamespaced 27 | } 28 | 29 | // NewInstalledDeisData returns a new InstalledDeisData using rcl as the rc.Lister implementation 30 | func NewInstalledDeisData(ri *k8s.ResourceInterfaceNamespaced) InstalledData { 31 | return &installedDeisData{k8sResources: ri} 32 | } 33 | 34 | // Get method for InstalledDeisData 35 | func (g *installedDeisData) Get() ([]byte, error) { 36 | var cluster models.Cluster 37 | deployments, err := k8s.GetDeployments(g.k8sResources.Deployments()) 38 | if err != nil { 39 | return nil, err 40 | } 41 | for _, deployment := range deployments { 42 | component := models.ComponentVersion{ 43 | Component: &models.Component{ 44 | Name: deployment.Name, 45 | Type: &deploymentType, 46 | }, 47 | Version: &models.Version{ 48 | Version: deployment.Annotations[versionAnnotation], 49 | Data: &models.VersionData{ 50 | Image: &deployment.Spec.Template.Spec.Containers[0].Image, 51 | }, 52 | }, 53 | } 54 | cluster.Components = append(cluster.Components, &component) 55 | } 56 | daemonSets, err := k8s.GetDaemonSets(g.k8sResources.DaemonSets()) 57 | if err != nil { 58 | return nil, err 59 | } 60 | for _, daemonSet := range daemonSets { 61 | component := models.ComponentVersion{ 62 | Component: &models.Component{ 63 | Name: daemonSet.Name, 64 | Type: &daemonSetType, 65 | }, 66 | Version: &models.Version{ 67 | Version: daemonSet.Annotations[versionAnnotation], 68 | Data: &models.VersionData{ 69 | Image: &daemonSet.Spec.Template.Spec.Containers[0].Image, 70 | }, 71 | }, 72 | } 73 | cluster.Components = append(cluster.Components, &component) 74 | } 75 | replicationControllers, err := k8s.GetReplicationControllers(g.k8sResources.ReplicationControllers()) 76 | if err != nil { 77 | return nil, err 78 | } 79 | for _, rc := range replicationControllers { 80 | component := models.ComponentVersion{ 81 | Component: &models.Component{ 82 | Name: rc.Name, 83 | Type: &rcType, 84 | }, 85 | Version: &models.Version{ 86 | Version: rc.Annotations[versionAnnotation], 87 | Data: &models.VersionData{ 88 | Image: &rc.Spec.Template.Spec.Containers[0].Image, 89 | }, 90 | }, 91 | } 92 | cluster.Components = append(cluster.Components, &component) 93 | } 94 | js, err := json.Marshal(cluster) 95 | if err != nil { 96 | return []byte{}, err 97 | } 98 | return js, nil 99 | } 100 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/publish_doctor_info_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | 10 | "github.com/go-swagger/go-swagger/client" 11 | "github.com/go-swagger/go-swagger/httpkit" 12 | 13 | strfmt "github.com/go-swagger/go-swagger/strfmt" 14 | 15 | "github.com/deis/workflow-manager/pkg/swagger/models" 16 | ) 17 | 18 | // PublishDoctorInfoReader is a Reader for the PublishDoctorInfo structure. 19 | type PublishDoctorInfoReader struct { 20 | formats strfmt.Registry 21 | } 22 | 23 | // ReadResponse reads a server response into the recieved o. 24 | func (o *PublishDoctorInfoReader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 25 | switch response.Code() { 26 | 27 | case 200: 28 | result := NewPublishDoctorInfoOK() 29 | if err := result.readResponse(response, consumer, o.formats); err != nil { 30 | return nil, err 31 | } 32 | return result, nil 33 | 34 | default: 35 | result := NewPublishDoctorInfoDefault(response.Code()) 36 | if err := result.readResponse(response, consumer, o.formats); err != nil { 37 | return nil, err 38 | } 39 | return nil, result 40 | } 41 | } 42 | 43 | // NewPublishDoctorInfoOK creates a PublishDoctorInfoOK with default headers values 44 | func NewPublishDoctorInfoOK() *PublishDoctorInfoOK { 45 | return &PublishDoctorInfoOK{} 46 | } 47 | 48 | /*PublishDoctorInfoOK handles this case with default header values. 49 | 50 | publish doctorInfo response 51 | */ 52 | type PublishDoctorInfoOK struct { 53 | } 54 | 55 | func (o *PublishDoctorInfoOK) Error() string { 56 | return fmt.Sprintf("[POST /v3/doctor/{uuid}][%d] publishDoctorInfoOK ", 200) 57 | } 58 | 59 | func (o *PublishDoctorInfoOK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 60 | 61 | return nil 62 | } 63 | 64 | // NewPublishDoctorInfoDefault creates a PublishDoctorInfoDefault with default headers values 65 | func NewPublishDoctorInfoDefault(code int) *PublishDoctorInfoDefault { 66 | return &PublishDoctorInfoDefault{ 67 | _statusCode: code, 68 | } 69 | } 70 | 71 | /*PublishDoctorInfoDefault handles this case with default header values. 72 | 73 | unexpected error 74 | */ 75 | type PublishDoctorInfoDefault struct { 76 | _statusCode int 77 | 78 | Payload *models.Error 79 | } 80 | 81 | // Code gets the status code for the publish doctor info default response 82 | func (o *PublishDoctorInfoDefault) Code() int { 83 | return o._statusCode 84 | } 85 | 86 | func (o *PublishDoctorInfoDefault) Error() string { 87 | return fmt.Sprintf("[POST /v3/doctor/{uuid}][%d] publishDoctorInfo default %+v", o._statusCode, o.Payload) 88 | } 89 | 90 | func (o *PublishDoctorInfoDefault) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 91 | 92 | o.Payload = new(models.Error) 93 | 94 | // response payload 95 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 96 | return err 97 | } 98 | 99 | return nil 100 | } 101 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/publish_component_release_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | 12 | "github.com/deis/workflow-manager/pkg/swagger/models" 13 | ) 14 | 15 | // NewPublishComponentReleaseParams creates a new PublishComponentReleaseParams object 16 | // with the default values initialized. 17 | func NewPublishComponentReleaseParams() *PublishComponentReleaseParams { 18 | var () 19 | return &PublishComponentReleaseParams{} 20 | } 21 | 22 | /*PublishComponentReleaseParams contains all the parameters to send to the API endpoint 23 | for the publish component release operation typically these are written to a http.Request 24 | */ 25 | type PublishComponentReleaseParams struct { 26 | 27 | /*Body*/ 28 | Body *models.ComponentVersion 29 | /*Component 30 | A component is a single deis component, e.g., deis-router 31 | 32 | */ 33 | Component string 34 | /*Release 35 | The release version of the deis component, eg., 2.0.0-beta2 36 | 37 | */ 38 | Release string 39 | /*Train 40 | A train is a release cadence type, e.g., "beta" or "stable" 41 | 42 | */ 43 | Train string 44 | } 45 | 46 | // WithBody adds the body to the publish component release params 47 | func (o *PublishComponentReleaseParams) WithBody(body *models.ComponentVersion) *PublishComponentReleaseParams { 48 | o.Body = body 49 | return o 50 | } 51 | 52 | // WithComponent adds the component to the publish component release params 53 | func (o *PublishComponentReleaseParams) WithComponent(component string) *PublishComponentReleaseParams { 54 | o.Component = component 55 | return o 56 | } 57 | 58 | // WithRelease adds the release to the publish component release params 59 | func (o *PublishComponentReleaseParams) WithRelease(release string) *PublishComponentReleaseParams { 60 | o.Release = release 61 | return o 62 | } 63 | 64 | // WithTrain adds the train to the publish component release params 65 | func (o *PublishComponentReleaseParams) WithTrain(train string) *PublishComponentReleaseParams { 66 | o.Train = train 67 | return o 68 | } 69 | 70 | // WriteToRequest writes these params to a swagger request 71 | func (o *PublishComponentReleaseParams) WriteToRequest(r client.Request, reg strfmt.Registry) error { 72 | 73 | var res []error 74 | 75 | if o.Body == nil { 76 | o.Body = new(models.ComponentVersion) 77 | } 78 | 79 | if err := r.SetBodyParam(o.Body); err != nil { 80 | return err 81 | } 82 | 83 | // path param component 84 | if err := r.SetPathParam("component", o.Component); err != nil { 85 | return err 86 | } 87 | 88 | // path param release 89 | if err := r.SetPathParam("release", o.Release); err != nil { 90 | return err 91 | } 92 | 93 | // path param train 94 | if err := r.SetPathParam("train", o.Train); err != nil { 95 | return err 96 | } 97 | 98 | if len(res) > 0 { 99 | return errors.CompositeValidationError(res...) 100 | } 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_doctor_info_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | 10 | "github.com/go-swagger/go-swagger/client" 11 | "github.com/go-swagger/go-swagger/httpkit" 12 | 13 | strfmt "github.com/go-swagger/go-swagger/strfmt" 14 | 15 | "github.com/deis/workflow-manager/pkg/swagger/models" 16 | ) 17 | 18 | // GetDoctorInfoReader is a Reader for the GetDoctorInfo structure. 19 | type GetDoctorInfoReader struct { 20 | formats strfmt.Registry 21 | } 22 | 23 | // ReadResponse reads a server response into the recieved o. 24 | func (o *GetDoctorInfoReader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 25 | switch response.Code() { 26 | 27 | case 200: 28 | result := NewGetDoctorInfoOK() 29 | if err := result.readResponse(response, consumer, o.formats); err != nil { 30 | return nil, err 31 | } 32 | return result, nil 33 | 34 | default: 35 | result := NewGetDoctorInfoDefault(response.Code()) 36 | if err := result.readResponse(response, consumer, o.formats); err != nil { 37 | return nil, err 38 | } 39 | return nil, result 40 | } 41 | } 42 | 43 | // NewGetDoctorInfoOK creates a GetDoctorInfoOK with default headers values 44 | func NewGetDoctorInfoOK() *GetDoctorInfoOK { 45 | return &GetDoctorInfoOK{} 46 | } 47 | 48 | /*GetDoctorInfoOK handles this case with default header values. 49 | 50 | doctor get response 51 | */ 52 | type GetDoctorInfoOK struct { 53 | Payload *models.DoctorInfo 54 | } 55 | 56 | func (o *GetDoctorInfoOK) Error() string { 57 | return fmt.Sprintf("[GET /v3/doctor/{uuid}][%d] getDoctorInfoOK %+v", 200, o.Payload) 58 | } 59 | 60 | func (o *GetDoctorInfoOK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 61 | 62 | o.Payload = new(models.DoctorInfo) 63 | 64 | // response payload 65 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 66 | return err 67 | } 68 | 69 | return nil 70 | } 71 | 72 | // NewGetDoctorInfoDefault creates a GetDoctorInfoDefault with default headers values 73 | func NewGetDoctorInfoDefault(code int) *GetDoctorInfoDefault { 74 | return &GetDoctorInfoDefault{ 75 | _statusCode: code, 76 | } 77 | } 78 | 79 | /*GetDoctorInfoDefault handles this case with default header values. 80 | 81 | unexpected error 82 | */ 83 | type GetDoctorInfoDefault struct { 84 | _statusCode int 85 | 86 | Payload *models.Error 87 | } 88 | 89 | // Code gets the status code for the get doctor info default response 90 | func (o *GetDoctorInfoDefault) Code() int { 91 | return o._statusCode 92 | } 93 | 94 | func (o *GetDoctorInfoDefault) Error() string { 95 | return fmt.Sprintf("[GET /v3/doctor/{uuid}][%d] getDoctorInfo default %+v", o._statusCode, o.Payload) 96 | } 97 | 98 | func (o *GetDoctorInfoDefault) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 99 | 100 | o.Payload = new(models.Error) 101 | 102 | // response payload 103 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 104 | return err 105 | } 106 | 107 | return nil 108 | } 109 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_cluster_by_id_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | 10 | "github.com/go-swagger/go-swagger/client" 11 | "github.com/go-swagger/go-swagger/httpkit" 12 | 13 | strfmt "github.com/go-swagger/go-swagger/strfmt" 14 | 15 | "github.com/deis/workflow-manager/pkg/swagger/models" 16 | ) 17 | 18 | // GetClusterByIDReader is a Reader for the GetClusterByID structure. 19 | type GetClusterByIDReader struct { 20 | formats strfmt.Registry 21 | } 22 | 23 | // ReadResponse reads a server response into the recieved o. 24 | func (o *GetClusterByIDReader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 25 | switch response.Code() { 26 | 27 | case 200: 28 | result := NewGetClusterByIDOK() 29 | if err := result.readResponse(response, consumer, o.formats); err != nil { 30 | return nil, err 31 | } 32 | return result, nil 33 | 34 | default: 35 | result := NewGetClusterByIDDefault(response.Code()) 36 | if err := result.readResponse(response, consumer, o.formats); err != nil { 37 | return nil, err 38 | } 39 | return nil, result 40 | } 41 | } 42 | 43 | // NewGetClusterByIDOK creates a GetClusterByIDOK with default headers values 44 | func NewGetClusterByIDOK() *GetClusterByIDOK { 45 | return &GetClusterByIDOK{} 46 | } 47 | 48 | /*GetClusterByIDOK handles this case with default header values. 49 | 50 | clusters details response 51 | */ 52 | type GetClusterByIDOK struct { 53 | Payload *models.Cluster 54 | } 55 | 56 | func (o *GetClusterByIDOK) Error() string { 57 | return fmt.Sprintf("[GET /v3/clusters/{id}][%d] getClusterByIdOK %+v", 200, o.Payload) 58 | } 59 | 60 | func (o *GetClusterByIDOK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 61 | 62 | o.Payload = new(models.Cluster) 63 | 64 | // response payload 65 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 66 | return err 67 | } 68 | 69 | return nil 70 | } 71 | 72 | // NewGetClusterByIDDefault creates a GetClusterByIDDefault with default headers values 73 | func NewGetClusterByIDDefault(code int) *GetClusterByIDDefault { 74 | return &GetClusterByIDDefault{ 75 | _statusCode: code, 76 | } 77 | } 78 | 79 | /*GetClusterByIDDefault handles this case with default header values. 80 | 81 | unexpected error 82 | */ 83 | type GetClusterByIDDefault struct { 84 | _statusCode int 85 | 86 | Payload *models.Error 87 | } 88 | 89 | // Code gets the status code for the get cluster by id default response 90 | func (o *GetClusterByIDDefault) Code() int { 91 | return o._statusCode 92 | } 93 | 94 | func (o *GetClusterByIDDefault) Error() string { 95 | return fmt.Sprintf("[GET /v3/clusters/{id}][%d] getClusterById default %+v", o._statusCode, o.Payload) 96 | } 97 | 98 | func (o *GetClusterByIDDefault) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 99 | 100 | o.Payload = new(models.Error) 101 | 102 | // response payload 103 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 104 | return err 105 | } 106 | 107 | return nil 108 | } 109 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_clusters_count_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | 10 | "github.com/go-swagger/go-swagger/client" 11 | "github.com/go-swagger/go-swagger/httpkit" 12 | 13 | strfmt "github.com/go-swagger/go-swagger/strfmt" 14 | 15 | "github.com/deis/workflow-manager/pkg/swagger/models" 16 | ) 17 | 18 | // GetClustersCountReader is a Reader for the GetClustersCount structure. 19 | type GetClustersCountReader struct { 20 | formats strfmt.Registry 21 | } 22 | 23 | // ReadResponse reads a server response into the recieved o. 24 | func (o *GetClustersCountReader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 25 | switch response.Code() { 26 | 27 | case 200: 28 | result := NewGetClustersCountOK() 29 | if err := result.readResponse(response, consumer, o.formats); err != nil { 30 | return nil, err 31 | } 32 | return result, nil 33 | 34 | default: 35 | result := NewGetClustersCountDefault(response.Code()) 36 | if err := result.readResponse(response, consumer, o.formats); err != nil { 37 | return nil, err 38 | } 39 | return nil, result 40 | } 41 | } 42 | 43 | // NewGetClustersCountOK creates a GetClustersCountOK with default headers values 44 | func NewGetClustersCountOK() *GetClustersCountOK { 45 | return &GetClustersCountOK{} 46 | } 47 | 48 | /*GetClustersCountOK handles this case with default header values. 49 | 50 | clusters count response 51 | */ 52 | type GetClustersCountOK struct { 53 | Payload int64 54 | } 55 | 56 | func (o *GetClustersCountOK) Error() string { 57 | return fmt.Sprintf("[GET /v3/clusters/count][%d] getClustersCountOK %+v", 200, o.Payload) 58 | } 59 | 60 | func (o *GetClustersCountOK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 61 | 62 | // response payload 63 | if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { 64 | return err 65 | } 66 | 67 | return nil 68 | } 69 | 70 | // NewGetClustersCountDefault creates a GetClustersCountDefault with default headers values 71 | func NewGetClustersCountDefault(code int) *GetClustersCountDefault { 72 | return &GetClustersCountDefault{ 73 | _statusCode: code, 74 | } 75 | } 76 | 77 | /*GetClustersCountDefault handles this case with default header values. 78 | 79 | unexpected error 80 | */ 81 | type GetClustersCountDefault struct { 82 | _statusCode int 83 | 84 | Payload *models.Error 85 | } 86 | 87 | // Code gets the status code for the get clusters count default response 88 | func (o *GetClustersCountDefault) Code() int { 89 | return o._statusCode 90 | } 91 | 92 | func (o *GetClustersCountDefault) Error() string { 93 | return fmt.Sprintf("[GET /v3/clusters/count][%d] getClustersCount default %+v", o._statusCode, o.Payload) 94 | } 95 | 96 | func (o *GetClustersCountDefault) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 97 | 98 | o.Payload = new(models.Error) 99 | 100 | // response payload 101 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 102 | return err 103 | } 104 | 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/create_cluster_details_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | 10 | "github.com/go-swagger/go-swagger/client" 11 | "github.com/go-swagger/go-swagger/httpkit" 12 | 13 | strfmt "github.com/go-swagger/go-swagger/strfmt" 14 | 15 | "github.com/deis/workflow-manager/pkg/swagger/models" 16 | ) 17 | 18 | // CreateClusterDetailsReader is a Reader for the CreateClusterDetails structure. 19 | type CreateClusterDetailsReader struct { 20 | formats strfmt.Registry 21 | } 22 | 23 | // ReadResponse reads a server response into the recieved o. 24 | func (o *CreateClusterDetailsReader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 25 | switch response.Code() { 26 | 27 | case 200: 28 | result := NewCreateClusterDetailsOK() 29 | if err := result.readResponse(response, consumer, o.formats); err != nil { 30 | return nil, err 31 | } 32 | return result, nil 33 | 34 | default: 35 | result := NewCreateClusterDetailsDefault(response.Code()) 36 | if err := result.readResponse(response, consumer, o.formats); err != nil { 37 | return nil, err 38 | } 39 | return nil, result 40 | } 41 | } 42 | 43 | // NewCreateClusterDetailsOK creates a CreateClusterDetailsOK with default headers values 44 | func NewCreateClusterDetailsOK() *CreateClusterDetailsOK { 45 | return &CreateClusterDetailsOK{} 46 | } 47 | 48 | /*CreateClusterDetailsOK handles this case with default header values. 49 | 50 | clusters details response 51 | */ 52 | type CreateClusterDetailsOK struct { 53 | Payload *models.Cluster 54 | } 55 | 56 | func (o *CreateClusterDetailsOK) Error() string { 57 | return fmt.Sprintf("[POST /v3/clusters][%d] createClusterDetailsOK %+v", 200, o.Payload) 58 | } 59 | 60 | func (o *CreateClusterDetailsOK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 61 | 62 | o.Payload = new(models.Cluster) 63 | 64 | // response payload 65 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 66 | return err 67 | } 68 | 69 | return nil 70 | } 71 | 72 | // NewCreateClusterDetailsDefault creates a CreateClusterDetailsDefault with default headers values 73 | func NewCreateClusterDetailsDefault(code int) *CreateClusterDetailsDefault { 74 | return &CreateClusterDetailsDefault{ 75 | _statusCode: code, 76 | } 77 | } 78 | 79 | /*CreateClusterDetailsDefault handles this case with default header values. 80 | 81 | unexpected error 82 | */ 83 | type CreateClusterDetailsDefault struct { 84 | _statusCode int 85 | 86 | Payload *models.Error 87 | } 88 | 89 | // Code gets the status code for the create cluster details default response 90 | func (o *CreateClusterDetailsDefault) Code() int { 91 | return o._statusCode 92 | } 93 | 94 | func (o *CreateClusterDetailsDefault) Error() string { 95 | return fmt.Sprintf("[POST /v3/clusters][%d] createClusterDetails default %+v", o._statusCode, o.Payload) 96 | } 97 | 98 | func (o *CreateClusterDetailsDefault) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 99 | 100 | o.Payload = new(models.Error) 101 | 102 | // response payload 103 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 104 | return err 105 | } 106 | 107 | return nil 108 | } 109 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_component_by_release_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | 10 | "github.com/go-swagger/go-swagger/client" 11 | "github.com/go-swagger/go-swagger/httpkit" 12 | 13 | strfmt "github.com/go-swagger/go-swagger/strfmt" 14 | 15 | "github.com/deis/workflow-manager/pkg/swagger/models" 16 | ) 17 | 18 | // GetComponentByReleaseReader is a Reader for the GetComponentByRelease structure. 19 | type GetComponentByReleaseReader struct { 20 | formats strfmt.Registry 21 | } 22 | 23 | // ReadResponse reads a server response into the recieved o. 24 | func (o *GetComponentByReleaseReader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 25 | switch response.Code() { 26 | 27 | case 200: 28 | result := NewGetComponentByReleaseOK() 29 | if err := result.readResponse(response, consumer, o.formats); err != nil { 30 | return nil, err 31 | } 32 | return result, nil 33 | 34 | default: 35 | result := NewGetComponentByReleaseDefault(response.Code()) 36 | if err := result.readResponse(response, consumer, o.formats); err != nil { 37 | return nil, err 38 | } 39 | return nil, result 40 | } 41 | } 42 | 43 | // NewGetComponentByReleaseOK creates a GetComponentByReleaseOK with default headers values 44 | func NewGetComponentByReleaseOK() *GetComponentByReleaseOK { 45 | return &GetComponentByReleaseOK{} 46 | } 47 | 48 | /*GetComponentByReleaseOK handles this case with default header values. 49 | 50 | component release response 51 | */ 52 | type GetComponentByReleaseOK struct { 53 | Payload *models.ComponentVersion 54 | } 55 | 56 | func (o *GetComponentByReleaseOK) Error() string { 57 | return fmt.Sprintf("[GET /v3/versions/{train}/{component}/{release}][%d] getComponentByReleaseOK %+v", 200, o.Payload) 58 | } 59 | 60 | func (o *GetComponentByReleaseOK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 61 | 62 | o.Payload = new(models.ComponentVersion) 63 | 64 | // response payload 65 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 66 | return err 67 | } 68 | 69 | return nil 70 | } 71 | 72 | // NewGetComponentByReleaseDefault creates a GetComponentByReleaseDefault with default headers values 73 | func NewGetComponentByReleaseDefault(code int) *GetComponentByReleaseDefault { 74 | return &GetComponentByReleaseDefault{ 75 | _statusCode: code, 76 | } 77 | } 78 | 79 | /*GetComponentByReleaseDefault handles this case with default header values. 80 | 81 | unexpected error 82 | */ 83 | type GetComponentByReleaseDefault struct { 84 | _statusCode int 85 | 86 | Payload *models.Error 87 | } 88 | 89 | // Code gets the status code for the get component by release default response 90 | func (o *GetComponentByReleaseDefault) Code() int { 91 | return o._statusCode 92 | } 93 | 94 | func (o *GetComponentByReleaseDefault) Error() string { 95 | return fmt.Sprintf("[GET /v3/versions/{train}/{component}/{release}][%d] getComponentByRelease default %+v", o._statusCode, o.Payload) 96 | } 97 | 98 | func (o *GetComponentByReleaseDefault) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 99 | 100 | o.Payload = new(models.Error) 101 | 102 | // response payload 103 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 104 | return err 105 | } 106 | 107 | return nil 108 | } 109 | -------------------------------------------------------------------------------- /handlers/handlers.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | // handler echoes the HTTP request. 4 | import ( 5 | "encoding/json" 6 | "net/http" 7 | 8 | "github.com/deis/workflow-manager/config" 9 | "github.com/deis/workflow-manager/data" 10 | "github.com/deis/workflow-manager/k8s" 11 | apiclient "github.com/deis/workflow-manager/pkg/swagger/client" 12 | "github.com/deis/workflow-manager/pkg/swagger/client/operations" 13 | "github.com/gorilla/mux" 14 | "github.com/satori/go.uuid" 15 | ) 16 | 17 | const ( 18 | componentsRoute = "/components" // resource value for components route 19 | idRoute = "/id" // resource value for ID route 20 | doctorRoute = "/doctor" 21 | ) 22 | 23 | // RegisterRoutes attaches handler functions to routes 24 | func RegisterRoutes( 25 | r *mux.Router, 26 | availVers data.AvailableVersions, 27 | k8sResources *k8s.ResourceInterfaceNamespaced, 28 | ) *mux.Router { 29 | 30 | clusterID := data.NewClusterIDFromPersistentStorage(k8sResources.Secrets()) 31 | r.Handle(componentsRoute, ComponentsHandler( 32 | data.NewInstalledDeisData(k8sResources), 33 | clusterID, 34 | data.NewLatestReleasedComponent(k8sResources, availVers), 35 | )) 36 | r.Handle(idRoute, IDHandler(clusterID)) 37 | doctorAPIClient, _ := config.GetSwaggerClient(config.Spec.DoctorAPIURL) 38 | r.Handle(doctorRoute, DoctorHandler( 39 | data.NewInstalledDeisData(k8sResources), 40 | k8s.NewRunningK8sData(k8sResources), 41 | clusterID, 42 | data.NewLatestReleasedComponent(k8sResources, availVers), 43 | doctorAPIClient, 44 | )).Methods("POST") 45 | return r 46 | } 47 | 48 | // ComponentsHandler route handler 49 | func ComponentsHandler( 50 | workflow data.InstalledData, 51 | clusterID data.ClusterID, 52 | availVers data.AvailableComponentVersion, 53 | ) http.Handler { 54 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 55 | cluster, err := data.GetCluster(workflow, clusterID, availVers) 56 | if err != nil { 57 | http.Error(w, err.Error(), http.StatusInternalServerError) 58 | return 59 | } 60 | if err := json.NewEncoder(w).Encode(cluster); err != nil { 61 | http.Error(w, err.Error(), http.StatusInternalServerError) 62 | } 63 | }) 64 | } 65 | 66 | // DoctorHandler route handler 67 | func DoctorHandler( 68 | workflow data.InstalledData, 69 | k8sData k8s.RunningK8sData, 70 | clusterID data.ClusterID, 71 | availVers data.AvailableComponentVersion, 72 | apiClient *apiclient.WorkflowManager, 73 | ) http.Handler { 74 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 75 | doctor, err := data.GetDoctorInfo(workflow, k8sData, clusterID, availVers) 76 | if err != nil { 77 | http.Error(w, err.Error(), http.StatusInternalServerError) 78 | return 79 | } 80 | uid := uuid.NewV4().String() 81 | _, err = apiClient.Operations.PublishDoctorInfo(&operations.PublishDoctorInfoParams{Body: &doctor, UUID: uid}) 82 | if err != nil { 83 | http.Error(w, err.Error(), http.StatusInternalServerError) 84 | return 85 | } 86 | writePlainText(uid, w) 87 | }) 88 | } 89 | 90 | // IDHandler route handler 91 | func IDHandler(getter data.ClusterID) http.Handler { 92 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 93 | id, err := data.GetID(getter) 94 | if err != nil { 95 | http.Error(w, err.Error(), http.StatusInternalServerError) 96 | return 97 | } 98 | writePlainText(id, w) 99 | }) 100 | } 101 | 102 | // writePlainText is a helper function for writing HTTP text data 103 | func writePlainText(text string, w http.ResponseWriter) { 104 | w.Header().Set("Content-Type", "text/plain") 105 | w.Write([]byte(text)) 106 | } 107 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/create_cluster_details_for_v2_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | 10 | "github.com/go-swagger/go-swagger/client" 11 | "github.com/go-swagger/go-swagger/httpkit" 12 | 13 | strfmt "github.com/go-swagger/go-swagger/strfmt" 14 | 15 | "github.com/deis/workflow-manager/pkg/swagger/models" 16 | ) 17 | 18 | // CreateClusterDetailsForV2Reader is a Reader for the CreateClusterDetailsForV2 structure. 19 | type CreateClusterDetailsForV2Reader struct { 20 | formats strfmt.Registry 21 | } 22 | 23 | // ReadResponse reads a server response into the recieved o. 24 | func (o *CreateClusterDetailsForV2Reader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 25 | switch response.Code() { 26 | 27 | case 200: 28 | result := NewCreateClusterDetailsForV2OK() 29 | if err := result.readResponse(response, consumer, o.formats); err != nil { 30 | return nil, err 31 | } 32 | return result, nil 33 | 34 | default: 35 | result := NewCreateClusterDetailsForV2Default(response.Code()) 36 | if err := result.readResponse(response, consumer, o.formats); err != nil { 37 | return nil, err 38 | } 39 | return nil, result 40 | } 41 | } 42 | 43 | // NewCreateClusterDetailsForV2OK creates a CreateClusterDetailsForV2OK with default headers values 44 | func NewCreateClusterDetailsForV2OK() *CreateClusterDetailsForV2OK { 45 | return &CreateClusterDetailsForV2OK{} 46 | } 47 | 48 | /*CreateClusterDetailsForV2OK handles this case with default header values. 49 | 50 | clusters details response 51 | */ 52 | type CreateClusterDetailsForV2OK struct { 53 | Payload *models.Cluster 54 | } 55 | 56 | func (o *CreateClusterDetailsForV2OK) Error() string { 57 | return fmt.Sprintf("[POST /v2/clusters/{id}][%d] createClusterDetailsForV2OK %+v", 200, o.Payload) 58 | } 59 | 60 | func (o *CreateClusterDetailsForV2OK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 61 | 62 | o.Payload = new(models.Cluster) 63 | 64 | // response payload 65 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 66 | return err 67 | } 68 | 69 | return nil 70 | } 71 | 72 | // NewCreateClusterDetailsForV2Default creates a CreateClusterDetailsForV2Default with default headers values 73 | func NewCreateClusterDetailsForV2Default(code int) *CreateClusterDetailsForV2Default { 74 | return &CreateClusterDetailsForV2Default{ 75 | _statusCode: code, 76 | } 77 | } 78 | 79 | /*CreateClusterDetailsForV2Default handles this case with default header values. 80 | 81 | unexpected error 82 | */ 83 | type CreateClusterDetailsForV2Default struct { 84 | _statusCode int 85 | 86 | Payload *models.Error 87 | } 88 | 89 | // Code gets the status code for the create cluster details for v2 default response 90 | func (o *CreateClusterDetailsForV2Default) Code() int { 91 | return o._statusCode 92 | } 93 | 94 | func (o *CreateClusterDetailsForV2Default) Error() string { 95 | return fmt.Sprintf("[POST /v2/clusters/{id}][%d] createClusterDetailsForV2 default %+v", o._statusCode, o.Payload) 96 | } 97 | 98 | func (o *CreateClusterDetailsForV2Default) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 99 | 100 | o.Payload = new(models.Error) 101 | 102 | // response payload 103 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 104 | return err 105 | } 106 | 107 | return nil 108 | } 109 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/publish_component_release_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | 10 | "github.com/go-swagger/go-swagger/client" 11 | "github.com/go-swagger/go-swagger/httpkit" 12 | 13 | strfmt "github.com/go-swagger/go-swagger/strfmt" 14 | 15 | "github.com/deis/workflow-manager/pkg/swagger/models" 16 | ) 17 | 18 | // PublishComponentReleaseReader is a Reader for the PublishComponentRelease structure. 19 | type PublishComponentReleaseReader struct { 20 | formats strfmt.Registry 21 | } 22 | 23 | // ReadResponse reads a server response into the recieved o. 24 | func (o *PublishComponentReleaseReader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 25 | switch response.Code() { 26 | 27 | case 200: 28 | result := NewPublishComponentReleaseOK() 29 | if err := result.readResponse(response, consumer, o.formats); err != nil { 30 | return nil, err 31 | } 32 | return result, nil 33 | 34 | default: 35 | result := NewPublishComponentReleaseDefault(response.Code()) 36 | if err := result.readResponse(response, consumer, o.formats); err != nil { 37 | return nil, err 38 | } 39 | return nil, result 40 | } 41 | } 42 | 43 | // NewPublishComponentReleaseOK creates a PublishComponentReleaseOK with default headers values 44 | func NewPublishComponentReleaseOK() *PublishComponentReleaseOK { 45 | return &PublishComponentReleaseOK{} 46 | } 47 | 48 | /*PublishComponentReleaseOK handles this case with default header values. 49 | 50 | publish component release response 51 | */ 52 | type PublishComponentReleaseOK struct { 53 | Payload *models.ComponentVersion 54 | } 55 | 56 | func (o *PublishComponentReleaseOK) Error() string { 57 | return fmt.Sprintf("[POST /v3/versions/{train}/{component}/{release}][%d] publishComponentReleaseOK %+v", 200, o.Payload) 58 | } 59 | 60 | func (o *PublishComponentReleaseOK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 61 | 62 | o.Payload = new(models.ComponentVersion) 63 | 64 | // response payload 65 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 66 | return err 67 | } 68 | 69 | return nil 70 | } 71 | 72 | // NewPublishComponentReleaseDefault creates a PublishComponentReleaseDefault with default headers values 73 | func NewPublishComponentReleaseDefault(code int) *PublishComponentReleaseDefault { 74 | return &PublishComponentReleaseDefault{ 75 | _statusCode: code, 76 | } 77 | } 78 | 79 | /*PublishComponentReleaseDefault handles this case with default header values. 80 | 81 | unexpected error 82 | */ 83 | type PublishComponentReleaseDefault struct { 84 | _statusCode int 85 | 86 | Payload *models.Error 87 | } 88 | 89 | // Code gets the status code for the publish component release default response 90 | func (o *PublishComponentReleaseDefault) Code() int { 91 | return o._statusCode 92 | } 93 | 94 | func (o *PublishComponentReleaseDefault) Error() string { 95 | return fmt.Sprintf("[POST /v3/versions/{train}/{component}/{release}][%d] publishComponentRelease default %+v", o._statusCode, o.Payload) 96 | } 97 | 98 | func (o *PublishComponentReleaseDefault) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 99 | 100 | o.Payload = new(models.Error) 101 | 102 | // response payload 103 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 104 | return err 105 | } 106 | 107 | return nil 108 | } 109 | -------------------------------------------------------------------------------- /data/available_versions_test.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | "net/http/httptest" 8 | "sync" 9 | "testing" 10 | 11 | "github.com/arschles/assert" 12 | "github.com/deis/workflow-manager/config" 13 | "github.com/deis/workflow-manager/pkg/swagger/client/operations" 14 | "github.com/deis/workflow-manager/pkg/swagger/models" 15 | ) 16 | 17 | // Creating a novel mock struct that fulfills the AvailableVersions interface 18 | type testAvailableVersions struct{} 19 | 20 | func (a testAvailableVersions) Refresh(cluster models.Cluster) ([]models.ComponentVersion, error) { 21 | data := getMockComponentVersions() 22 | var componentVersions []models.ComponentVersion 23 | if err := json.Unmarshal(data, &componentVersions); err != nil { 24 | return nil, err 25 | } 26 | return componentVersions, nil 27 | } 28 | 29 | func (a testAvailableVersions) Store(c []models.ComponentVersion) { 30 | return 31 | } 32 | 33 | func (a testAvailableVersions) Cached() []models.ComponentVersion { 34 | return nil 35 | } 36 | 37 | // Creating another mock struct that fulfills the AvailableVersions interface 38 | type shouldBypassAvailableVersions struct{} 39 | 40 | func (a shouldBypassAvailableVersions) Refresh(cluster models.Cluster) ([]models.ComponentVersion, error) { 41 | var componentVersions []models.ComponentVersion 42 | data := []byte(fmt.Sprintf(`[{ 43 | "components": [ 44 | { 45 | "component": { 46 | "name": "bypass me", 47 | "description": "bypass me" 48 | }, 49 | "version": { 50 | "version": "v2-bypass" 51 | } 52 | } 53 | ] 54 | }]`)) 55 | if err := json.Unmarshal(data, &componentVersions); err != nil { 56 | return nil, err 57 | } 58 | return componentVersions, nil 59 | } 60 | 61 | func (a shouldBypassAvailableVersions) Store(c []models.ComponentVersion) { 62 | return 63 | } 64 | 65 | func (a shouldBypassAvailableVersions) Cached() []models.ComponentVersion { 66 | return nil 67 | } 68 | 69 | // Calls GetAvailableVersions twice, the first time we expect our passed-in struct w/ Refresh() method 70 | // to be invoked, the 2nd time we expect to receive the same value back (cached in memory) 71 | // and for the passed-in Refresh() method to be ignored 72 | func TestGetAvailableVersions(t *testing.T) { 73 | mock := getMockComponentVersions() 74 | var mockVersions []models.ComponentVersion 75 | assert.NoErr(t, json.Unmarshal(mock, &mockVersions)) 76 | versions, err := GetAvailableVersions(testAvailableVersions{}, models.Cluster{}) 77 | assert.NoErr(t, err) 78 | assert.Equal(t, versions, mockVersions, "component versions data") 79 | versions, err = GetAvailableVersions(shouldBypassAvailableVersions{}, models.Cluster{}) 80 | assert.NoErr(t, err) 81 | assert.Equal(t, versions, mockVersions, "component versions data") 82 | } 83 | 84 | func TestRefreshAvailableVersions(t *testing.T) { 85 | desc := "this is test1" 86 | updAvail := "nothing" 87 | expectedCompVsns := operations.GetComponentsByLatestReleaseOKBodyBody{ 88 | Data: []*models.ComponentVersion{ 89 | &models.ComponentVersion{ 90 | Component: &models.Component{Name: "test1", Description: &desc}, 91 | Version: &models.Version{Train: "testTrain"}, 92 | UpdateAvailable: &updAvail, 93 | }, 94 | }, 95 | } 96 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 97 | w.Header().Set("Content-Type", "application/json; charset=UTF-8") 98 | if err := json.NewEncoder(w).Encode(expectedCompVsns); err != nil { 99 | http.Error(w, "error encoding JSON", http.StatusInternalServerError) 100 | return 101 | } 102 | })) 103 | defer ts.Close() 104 | apiclient, err := config.GetSwaggerClient(ts.URL) 105 | assert.NoErr(t, err) 106 | vsns := availableVersionsFromAPI{ 107 | rwm: new(sync.RWMutex), 108 | baseVersionsURL: ts.URL, 109 | apiClient: apiclient, 110 | } 111 | retCompVsns, err := vsns.Refresh(models.Cluster{}) 112 | assert.NoErr(t, err) 113 | assert.Equal(t, len(retCompVsns), len(expectedCompVsns.Data), "number of component versions") 114 | } 115 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_clusters_by_age_parameters.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "github.com/go-swagger/go-swagger/client" 8 | "github.com/go-swagger/go-swagger/errors" 9 | 10 | strfmt "github.com/go-swagger/go-swagger/strfmt" 11 | ) 12 | 13 | // NewGetClustersByAgeParams creates a new GetClustersByAgeParams object 14 | // with the default values initialized. 15 | func NewGetClustersByAgeParams() *GetClustersByAgeParams { 16 | var () 17 | return &GetClustersByAgeParams{} 18 | } 19 | 20 | /*GetClustersByAgeParams contains all the parameters to send to the API endpoint 21 | for the get clusters by age operation typically these are written to a http.Request 22 | */ 23 | type GetClustersByAgeParams struct { 24 | 25 | /*CheckedInAfter*/ 26 | CheckedInAfter *strfmt.DateTime 27 | /*CheckedInBefore*/ 28 | CheckedInBefore *strfmt.DateTime 29 | /*CreatedAfter*/ 30 | CreatedAfter *strfmt.DateTime 31 | /*CreatedBefore*/ 32 | CreatedBefore *strfmt.DateTime 33 | } 34 | 35 | // WithCheckedInAfter adds the checkedInAfter to the get clusters by age params 36 | func (o *GetClustersByAgeParams) WithCheckedInAfter(checkedInAfter *strfmt.DateTime) *GetClustersByAgeParams { 37 | o.CheckedInAfter = checkedInAfter 38 | return o 39 | } 40 | 41 | // WithCheckedInBefore adds the checkedInBefore to the get clusters by age params 42 | func (o *GetClustersByAgeParams) WithCheckedInBefore(checkedInBefore *strfmt.DateTime) *GetClustersByAgeParams { 43 | o.CheckedInBefore = checkedInBefore 44 | return o 45 | } 46 | 47 | // WithCreatedAfter adds the createdAfter to the get clusters by age params 48 | func (o *GetClustersByAgeParams) WithCreatedAfter(createdAfter *strfmt.DateTime) *GetClustersByAgeParams { 49 | o.CreatedAfter = createdAfter 50 | return o 51 | } 52 | 53 | // WithCreatedBefore adds the createdBefore to the get clusters by age params 54 | func (o *GetClustersByAgeParams) WithCreatedBefore(createdBefore *strfmt.DateTime) *GetClustersByAgeParams { 55 | o.CreatedBefore = createdBefore 56 | return o 57 | } 58 | 59 | // WriteToRequest writes these params to a swagger request 60 | func (o *GetClustersByAgeParams) WriteToRequest(r client.Request, reg strfmt.Registry) error { 61 | 62 | var res []error 63 | 64 | if o.CheckedInAfter != nil { 65 | 66 | // query param checked_in_after 67 | var qrCheckedInAfter strfmt.DateTime 68 | if o.CheckedInAfter != nil { 69 | qrCheckedInAfter = *o.CheckedInAfter 70 | } 71 | qCheckedInAfter := qrCheckedInAfter.String() 72 | if qCheckedInAfter != "" { 73 | if err := r.SetQueryParam("checked_in_after", qCheckedInAfter); err != nil { 74 | return err 75 | } 76 | } 77 | 78 | } 79 | 80 | if o.CheckedInBefore != nil { 81 | 82 | // query param checked_in_before 83 | var qrCheckedInBefore strfmt.DateTime 84 | if o.CheckedInBefore != nil { 85 | qrCheckedInBefore = *o.CheckedInBefore 86 | } 87 | qCheckedInBefore := qrCheckedInBefore.String() 88 | if qCheckedInBefore != "" { 89 | if err := r.SetQueryParam("checked_in_before", qCheckedInBefore); err != nil { 90 | return err 91 | } 92 | } 93 | 94 | } 95 | 96 | if o.CreatedAfter != nil { 97 | 98 | // query param created_after 99 | var qrCreatedAfter strfmt.DateTime 100 | if o.CreatedAfter != nil { 101 | qrCreatedAfter = *o.CreatedAfter 102 | } 103 | qCreatedAfter := qrCreatedAfter.String() 104 | if qCreatedAfter != "" { 105 | if err := r.SetQueryParam("created_after", qCreatedAfter); err != nil { 106 | return err 107 | } 108 | } 109 | 110 | } 111 | 112 | if o.CreatedBefore != nil { 113 | 114 | // query param created_before 115 | var qrCreatedBefore strfmt.DateTime 116 | if o.CreatedBefore != nil { 117 | qrCreatedBefore = *o.CreatedBefore 118 | } 119 | qCreatedBefore := qrCreatedBefore.String() 120 | if qCreatedBefore != "" { 121 | if err := r.SetQueryParam("created_before", qCreatedBefore); err != nil { 122 | return err 123 | } 124 | } 125 | 126 | } 127 | 128 | if len(res) > 0 { 129 | return errors.CompositeValidationError(res...) 130 | } 131 | return nil 132 | } 133 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_clusters_by_age_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | 10 | "github.com/go-swagger/go-swagger/client" 11 | "github.com/go-swagger/go-swagger/errors" 12 | "github.com/go-swagger/go-swagger/httpkit" 13 | "github.com/go-swagger/go-swagger/httpkit/validate" 14 | 15 | strfmt "github.com/go-swagger/go-swagger/strfmt" 16 | 17 | "github.com/deis/workflow-manager/pkg/swagger/models" 18 | ) 19 | 20 | // GetClustersByAgeReader is a Reader for the GetClustersByAge structure. 21 | type GetClustersByAgeReader struct { 22 | formats strfmt.Registry 23 | } 24 | 25 | // ReadResponse reads a server response into the recieved o. 26 | func (o *GetClustersByAgeReader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 27 | switch response.Code() { 28 | 29 | case 200: 30 | result := NewGetClustersByAgeOK() 31 | if err := result.readResponse(response, consumer, o.formats); err != nil { 32 | return nil, err 33 | } 34 | return result, nil 35 | 36 | default: 37 | result := NewGetClustersByAgeDefault(response.Code()) 38 | if err := result.readResponse(response, consumer, o.formats); err != nil { 39 | return nil, err 40 | } 41 | return nil, result 42 | } 43 | } 44 | 45 | // NewGetClustersByAgeOK creates a GetClustersByAgeOK with default headers values 46 | func NewGetClustersByAgeOK() *GetClustersByAgeOK { 47 | return &GetClustersByAgeOK{} 48 | } 49 | 50 | /*GetClustersByAgeOK handles this case with default header values. 51 | 52 | clusters details response 53 | */ 54 | type GetClustersByAgeOK struct { 55 | Payload GetClustersByAgeOKBodyBody 56 | } 57 | 58 | func (o *GetClustersByAgeOK) Error() string { 59 | return fmt.Sprintf("[GET /v3/clusters/age][%d] getClustersByAgeOK %+v", 200, o.Payload) 60 | } 61 | 62 | func (o *GetClustersByAgeOK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 63 | 64 | // response payload 65 | if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { 66 | return err 67 | } 68 | 69 | return nil 70 | } 71 | 72 | // NewGetClustersByAgeDefault creates a GetClustersByAgeDefault with default headers values 73 | func NewGetClustersByAgeDefault(code int) *GetClustersByAgeDefault { 74 | return &GetClustersByAgeDefault{ 75 | _statusCode: code, 76 | } 77 | } 78 | 79 | /*GetClustersByAgeDefault handles this case with default header values. 80 | 81 | unexpected error 82 | */ 83 | type GetClustersByAgeDefault struct { 84 | _statusCode int 85 | 86 | Payload *models.Error 87 | } 88 | 89 | // Code gets the status code for the get clusters by age default response 90 | func (o *GetClustersByAgeDefault) Code() int { 91 | return o._statusCode 92 | } 93 | 94 | func (o *GetClustersByAgeDefault) Error() string { 95 | return fmt.Sprintf("[GET /v3/clusters/age][%d] getClustersByAge default %+v", o._statusCode, o.Payload) 96 | } 97 | 98 | func (o *GetClustersByAgeDefault) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 99 | 100 | o.Payload = new(models.Error) 101 | 102 | // response payload 103 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 104 | return err 105 | } 106 | 107 | return nil 108 | } 109 | 110 | /*GetClustersByAgeOKBodyBody get clusters by age o k body body 111 | 112 | swagger:model GetClustersByAgeOKBodyBody 113 | */ 114 | type GetClustersByAgeOKBodyBody struct { 115 | 116 | /* data 117 | 118 | Required: true 119 | */ 120 | Data []*models.Cluster `json:"data"` 121 | } 122 | 123 | // Validate validates this get clusters by age o k body body 124 | func (o *GetClustersByAgeOKBodyBody) Validate(formats strfmt.Registry) error { 125 | var res []error 126 | 127 | if err := o.validateData(formats); err != nil { 128 | // prop 129 | res = append(res, err) 130 | } 131 | 132 | if len(res) > 0 { 133 | return errors.CompositeValidationError(res...) 134 | } 135 | return nil 136 | } 137 | 138 | func (o *GetClustersByAgeOKBodyBody) validateData(formats strfmt.Registry) error { 139 | 140 | if err := validate.Required("getClustersByAgeOK"+"."+"data", "body", o.Data); err != nil { 141 | return err 142 | } 143 | 144 | for i := 0; i < len(o.Data); i++ { 145 | 146 | if o.Data[i] != nil { 147 | 148 | if err := o.Data[i].Validate(formats); err != nil { 149 | return err 150 | } 151 | } 152 | 153 | } 154 | 155 | return nil 156 | } 157 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_component_by_name_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | 10 | "github.com/go-swagger/go-swagger/client" 11 | "github.com/go-swagger/go-swagger/errors" 12 | "github.com/go-swagger/go-swagger/httpkit" 13 | "github.com/go-swagger/go-swagger/httpkit/validate" 14 | 15 | strfmt "github.com/go-swagger/go-swagger/strfmt" 16 | 17 | "github.com/deis/workflow-manager/pkg/swagger/models" 18 | ) 19 | 20 | // GetComponentByNameReader is a Reader for the GetComponentByName structure. 21 | type GetComponentByNameReader struct { 22 | formats strfmt.Registry 23 | } 24 | 25 | // ReadResponse reads a server response into the recieved o. 26 | func (o *GetComponentByNameReader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 27 | switch response.Code() { 28 | 29 | case 200: 30 | result := NewGetComponentByNameOK() 31 | if err := result.readResponse(response, consumer, o.formats); err != nil { 32 | return nil, err 33 | } 34 | return result, nil 35 | 36 | default: 37 | result := NewGetComponentByNameDefault(response.Code()) 38 | if err := result.readResponse(response, consumer, o.formats); err != nil { 39 | return nil, err 40 | } 41 | return nil, result 42 | } 43 | } 44 | 45 | // NewGetComponentByNameOK creates a GetComponentByNameOK with default headers values 46 | func NewGetComponentByNameOK() *GetComponentByNameOK { 47 | return &GetComponentByNameOK{} 48 | } 49 | 50 | /*GetComponentByNameOK handles this case with default header values. 51 | 52 | component releases response 53 | */ 54 | type GetComponentByNameOK struct { 55 | Payload GetComponentByNameOKBodyBody 56 | } 57 | 58 | func (o *GetComponentByNameOK) Error() string { 59 | return fmt.Sprintf("[GET /v3/versions/{train}/{component}][%d] getComponentByNameOK %+v", 200, o.Payload) 60 | } 61 | 62 | func (o *GetComponentByNameOK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 63 | 64 | // response payload 65 | if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { 66 | return err 67 | } 68 | 69 | return nil 70 | } 71 | 72 | // NewGetComponentByNameDefault creates a GetComponentByNameDefault with default headers values 73 | func NewGetComponentByNameDefault(code int) *GetComponentByNameDefault { 74 | return &GetComponentByNameDefault{ 75 | _statusCode: code, 76 | } 77 | } 78 | 79 | /*GetComponentByNameDefault handles this case with default header values. 80 | 81 | unexpected error 82 | */ 83 | type GetComponentByNameDefault struct { 84 | _statusCode int 85 | 86 | Payload *models.Error 87 | } 88 | 89 | // Code gets the status code for the get component by name default response 90 | func (o *GetComponentByNameDefault) Code() int { 91 | return o._statusCode 92 | } 93 | 94 | func (o *GetComponentByNameDefault) Error() string { 95 | return fmt.Sprintf("[GET /v3/versions/{train}/{component}][%d] getComponentByName default %+v", o._statusCode, o.Payload) 96 | } 97 | 98 | func (o *GetComponentByNameDefault) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 99 | 100 | o.Payload = new(models.Error) 101 | 102 | // response payload 103 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 104 | return err 105 | } 106 | 107 | return nil 108 | } 109 | 110 | /*GetComponentByNameOKBodyBody get component by name o k body body 111 | 112 | swagger:model GetComponentByNameOKBodyBody 113 | */ 114 | type GetComponentByNameOKBodyBody struct { 115 | 116 | /* data 117 | 118 | Required: true 119 | */ 120 | Data []*models.ComponentVersion `json:"data"` 121 | } 122 | 123 | // Validate validates this get component by name o k body body 124 | func (o *GetComponentByNameOKBodyBody) Validate(formats strfmt.Registry) error { 125 | var res []error 126 | 127 | if err := o.validateData(formats); err != nil { 128 | // prop 129 | res = append(res, err) 130 | } 131 | 132 | if len(res) > 0 { 133 | return errors.CompositeValidationError(res...) 134 | } 135 | return nil 136 | } 137 | 138 | func (o *GetComponentByNameOKBodyBody) validateData(formats strfmt.Registry) error { 139 | 140 | if err := validate.Required("getComponentByNameOK"+"."+"data", "body", o.Data); err != nil { 141 | return err 142 | } 143 | 144 | for i := 0; i < len(o.Data); i++ { 145 | 146 | if o.Data[i] != nil { 147 | 148 | if err := o.Data[i].Validate(formats); err != nil { 149 | return err 150 | } 151 | } 152 | 153 | } 154 | 155 | return nil 156 | } 157 | -------------------------------------------------------------------------------- /boot_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "testing" 10 | 11 | "github.com/arschles/assert" 12 | "github.com/deis/workflow-manager/config" 13 | "github.com/deis/workflow-manager/data" 14 | "github.com/deis/workflow-manager/handlers" 15 | "github.com/deis/workflow-manager/mocks" 16 | apiclient "github.com/deis/workflow-manager/pkg/swagger/client" 17 | "github.com/gorilla/mux" 18 | ) 19 | 20 | func newServer(apiClient *apiclient.WorkflowManager) *httptest.Server { 21 | r := mux.NewRouter() 22 | compHdl := handlers.ComponentsHandler( 23 | mocks.InstalledMockData{}, 24 | &mocks.ClusterIDMockData{}, 25 | mocks.LatestMockData{}, 26 | ) 27 | r.Handle("/components", compHdl) 28 | idHdl := handlers.IDHandler(&mocks.ClusterIDMockData{}) 29 | r.Handle("/id", idHdl) 30 | docHdl := handlers.DoctorHandler( 31 | mocks.InstalledMockData{}, 32 | mocks.RunningK8sMockData{}, // TODO: mock k8s node data 33 | &mocks.ClusterIDMockData{}, 34 | mocks.LatestMockData{}, 35 | apiClient, 36 | ) 37 | r.Handle("/doctor", docHdl).Methods("POST") 38 | return httptest.NewServer(r) 39 | } 40 | 41 | func TestGetComponents(t *testing.T) { 42 | const componentRoute = "/components" 43 | resp, apiServer, err := testGet(componentRoute) 44 | if apiServer != nil { 45 | apiServer.Close() 46 | } 47 | assert.NoErr(t, err) 48 | assert200(t, resp) 49 | respData, err := ioutil.ReadAll(resp.Body) 50 | assert.NoErr(t, err) 51 | cluster, err := data.ParseJSONCluster(respData) 52 | assert.NoErr(t, err) 53 | mockData, err := mocks.GetMockCluster() 54 | assert.NoErr(t, err) 55 | mockCluster, err := data.ParseJSONCluster(mockData) 56 | assert.NoErr(t, err) 57 | assert.Equal(t, cluster.ID, mockCluster.ID, "cluster ID value") 58 | for i, component := range cluster.Components { 59 | assert.Equal(t, component.Component, mockCluster.Components[i].Component, "component type") 60 | assert.Equal(t, component.Version, mockCluster.Components[i].Version, "version type") 61 | _, err := mocks.GetMockLatest(component.Component.Name) 62 | assert.NoErr(t, err) 63 | // TODO add tests for UpdateAvailable field 64 | } 65 | } 66 | 67 | func TestPostDoctor(t *testing.T) { 68 | const doctorRoute = "/doctor" 69 | resp, apiServer, err := testPostNoBody(doctorRoute) 70 | if apiServer != nil { 71 | apiServer.Close() 72 | } 73 | assert.NoErr(t, err) 74 | assert200(t, resp) 75 | } 76 | 77 | func TestGetID(t *testing.T) { 78 | const idRoute = "/id" 79 | resp, apiServer, err := testGet(idRoute) 80 | if apiServer != nil { 81 | apiServer.Close() 82 | } 83 | assert.NoErr(t, err) 84 | assert200(t, resp) 85 | respData, err := ioutil.ReadAll(resp.Body) 86 | assert.NoErr(t, err) 87 | mockData, err := mocks.GetMockClusterID() 88 | assert.NoErr(t, err) 89 | assert.Equal(t, string(respData), mockData, "id data response") 90 | } 91 | 92 | func testGet(route string) (*http.Response, *httptest.Server, error) { 93 | apiClient, apiServer, err := getWfmMockAPIClient([]byte("")) 94 | if err != nil { 95 | return nil, nil, err 96 | } 97 | server := newServer(apiClient) 98 | defer server.Close() 99 | resp, err := httpGet(server, route) 100 | if err != nil { 101 | return nil, nil, err 102 | } 103 | return resp, apiServer, nil 104 | } 105 | 106 | func testPostNoBody(route string) (*http.Response, *httptest.Server, error) { 107 | apiClient, apiServer, err := getWfmMockAPIClient([]byte("")) 108 | if err != nil { 109 | return nil, nil, err 110 | } 111 | server := newServer(apiClient) 112 | defer server.Close() 113 | resp, err := httpPost(server, route, "") 114 | if err != nil { 115 | return nil, nil, err 116 | } 117 | return resp, apiServer, nil 118 | } 119 | 120 | func getWfmMockAPIClient(respBody []byte) (*apiclient.WorkflowManager, *httptest.Server, error) { 121 | apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 122 | w.Header().Set("Content-Type", "application/json; charset=UTF-8") 123 | if err := json.NewEncoder(w).Encode(respBody); err != nil { 124 | http.Error(w, "error encoding JSON", http.StatusInternalServerError) 125 | return 126 | } 127 | })) 128 | apiClient, err := config.GetSwaggerClient(apiServer.URL) 129 | if err != nil { 130 | return nil, nil, err 131 | } 132 | return apiClient, apiServer, nil 133 | 134 | } 135 | 136 | func httpGet(s *httptest.Server, route string) (*http.Response, error) { 137 | return http.Get(s.URL + route) 138 | } 139 | 140 | func httpPost(s *httptest.Server, route string, json string) (*http.Response, error) { 141 | fullURL := s.URL + route 142 | const bodyType = "application/json" 143 | return http.Post(fullURL, bodyType, bytes.NewBuffer([]byte(json))) 144 | } 145 | 146 | func assert200(t *testing.T, resp *http.Response) { 147 | if resp.StatusCode != 200 { 148 | t.Fatalf("Received non-200 response: %d\n", resp.StatusCode) 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /jobs/jobs.go: -------------------------------------------------------------------------------- 1 | package jobs 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | "github.com/deis/workflow-manager/config" 8 | "github.com/deis/workflow-manager/data" 9 | "github.com/deis/workflow-manager/k8s" 10 | apiclient "github.com/deis/workflow-manager/pkg/swagger/client" 11 | "github.com/deis/workflow-manager/pkg/swagger/client/operations" 12 | ) 13 | 14 | // Periodic is an interface for managing periodic job invocation 15 | type Periodic interface { 16 | // Do begins the periodic job. It starts the first execution of the job, and then is 17 | // repsonsible for executing it every Frequency() thereafter 18 | Do() error 19 | Frequency() time.Duration 20 | } 21 | 22 | // SendVersions fulfills the Periodic interface 23 | type sendVersions struct { 24 | k8sResources *k8s.ResourceInterfaceNamespaced 25 | clusterID data.ClusterID 26 | apiClient *apiclient.WorkflowManager 27 | availableVersions data.AvailableVersions 28 | frequency time.Duration 29 | } 30 | 31 | // NewSendVersionsPeriodic creates a new SendVersions using sgc and rcl as the the secret getter / creator and replication controller lister implementations (respectively) 32 | func NewSendVersionsPeriodic( 33 | apiClient *apiclient.WorkflowManager, 34 | clusterID data.ClusterID, 35 | ri *k8s.ResourceInterfaceNamespaced, 36 | availableVersions data.AvailableVersions, 37 | frequency time.Duration, 38 | ) Periodic { 39 | return &sendVersions{ 40 | k8sResources: ri, 41 | clusterID: clusterID, 42 | apiClient: apiClient, 43 | availableVersions: availableVersions, 44 | frequency: frequency, 45 | } 46 | } 47 | 48 | // Do is the Periodic interface implementation 49 | func (s sendVersions) Do() error { 50 | if config.Spec.CheckVersions { 51 | err := sendVersionsImpl(s.apiClient, s.clusterID, s.k8sResources, s.availableVersions) 52 | if err != nil { 53 | return err 54 | } 55 | } 56 | return nil 57 | } 58 | 59 | // Frequency is the Periodic interface implementation 60 | func (s sendVersions) Frequency() time.Duration { 61 | return s.frequency 62 | } 63 | 64 | type getLatestVersionData struct { 65 | vsns data.AvailableVersions 66 | installedData data.InstalledData 67 | clusterID data.ClusterID 68 | availableComponentVsn data.AvailableComponentVersion 69 | k8sResources k8s.ResourceInterfaceNamespaced 70 | frequency time.Duration 71 | } 72 | 73 | // NewGetLatestVersionDataPeriodic creates a new periodic implementation that gets latest version data. It uses sgc and rcl as the secret getter/creator and replication controller lister implementations (respectively) 74 | func NewGetLatestVersionDataPeriodic( 75 | installedData data.InstalledData, 76 | clusterID data.ClusterID, 77 | availVsn data.AvailableVersions, 78 | availCompVsn data.AvailableComponentVersion, 79 | frequency time.Duration, 80 | ) Periodic { 81 | 82 | return &getLatestVersionData{ 83 | vsns: availVsn, 84 | installedData: installedData, 85 | clusterID: clusterID, 86 | availableComponentVsn: availCompVsn, 87 | frequency: frequency, 88 | } 89 | } 90 | 91 | // Do is the Periodic interface implementation 92 | func (u *getLatestVersionData) Do() error { 93 | cluster, err := data.GetCluster(u.installedData, u.clusterID, u.availableComponentVsn) 94 | if err != nil { 95 | return err 96 | } 97 | if _, err := u.vsns.Refresh(cluster); err != nil { 98 | return err 99 | } 100 | return nil 101 | } 102 | 103 | // Frequency is the Periodic interface implementation 104 | func (u getLatestVersionData) Frequency() time.Duration { 105 | return u.frequency 106 | } 107 | 108 | // DoPeriodic calls p.Do() once, and then again every p.Frequency() on each element p in pSlice. 109 | // For each p in pSlice, a new goroutine is started, and the returned channel can be closed 110 | // to stop all of the goroutines. 111 | func DoPeriodic(pSlice []Periodic) chan<- struct{} { 112 | doneCh := make(chan struct{}) 113 | for _, p := range pSlice { 114 | go func(p Periodic) { 115 | // execute once at the beginning 116 | err := p.Do() 117 | if err != nil { 118 | log.Printf("periodic job ran and returned error (%s)", err) 119 | } 120 | ticker := time.NewTicker(p.Frequency()) 121 | for { 122 | select { 123 | case <-ticker.C: 124 | err := p.Do() 125 | if err != nil { 126 | log.Printf("periodic job ran and returned error (%s)", err) 127 | } 128 | case <-doneCh: 129 | ticker.Stop() 130 | return 131 | } 132 | } 133 | }(p) 134 | } 135 | return doneCh 136 | } 137 | 138 | // sendVersions sends cluster version data 139 | func sendVersionsImpl( 140 | apiClient *apiclient.WorkflowManager, 141 | clusterID data.ClusterID, 142 | k8sResources *k8s.ResourceInterfaceNamespaced, 143 | availableVersions data.AvailableVersions, 144 | ) error { 145 | cluster, err := data.GetCluster( 146 | data.NewInstalledDeisData(k8sResources), 147 | clusterID, 148 | data.NewLatestReleasedComponent(k8sResources, availableVersions), 149 | ) 150 | if err != nil { 151 | log.Println("error getting installed components data") 152 | return err 153 | } 154 | 155 | _, err = apiClient.Operations.CreateClusterDetails(&operations.CreateClusterDetailsParams{Body: &cluster}) 156 | if err != nil { 157 | log.Println("error sending diagnostic data") 158 | return err 159 | } 160 | return nil 161 | } 162 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | |![](https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/Warning.svg/156px-Warning.svg.png) | Deis Workflow is no longer maintained.
Please [read the announcement](https://deis.com/blog/2017/deis-workflow-final-release/) for more detail. | 3 | |---:|---| 4 | | 09/07/2017 | Deis Workflow [v2.18][] final release before entering maintenance mode | 5 | | 03/01/2018 | End of Workflow maintenance: critical patches no longer merged | 6 | | | [Hephy](https://github.com/teamhephy/workflow) is a fork of Workflow that is actively developed and accepts code contributions. | 7 | 8 | # Deis Workflow Manager 9 | 10 | [![Build Status](https://travis-ci.org/deis/workflow-manager.svg?branch=master)](https://travis-ci.org/deis/workflow-manager) [![codecov](https://codecov.io/gh/deis/workflow-manager/branch/master/graph/badge.svg)](https://codecov.io/gh/deis/workflow-manager) 11 | [![Go Report Card](https://goreportcard.com/badge/github.com/deis/workflow-manager)](https://goreportcard.com/report/github.com/deis/workflow-manager) [![codebeat badge](https://codebeat.co/badges/29e2c379-0490-45db-95fe-20b25bd5a466)](https://codebeat.co/projects/github-com-deis-workflow-manager) 12 | [![Docker Repository on Quay](https://quay.io/repository/deis/workflow-manager/status "Docker Repository on Quay")](https://quay.io/repository/deis/workflow-manager) 13 | 14 | This repository contains the manager component for Deis Workflow. Deis 15 | (pronounced DAY-iss) Workflow is an open source Platform as a Service (PaaS) 16 | that adds a developer-friendly layer to any [Kubernetes][k8s-home] cluster, 17 | making it easy to deploy and manage applications on your own servers. 18 | 19 | For more information about Deis Workflow, please visit the main project page at 20 | https://github.com/deis/workflow. 21 | 22 | We welcome your input! If you have feedback on Workflow Manager, 23 | please [submit an issue][issues]. If you'd like to participate in development, 24 | please read the "Development" section below and [submit a pull request][prs]. 25 | 26 | ## Stay up to date 27 | 28 | One of the primary goals for Workflow Manager is notifying operators of 29 | component freshness. Workflow Manager will regularly check your cluster against 30 | the latest stable components. If components are missing due to failure or are 31 | simply out of date, Workflow operators will know at a glance. 32 | 33 | By default, Workflow Manager will make version checks to an external service. 34 | This submits component and version information to our versions service running 35 | at [https://versions.deis.com](https://versions.deis.com). If you prefer this 36 | check not happen, you may disable the function by setting 37 | `WORKFLOW_MANAGER_CHECKVERSIONS` to `false` in the Workflow Manager's 38 | Replication Controller. 39 | 40 | ## Workflow Doctor 41 | 42 | Deployed closest to any potential problem, Workflow Manager is also designed to 43 | help when things aren't going well. To aid troubleshooting efforts cluster 44 | operators will be able to easily gather and securely submit cluster health and 45 | status information to the Deis team. 46 | 47 | Functionality will be added in a later release. 48 | 49 | # Development 50 | 51 | The Deis project welcomes contributions from all developers. The high level 52 | process for development matches many other open source projects. See below for 53 | an outline. 54 | 55 | * Fork this repository 56 | * Make your changes 57 | * [Submit a pull request][prs] (PR) to this repository with your changes, and unit tests whenever possible 58 | * If your PR fixes any [issues][issues], make sure you write `Fixes #1234` in your PR description (where `#1234` is the number of the issue you're closing) 59 | * The Deis core contributors will review your code. After each of them sign off on your code, they'll label your PR with `LGTM1` and `LGTM2` (respectively). Once that happens, a contributor will merge it 60 | 61 | ## Docker Based Development Environment 62 | 63 | The preferred environment for development uses [the `go-dev` Docker 64 | image](https://github.com/deis/docker-go-dev). The tools described in this 65 | section are used to build, test, package and release each version of Deis. 66 | 67 | To use it yourself, you must have [make](https://www.gnu.org/software/make/) 68 | installed and Docker installed and running on your local development machine. 69 | 70 | If you don't have Docker installed, please go to https://www.docker.com/ to 71 | install it. 72 | 73 | After you have those dependencies, bootstrap dependencies with `make bootstrap`, 74 | build your code with `make build` and execute unit tests with `make test`. 75 | 76 | ## Native Go Development Environment 77 | 78 | You can also use the standard `go` toolchain to build and test if you prefer. 79 | To do so, you'll need [glide](https://github.com/Masterminds/glide) 0.9 or 80 | above and [Go 1.6](http://golang.org) or above installed. 81 | 82 | After you have those dependencies, you can build and unit-test your code with 83 | `go build` and `go test $(glide nv)`, respectively. 84 | 85 | Note that you will not be able to build or push Docker images using this method 86 | of development. 87 | 88 | # Testing 89 | 90 | The Deis project requires that as much code as possible is unit tested, but the 91 | core contributors also recognize that some code must be tested at a higher 92 | level (functional or integration tests, for example). 93 | 94 | 95 | [issues]: https://github.com/deis/workflow-manager/issues 96 | [prs]: https://github.com/deis/workflow-manager/pulls 97 | [k8s-home]: https://kubernetes.io 98 | [v2.18]: https://github.com/deis/workflow/releases/tag/v2.18.0 99 | -------------------------------------------------------------------------------- /pkg/swagger/models/namespace.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | strfmt "github.com/go-swagger/go-swagger/strfmt" 8 | 9 | "github.com/go-swagger/go-swagger/errors" 10 | "github.com/go-swagger/go-swagger/httpkit/validate" 11 | ) 12 | 13 | /*Namespace namespace 14 | 15 | swagger:model namespace 16 | */ 17 | type Namespace struct { 18 | 19 | /* daemon sets 20 | 21 | Required: true 22 | */ 23 | DaemonSets []*K8sResource `json:"daemonSets"` 24 | 25 | /* deployments 26 | 27 | Required: true 28 | */ 29 | Deployments []*K8sResource `json:"deployments"` 30 | 31 | /* events 32 | 33 | Required: true 34 | */ 35 | Events []*K8sResource `json:"events"` 36 | 37 | /* name 38 | 39 | Required: true 40 | */ 41 | Name string `json:"name"` 42 | 43 | /* pods 44 | 45 | Required: true 46 | */ 47 | Pods []*K8sResource `json:"pods"` 48 | 49 | /* replica sets 50 | 51 | Required: true 52 | */ 53 | ReplicaSets []*K8sResource `json:"replicaSets"` 54 | 55 | /* replication controllers 56 | 57 | Required: true 58 | */ 59 | ReplicationControllers []*K8sResource `json:"replicationControllers"` 60 | 61 | /* services 62 | 63 | Required: true 64 | */ 65 | Services []*K8sResource `json:"services"` 66 | } 67 | 68 | // Validate validates this namespace 69 | func (m *Namespace) Validate(formats strfmt.Registry) error { 70 | var res []error 71 | 72 | if err := m.validateDaemonSets(formats); err != nil { 73 | // prop 74 | res = append(res, err) 75 | } 76 | 77 | if err := m.validateDeployments(formats); err != nil { 78 | // prop 79 | res = append(res, err) 80 | } 81 | 82 | if err := m.validateEvents(formats); err != nil { 83 | // prop 84 | res = append(res, err) 85 | } 86 | 87 | if err := m.validateName(formats); err != nil { 88 | // prop 89 | res = append(res, err) 90 | } 91 | 92 | if err := m.validatePods(formats); err != nil { 93 | // prop 94 | res = append(res, err) 95 | } 96 | 97 | if err := m.validateReplicaSets(formats); err != nil { 98 | // prop 99 | res = append(res, err) 100 | } 101 | 102 | if err := m.validateReplicationControllers(formats); err != nil { 103 | // prop 104 | res = append(res, err) 105 | } 106 | 107 | if err := m.validateServices(formats); err != nil { 108 | // prop 109 | res = append(res, err) 110 | } 111 | 112 | if len(res) > 0 { 113 | return errors.CompositeValidationError(res...) 114 | } 115 | return nil 116 | } 117 | 118 | func (m *Namespace) validateDaemonSets(formats strfmt.Registry) error { 119 | 120 | if err := validate.Required("daemonSets", "body", m.DaemonSets); err != nil { 121 | return err 122 | } 123 | 124 | for i := 0; i < len(m.DaemonSets); i++ { 125 | 126 | if m.DaemonSets[i] != nil { 127 | 128 | if err := m.DaemonSets[i].Validate(formats); err != nil { 129 | return err 130 | } 131 | } 132 | 133 | } 134 | 135 | return nil 136 | } 137 | 138 | func (m *Namespace) validateDeployments(formats strfmt.Registry) error { 139 | 140 | if err := validate.Required("deployments", "body", m.Deployments); err != nil { 141 | return err 142 | } 143 | 144 | for i := 0; i < len(m.Deployments); i++ { 145 | 146 | if m.Deployments[i] != nil { 147 | 148 | if err := m.Deployments[i].Validate(formats); err != nil { 149 | return err 150 | } 151 | } 152 | 153 | } 154 | 155 | return nil 156 | } 157 | 158 | func (m *Namespace) validateEvents(formats strfmt.Registry) error { 159 | 160 | if err := validate.Required("events", "body", m.Events); err != nil { 161 | return err 162 | } 163 | 164 | for i := 0; i < len(m.Events); i++ { 165 | 166 | if m.Events[i] != nil { 167 | 168 | if err := m.Events[i].Validate(formats); err != nil { 169 | return err 170 | } 171 | } 172 | 173 | } 174 | 175 | return nil 176 | } 177 | 178 | func (m *Namespace) validateName(formats strfmt.Registry) error { 179 | 180 | if err := validate.RequiredString("name", "body", string(m.Name)); err != nil { 181 | return err 182 | } 183 | 184 | return nil 185 | } 186 | 187 | func (m *Namespace) validatePods(formats strfmt.Registry) error { 188 | 189 | if err := validate.Required("pods", "body", m.Pods); err != nil { 190 | return err 191 | } 192 | 193 | for i := 0; i < len(m.Pods); i++ { 194 | 195 | if m.Pods[i] != nil { 196 | 197 | if err := m.Pods[i].Validate(formats); err != nil { 198 | return err 199 | } 200 | } 201 | 202 | } 203 | 204 | return nil 205 | } 206 | 207 | func (m *Namespace) validateReplicaSets(formats strfmt.Registry) error { 208 | 209 | if err := validate.Required("replicaSets", "body", m.ReplicaSets); err != nil { 210 | return err 211 | } 212 | 213 | for i := 0; i < len(m.ReplicaSets); i++ { 214 | 215 | if m.ReplicaSets[i] != nil { 216 | 217 | if err := m.ReplicaSets[i].Validate(formats); err != nil { 218 | return err 219 | } 220 | } 221 | 222 | } 223 | 224 | return nil 225 | } 226 | 227 | func (m *Namespace) validateReplicationControllers(formats strfmt.Registry) error { 228 | 229 | if err := validate.Required("replicationControllers", "body", m.ReplicationControllers); err != nil { 230 | return err 231 | } 232 | 233 | for i := 0; i < len(m.ReplicationControllers); i++ { 234 | 235 | if m.ReplicationControllers[i] != nil { 236 | 237 | if err := m.ReplicationControllers[i].Validate(formats); err != nil { 238 | return err 239 | } 240 | } 241 | 242 | } 243 | 244 | return nil 245 | } 246 | 247 | func (m *Namespace) validateServices(formats strfmt.Registry) error { 248 | 249 | if err := validate.Required("services", "body", m.Services); err != nil { 250 | return err 251 | } 252 | 253 | for i := 0; i < len(m.Services); i++ { 254 | 255 | if m.Services[i] != nil { 256 | 257 | if err := m.Services[i].Validate(formats); err != nil { 258 | return err 259 | } 260 | } 261 | 262 | } 263 | 264 | return nil 265 | } 266 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_components_by_latest_release_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | 10 | "github.com/go-swagger/go-swagger/client" 11 | "github.com/go-swagger/go-swagger/errors" 12 | "github.com/go-swagger/go-swagger/httpkit" 13 | "github.com/go-swagger/go-swagger/httpkit/validate" 14 | "github.com/go-swagger/go-swagger/swag" 15 | 16 | strfmt "github.com/go-swagger/go-swagger/strfmt" 17 | 18 | "github.com/deis/workflow-manager/pkg/swagger/models" 19 | ) 20 | 21 | // GetComponentsByLatestReleaseReader is a Reader for the GetComponentsByLatestRelease structure. 22 | type GetComponentsByLatestReleaseReader struct { 23 | formats strfmt.Registry 24 | } 25 | 26 | // ReadResponse reads a server response into the recieved o. 27 | func (o *GetComponentsByLatestReleaseReader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 28 | switch response.Code() { 29 | 30 | case 200: 31 | result := NewGetComponentsByLatestReleaseOK() 32 | if err := result.readResponse(response, consumer, o.formats); err != nil { 33 | return nil, err 34 | } 35 | return result, nil 36 | 37 | default: 38 | result := NewGetComponentsByLatestReleaseDefault(response.Code()) 39 | if err := result.readResponse(response, consumer, o.formats); err != nil { 40 | return nil, err 41 | } 42 | return nil, result 43 | } 44 | } 45 | 46 | // NewGetComponentsByLatestReleaseOK creates a GetComponentsByLatestReleaseOK with default headers values 47 | func NewGetComponentsByLatestReleaseOK() *GetComponentsByLatestReleaseOK { 48 | return &GetComponentsByLatestReleaseOK{} 49 | } 50 | 51 | /*GetComponentsByLatestReleaseOK handles this case with default header values. 52 | 53 | component releases response 54 | */ 55 | type GetComponentsByLatestReleaseOK struct { 56 | Payload GetComponentsByLatestReleaseOKBodyBody 57 | } 58 | 59 | func (o *GetComponentsByLatestReleaseOK) Error() string { 60 | return fmt.Sprintf("[POST /v3/versions/latest][%d] getComponentsByLatestReleaseOK %+v", 200, o.Payload) 61 | } 62 | 63 | func (o *GetComponentsByLatestReleaseOK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 64 | 65 | // response payload 66 | if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { 67 | return err 68 | } 69 | 70 | return nil 71 | } 72 | 73 | // NewGetComponentsByLatestReleaseDefault creates a GetComponentsByLatestReleaseDefault with default headers values 74 | func NewGetComponentsByLatestReleaseDefault(code int) *GetComponentsByLatestReleaseDefault { 75 | return &GetComponentsByLatestReleaseDefault{ 76 | _statusCode: code, 77 | } 78 | } 79 | 80 | /*GetComponentsByLatestReleaseDefault handles this case with default header values. 81 | 82 | unexpected error 83 | */ 84 | type GetComponentsByLatestReleaseDefault struct { 85 | _statusCode int 86 | 87 | Payload *models.Error 88 | } 89 | 90 | // Code gets the status code for the get components by latest release default response 91 | func (o *GetComponentsByLatestReleaseDefault) Code() int { 92 | return o._statusCode 93 | } 94 | 95 | func (o *GetComponentsByLatestReleaseDefault) Error() string { 96 | return fmt.Sprintf("[POST /v3/versions/latest][%d] getComponentsByLatestRelease default %+v", o._statusCode, o.Payload) 97 | } 98 | 99 | func (o *GetComponentsByLatestReleaseDefault) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 100 | 101 | o.Payload = new(models.Error) 102 | 103 | // response payload 104 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 105 | return err 106 | } 107 | 108 | return nil 109 | } 110 | 111 | /*GetComponentsByLatestReleaseBody get components by latest release body 112 | 113 | swagger:model GetComponentsByLatestReleaseBody 114 | */ 115 | type GetComponentsByLatestReleaseBody struct { 116 | 117 | /* data 118 | */ 119 | Data []*models.ComponentVersion `json:"data,omitempty"` 120 | } 121 | 122 | // Validate validates this get components by latest release body 123 | func (o *GetComponentsByLatestReleaseBody) Validate(formats strfmt.Registry) error { 124 | var res []error 125 | 126 | if err := o.validateData(formats); err != nil { 127 | // prop 128 | res = append(res, err) 129 | } 130 | 131 | if len(res) > 0 { 132 | return errors.CompositeValidationError(res...) 133 | } 134 | return nil 135 | } 136 | 137 | func (o *GetComponentsByLatestReleaseBody) validateData(formats strfmt.Registry) error { 138 | 139 | if swag.IsZero(o.Data) { // not required 140 | return nil 141 | } 142 | 143 | for i := 0; i < len(o.Data); i++ { 144 | 145 | if o.Data[i] != nil { 146 | 147 | if err := o.Data[i].Validate(formats); err != nil { 148 | return err 149 | } 150 | } 151 | 152 | } 153 | 154 | return nil 155 | } 156 | 157 | /*GetComponentsByLatestReleaseOKBodyBody get components by latest release o k body body 158 | 159 | swagger:model GetComponentsByLatestReleaseOKBodyBody 160 | */ 161 | type GetComponentsByLatestReleaseOKBodyBody struct { 162 | 163 | /* data 164 | 165 | Required: true 166 | */ 167 | Data []*models.ComponentVersion `json:"data"` 168 | } 169 | 170 | // Validate validates this get components by latest release o k body body 171 | func (o *GetComponentsByLatestReleaseOKBodyBody) Validate(formats strfmt.Registry) error { 172 | var res []error 173 | 174 | if err := o.validateData(formats); err != nil { 175 | // prop 176 | res = append(res, err) 177 | } 178 | 179 | if len(res) > 0 { 180 | return errors.CompositeValidationError(res...) 181 | } 182 | return nil 183 | } 184 | 185 | func (o *GetComponentsByLatestReleaseOKBodyBody) validateData(formats strfmt.Registry) error { 186 | 187 | if err := validate.Required("getComponentsByLatestReleaseOK"+"."+"data", "body", o.Data); err != nil { 188 | return err 189 | } 190 | 191 | for i := 0; i < len(o.Data); i++ { 192 | 193 | if o.Data[i] != nil { 194 | 195 | if err := o.Data[i].Validate(formats); err != nil { 196 | return err 197 | } 198 | } 199 | 200 | } 201 | 202 | return nil 203 | } 204 | -------------------------------------------------------------------------------- /mocks/mocks.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "strings" 10 | 11 | "github.com/deis/workflow-manager/pkg/swagger/models" 12 | ) 13 | 14 | const mainPackage = "workflow-manager" 15 | 16 | func getMocksWd() string { 17 | cwd, err := os.Getwd() 18 | if err != nil { 19 | log.Println(err) 20 | } 21 | cwdSplit := strings.Split(cwd, "/") 22 | if (cwdSplit[len(cwdSplit)-1]) != mainPackage { 23 | cwdSplit = cwdSplit[:len(cwdSplit)-1] // strip last directory 24 | return strings.Join(cwdSplit, "/") + "/mocks/" 25 | } 26 | return cwd + "/mocks/" 27 | } 28 | 29 | // InstalledMockData mock data struct 30 | type InstalledMockData struct{} 31 | 32 | // Get method for InstalledMockData 33 | func (g InstalledMockData) Get() ([]byte, error) { 34 | data, err := GetMockComponents() 35 | if err != nil { 36 | log.Print(err) 37 | return nil, err 38 | } 39 | return data, nil 40 | } 41 | 42 | // RunningK8sMockData data struct 43 | type RunningK8sMockData struct{} 44 | 45 | // DaemonSets method for RunningK8sMockData 46 | func (k RunningK8sMockData) DaemonSets() ([]*models.K8sResource, error) { 47 | // TODO: implement 48 | return []*models.K8sResource{}, nil 49 | } 50 | 51 | // Deployments method for RunningK8sMockData 52 | func (k RunningK8sMockData) Deployments() ([]*models.K8sResource, error) { 53 | // TODO: implement 54 | return []*models.K8sResource{}, nil 55 | } 56 | 57 | // Events method for RunningK8sMockData 58 | func (k RunningK8sMockData) Events() ([]*models.K8sResource, error) { 59 | // TODO: implement 60 | return []*models.K8sResource{}, nil 61 | } 62 | 63 | // Nodes method for RunningK8sMockData 64 | func (k RunningK8sMockData) Nodes() ([]*models.K8sResource, error) { 65 | // TODO: implement 66 | return []*models.K8sResource{}, nil 67 | } 68 | 69 | // Pods method for RunningK8sMockData 70 | func (k RunningK8sMockData) Pods() ([]*models.K8sResource, error) { 71 | // TODO: implement 72 | return []*models.K8sResource{}, nil 73 | } 74 | 75 | // ReplicaSets method for RunningK8sMockData 76 | func (k RunningK8sMockData) ReplicaSets() ([]*models.K8sResource, error) { 77 | // TODO: implement 78 | return []*models.K8sResource{}, nil 79 | } 80 | 81 | // ReplicationControllers method for RunningK8sMockData 82 | func (k RunningK8sMockData) ReplicationControllers() ([]*models.K8sResource, error) { 83 | // TODO: implement 84 | return []*models.K8sResource{}, nil 85 | } 86 | 87 | // Services method for RunningK8sMockData 88 | func (k RunningK8sMockData) Services() ([]*models.K8sResource, error) { 89 | // TODO: implement 90 | return []*models.K8sResource{}, nil 91 | } 92 | 93 | // ClusterIDMockData mock data struct 94 | type ClusterIDMockData struct { 95 | cache string 96 | } 97 | 98 | // Get is the ClusterID interface implementation 99 | func (c ClusterIDMockData) Get() (string, error) { 100 | data, err := GetMockClusterID() 101 | if err != nil { 102 | log.Print(err) 103 | return "", err 104 | } 105 | return data, nil 106 | } 107 | 108 | // Cached is the ClusterID interface implementation 109 | func (c ClusterIDMockData) Cached() string { 110 | return c.cache 111 | } 112 | 113 | // StoreInCache is the ClusterID interface implementation 114 | func (c *ClusterIDMockData) StoreInCache(cid string) { 115 | c.cache = cid 116 | } 117 | 118 | // LatestMockData mock data struct 119 | type LatestMockData struct{} 120 | 121 | // Get method for LatestMockData 122 | func (c LatestMockData) Get(component string, cluster models.Cluster) (models.Version, error) { 123 | data, err := GetMockLatest(component) 124 | if err != nil { 125 | log.Print(err) 126 | return models.Version{}, err 127 | } 128 | return data, nil 129 | } 130 | 131 | // GetMockCluster returns a mock JSON cluster response 132 | func GetMockCluster() ([]byte, error) { 133 | data, err := getJSON(getMocksWd() + "cluster.json") 134 | if err != nil { 135 | return nil, err 136 | } 137 | return data, nil 138 | } 139 | 140 | // GetMockComponents returns a mock JSON cluster response 141 | func GetMockComponents() ([]byte, error) { 142 | data, err := getJSON(getMocksWd() + "components.json") 143 | if err != nil { 144 | return nil, err 145 | } 146 | return data, nil 147 | } 148 | 149 | // GetMockClusterPost returns mock JSON cluster POST data 150 | func GetMockClusterPost() ([]byte, error) { 151 | data, err := getJSON(getMocksWd() + "cluster-post.json") 152 | if err != nil { 153 | return nil, err 154 | } 155 | return data, nil 156 | } 157 | 158 | // GetMockClusterID returns a mock JSON cluster response 159 | func GetMockClusterID() (string, error) { 160 | data, err := getText(getMocksWd() + "id.txt") 161 | if err != nil { 162 | return "", err 163 | } 164 | return string(data), nil 165 | } 166 | 167 | // GetMockLatest returns a mock "latest component version" response 168 | func GetMockLatest(c string) (models.Version, error) { 169 | data, err := getText(getMocksWd() + "latest-component-version-" + c + ".txt") 170 | if err != nil { 171 | return models.Version{}, err 172 | } 173 | return models.Version{Version: data}, nil 174 | } 175 | 176 | // GetMockComponentV2Beta returns a mock "latest component version" response, for v2-beta 177 | func GetMockComponentV2Beta() ([]byte, error) { 178 | data, err := getJSON(getMocksWd() + "latest-component-version-v2-beta.json") 179 | if err != nil { 180 | return nil, err 181 | } 182 | return data, nil 183 | } 184 | 185 | // getJSON gets a JSON file from the local filesystem 186 | func getJSON(filepath string) ([]byte, error) { 187 | data, err := ioutil.ReadFile(filepath) 188 | if err != nil { 189 | log.Printf("Error reading .json file: %#v\n", err) 190 | return nil, err 191 | } 192 | if !isJSON(data) { 193 | return nil, fmt.Errorf("data is not valid JSON") 194 | } 195 | return data, nil 196 | } 197 | 198 | // getText gets a text file from the local filesystem 199 | func getText(filepath string) (string, error) { 200 | data, err := ioutil.ReadFile(filepath) 201 | if err != nil { 202 | log.Printf("Error reading .txt file: %#v\n", err) 203 | return "", err 204 | } 205 | return strings.TrimSpace(string(data)), nil 206 | } 207 | 208 | // isJSON checks for valid JSON 209 | func isJSON(b []byte) bool { 210 | var js map[string]interface{} 211 | return json.Unmarshal(b, &js) == nil 212 | } 213 | -------------------------------------------------------------------------------- /pkg/swagger/client/operations/get_components_by_latest_release_for_v2_responses.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | 10 | "github.com/go-swagger/go-swagger/client" 11 | "github.com/go-swagger/go-swagger/errors" 12 | "github.com/go-swagger/go-swagger/httpkit" 13 | "github.com/go-swagger/go-swagger/httpkit/validate" 14 | "github.com/go-swagger/go-swagger/swag" 15 | 16 | strfmt "github.com/go-swagger/go-swagger/strfmt" 17 | 18 | "github.com/deis/workflow-manager/pkg/swagger/models" 19 | ) 20 | 21 | // GetComponentsByLatestReleaseForV2Reader is a Reader for the GetComponentsByLatestReleaseForV2 structure. 22 | type GetComponentsByLatestReleaseForV2Reader struct { 23 | formats strfmt.Registry 24 | } 25 | 26 | // ReadResponse reads a server response into the recieved o. 27 | func (o *GetComponentsByLatestReleaseForV2Reader) ReadResponse(response client.Response, consumer httpkit.Consumer) (interface{}, error) { 28 | switch response.Code() { 29 | 30 | case 200: 31 | result := NewGetComponentsByLatestReleaseForV2OK() 32 | if err := result.readResponse(response, consumer, o.formats); err != nil { 33 | return nil, err 34 | } 35 | return result, nil 36 | 37 | default: 38 | result := NewGetComponentsByLatestReleaseForV2Default(response.Code()) 39 | if err := result.readResponse(response, consumer, o.formats); err != nil { 40 | return nil, err 41 | } 42 | return nil, result 43 | } 44 | } 45 | 46 | // NewGetComponentsByLatestReleaseForV2OK creates a GetComponentsByLatestReleaseForV2OK with default headers values 47 | func NewGetComponentsByLatestReleaseForV2OK() *GetComponentsByLatestReleaseForV2OK { 48 | return &GetComponentsByLatestReleaseForV2OK{} 49 | } 50 | 51 | /*GetComponentsByLatestReleaseForV2OK handles this case with default header values. 52 | 53 | component releases response 54 | */ 55 | type GetComponentsByLatestReleaseForV2OK struct { 56 | Payload GetComponentsByLatestReleaseForV2OKBodyBody 57 | } 58 | 59 | func (o *GetComponentsByLatestReleaseForV2OK) Error() string { 60 | return fmt.Sprintf("[POST /v2/versions/latest][%d] getComponentsByLatestReleaseForV2OK %+v", 200, o.Payload) 61 | } 62 | 63 | func (o *GetComponentsByLatestReleaseForV2OK) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 64 | 65 | // response payload 66 | if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { 67 | return err 68 | } 69 | 70 | return nil 71 | } 72 | 73 | // NewGetComponentsByLatestReleaseForV2Default creates a GetComponentsByLatestReleaseForV2Default with default headers values 74 | func NewGetComponentsByLatestReleaseForV2Default(code int) *GetComponentsByLatestReleaseForV2Default { 75 | return &GetComponentsByLatestReleaseForV2Default{ 76 | _statusCode: code, 77 | } 78 | } 79 | 80 | /*GetComponentsByLatestReleaseForV2Default handles this case with default header values. 81 | 82 | unexpected error 83 | */ 84 | type GetComponentsByLatestReleaseForV2Default struct { 85 | _statusCode int 86 | 87 | Payload *models.Error 88 | } 89 | 90 | // Code gets the status code for the get components by latest release for v2 default response 91 | func (o *GetComponentsByLatestReleaseForV2Default) Code() int { 92 | return o._statusCode 93 | } 94 | 95 | func (o *GetComponentsByLatestReleaseForV2Default) Error() string { 96 | return fmt.Sprintf("[POST /v2/versions/latest][%d] getComponentsByLatestReleaseForV2 default %+v", o._statusCode, o.Payload) 97 | } 98 | 99 | func (o *GetComponentsByLatestReleaseForV2Default) readResponse(response client.Response, consumer httpkit.Consumer, formats strfmt.Registry) error { 100 | 101 | o.Payload = new(models.Error) 102 | 103 | // response payload 104 | if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { 105 | return err 106 | } 107 | 108 | return nil 109 | } 110 | 111 | /*GetComponentsByLatestReleaseForV2Body get components by latest release for v2 body 112 | 113 | swagger:model GetComponentsByLatestReleaseForV2Body 114 | */ 115 | type GetComponentsByLatestReleaseForV2Body struct { 116 | 117 | /* data 118 | */ 119 | Data []*models.ComponentVersion `json:"data,omitempty"` 120 | } 121 | 122 | // Validate validates this get components by latest release for v2 body 123 | func (o *GetComponentsByLatestReleaseForV2Body) Validate(formats strfmt.Registry) error { 124 | var res []error 125 | 126 | if err := o.validateData(formats); err != nil { 127 | // prop 128 | res = append(res, err) 129 | } 130 | 131 | if len(res) > 0 { 132 | return errors.CompositeValidationError(res...) 133 | } 134 | return nil 135 | } 136 | 137 | func (o *GetComponentsByLatestReleaseForV2Body) validateData(formats strfmt.Registry) error { 138 | 139 | if swag.IsZero(o.Data) { // not required 140 | return nil 141 | } 142 | 143 | for i := 0; i < len(o.Data); i++ { 144 | 145 | if o.Data[i] != nil { 146 | 147 | if err := o.Data[i].Validate(formats); err != nil { 148 | return err 149 | } 150 | } 151 | 152 | } 153 | 154 | return nil 155 | } 156 | 157 | /*GetComponentsByLatestReleaseForV2OKBodyBody get components by latest release for v2 o k body body 158 | 159 | swagger:model GetComponentsByLatestReleaseForV2OKBodyBody 160 | */ 161 | type GetComponentsByLatestReleaseForV2OKBodyBody struct { 162 | 163 | /* data 164 | 165 | Required: true 166 | */ 167 | Data []*models.ComponentVersion `json:"data"` 168 | } 169 | 170 | // Validate validates this get components by latest release for v2 o k body body 171 | func (o *GetComponentsByLatestReleaseForV2OKBodyBody) Validate(formats strfmt.Registry) error { 172 | var res []error 173 | 174 | if err := o.validateData(formats); err != nil { 175 | // prop 176 | res = append(res, err) 177 | } 178 | 179 | if len(res) > 0 { 180 | return errors.CompositeValidationError(res...) 181 | } 182 | return nil 183 | } 184 | 185 | func (o *GetComponentsByLatestReleaseForV2OKBodyBody) validateData(formats strfmt.Registry) error { 186 | 187 | if err := validate.Required("getComponentsByLatestReleaseForV2OK"+"."+"data", "body", o.Data); err != nil { 188 | return err 189 | } 190 | 191 | for i := 0; i < len(o.Data); i++ { 192 | 193 | if o.Data[i] != nil { 194 | 195 | if err := o.Data[i].Validate(formats); err != nil { 196 | return err 197 | } 198 | } 199 | 200 | } 201 | 202 | return nil 203 | } 204 | -------------------------------------------------------------------------------- /data/data.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "strings" 8 | 9 | "github.com/deis/workflow-manager/config" 10 | "github.com/deis/workflow-manager/k8s" 11 | "github.com/deis/workflow-manager/pkg/swagger/models" 12 | ) 13 | 14 | const ( 15 | wfmSecretName = "deis-workflow-manager" 16 | clusterIDSecretKey = "cluster-id" 17 | ) 18 | 19 | // GetCluster collects all cluster metadata and returns a Cluster 20 | func GetCluster( 21 | c InstalledData, 22 | i ClusterID, 23 | v AvailableComponentVersion, 24 | ) (models.Cluster, error) { 25 | 26 | // Populate cluster object with installed components 27 | cluster, err := GetInstalled(c) 28 | if err != nil { 29 | return models.Cluster{}, err 30 | } 31 | if err := AddUpdateData(&cluster, v); err != nil { 32 | log.Printf("unable to decorate cluster data with available updates data: %#v", err) 33 | } 34 | // Get the cluster ID 35 | id, err := GetID(i) 36 | if err != nil { 37 | return cluster, err 38 | } 39 | // Attach the cluster ID to the components-populated cluster object 40 | cluster.ID = id 41 | return cluster, nil 42 | } 43 | 44 | // AddUpdateData adds UpdateAvailable field data to cluster components 45 | // Any cluster object modifications are made "in-place" 46 | func AddUpdateData(c *models.Cluster, v AvailableComponentVersion) error { 47 | // Determine if any components have an available update 48 | for i, component := range c.Components { 49 | installed := component.Version.Version 50 | latest, err := v.Get(component.Component.Name, *c) 51 | if err != nil { 52 | return err 53 | } 54 | newest := newestVersion(installed, latest.Version) 55 | if newest != installed { 56 | c.Components[i].UpdateAvailable = &newest 57 | } 58 | } 59 | return nil 60 | } 61 | 62 | // GetAvailableVersions gets available component version data from the cache. If there was a cache miss, gets the versions from the k8s and versions APIs 63 | func GetAvailableVersions(a AvailableVersions, cluster models.Cluster) ([]models.ComponentVersion, error) { 64 | // First, check to see if we have an in-memory copy 65 | data := a.Cached() 66 | // If we don't have any cached data, get the data from the remote authority 67 | if len(data) == 0 { 68 | d, err := a.Refresh(cluster) 69 | if err != nil { 70 | return nil, err 71 | } 72 | data = d 73 | } 74 | return data, nil 75 | } 76 | 77 | // GetInstalled collects all installed components and returns a Cluster 78 | func GetInstalled(g InstalledData) (models.Cluster, error) { 79 | installed, err := g.Get() 80 | if err != nil { 81 | return models.Cluster{}, err 82 | } 83 | var cluster models.Cluster 84 | cluster, err = ParseJSONCluster(installed) 85 | if err != nil { 86 | return models.Cluster{}, err 87 | } 88 | return cluster, nil 89 | } 90 | 91 | // GetLatestVersion returns the latest known version of a deis component 92 | func GetLatestVersion( 93 | component string, 94 | cluster models.Cluster, 95 | availVsns AvailableVersions, 96 | ) (models.Version, error) { 97 | var latestVersion models.Version 98 | latestVersions, err := GetAvailableVersions(availVsns, cluster) 99 | if err != nil { 100 | return models.Version{}, err 101 | } 102 | for _, componentVersion := range latestVersions { 103 | if componentVersion.Component.Name == component { 104 | latestVersion = *componentVersion.Version 105 | } 106 | } 107 | if latestVersion.Version == "" { 108 | return models.Version{}, fmt.Errorf("latest version not available for %s", component) 109 | } 110 | return latestVersion, nil 111 | } 112 | 113 | // ParseJSONCluster converts a JSON representation of a cluster 114 | // to a Cluster type 115 | func ParseJSONCluster(rawJSON []byte) (models.Cluster, error) { 116 | var cluster models.Cluster 117 | if err := json.Unmarshal(rawJSON, &cluster); err != nil { 118 | return models.Cluster{}, err 119 | } 120 | return cluster, nil 121 | } 122 | 123 | // NewestSemVer returns the newest (largest) semver string 124 | func NewestSemVer(v1 string, v2 string) (string, error) { 125 | v1Slice := strings.Split(v1, ".") 126 | v2Slice := strings.Split(v2, ".") 127 | for i, subVer1 := range v1Slice { 128 | if v2Slice[i] > subVer1 { 129 | return v2, nil 130 | } else if subVer1 > v2Slice[i] { 131 | return v1, nil 132 | } 133 | } 134 | return v1, nil 135 | } 136 | 137 | // GetDoctorInfo collects doctor info and return DoctorInfo struct 138 | func GetDoctorInfo( 139 | c InstalledData, // workflow cluster data 140 | k k8s.RunningK8sData, // k8s data 141 | i ClusterID, 142 | v AvailableComponentVersion, 143 | ) (models.DoctorInfo, error) { 144 | cluster, err := GetCluster(c, i, v) 145 | if err != nil { 146 | return models.DoctorInfo{}, err 147 | } 148 | nodes := getK8sNodes(k) 149 | namespaces := []*models.Namespace{getK8sDeisNamespace(k)} 150 | doctor := models.DoctorInfo{ 151 | Workflow: &cluster, 152 | Nodes: nodes, 153 | Namespaces: namespaces, 154 | } 155 | return doctor, nil 156 | } 157 | 158 | // getK8sDeisNamespace is a helper function that returns data 159 | // from the "deis" K8s namespace for RESTful consumption 160 | func getK8sDeisNamespace(k k8s.RunningK8sData) *models.Namespace { 161 | pods, err := k8s.GetPodsModels(k) 162 | if err != nil { 163 | log.Printf("unable to get K8s pods data: %#v", err) 164 | } 165 | services, err := k8s.GetServicesModels(k) 166 | if err != nil { 167 | log.Printf("unable to get K8s services data: %#v", err) 168 | } 169 | replicationControllers, err := k8s.GetReplicationControllersModels(k) 170 | if err != nil { 171 | log.Printf("unable to get K8s RC data: %#v", err) 172 | } 173 | replicaSets, err := k8s.GetReplicaSetsModels(k) 174 | if err != nil { 175 | log.Printf("unable to get K8s replicaSets data: %#v", err) 176 | } 177 | daemonSets, err := k8s.GetDaemonSetsModels(k) 178 | if err != nil { 179 | log.Printf("unable to get K8s daemonSets data: %#v", err) 180 | } 181 | deployments, err := k8s.GetDeploymentsModels(k) 182 | if err != nil { 183 | log.Printf("unable to get K8s deployments data: %#v", err) 184 | } 185 | events, err := k8s.GetEventsModels(k) 186 | if err != nil { 187 | log.Printf("unable to get K8s events data: %#v", err) 188 | } 189 | return &models.Namespace{ 190 | Name: config.Spec.DeisNamespace, 191 | DaemonSets: daemonSets, 192 | Deployments: deployments, 193 | Events: events, 194 | Pods: pods, 195 | ReplicaSets: replicaSets, 196 | ReplicationControllers: replicationControllers, 197 | Services: services, 198 | } 199 | } 200 | 201 | // getK8sNodes is a helper function that returns K8s nodes data for RESTful consumption 202 | func getK8sNodes(k k8s.RunningK8sData) []*models.K8sResource { 203 | nodes, err := k8s.GetNodesModels(k) 204 | if err != nil { 205 | log.Println("unable to get K8s nodes data") 206 | } 207 | return nodes 208 | } 209 | 210 | // newestVersion is a temporary static implementation of a real "return newest version" function 211 | func newestVersion(v1 string, v2 string) string { 212 | return v1 213 | } 214 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: b64e0a5c65cb4f9289e33a902292753e8e66922cf41e3fcd776507f2be156b08 2 | updated: 2016-07-19T18:06:10.791383535Z 3 | imports: 4 | - name: bitbucket.org/ww/goautoneg 5 | version: 75cd24fc2f2c 6 | - name: github.com/arschles/assert 7 | version: fc2da9844984ce5093111298174706e14d4c0c47 8 | - name: github.com/arschles/testsrv 9 | version: 1ca51fb1a3b6e612411883cf1c9dbc58319b1d5b 10 | - name: github.com/asaskevich/govalidator 11 | version: 9699ab6b38bee2e02cd3fe8b99ecf67665395c96 12 | repo: https://github.com/asaskevich/govalidator 13 | - name: github.com/beorn7/perks 14 | version: b965b613227fddccbfffe13eae360ed3fa822f8d 15 | subpackages: 16 | - quantile 17 | - name: github.com/blang/semver 18 | version: 31b736133b98f26d5e078ec9eb591666edfd091f 19 | - name: github.com/davecgh/go-spew 20 | version: 3e6e67c4dcea3ac2f25fd4731abc0e1deaf36216 21 | subpackages: 22 | - spew 23 | - name: github.com/deis/kubeapp 24 | version: d1fd2e1f9db4b8584edc540d523a85af9c51e71e 25 | subpackages: 26 | - api 27 | - api/rc 28 | - api/daemonset 29 | - api/deployment 30 | - api/node 31 | - api/pod 32 | - api/replicaset 33 | - api/service 34 | - api/secret 35 | - name: github.com/docker/docker 36 | version: 0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d 37 | subpackages: 38 | - pkg/jsonmessage 39 | - pkg/mount 40 | - pkg/parsers 41 | - pkg/symlink 42 | - pkg/term 43 | - pkg/timeutils 44 | - pkg/units 45 | - name: github.com/docker/go-units 46 | version: 0bbddae09c5a5419a8c6dcdd7ff90da3d450393b 47 | - name: github.com/emicklei/go-restful 48 | version: 777bb3f19bcafe2575ffb2a3e46af92509ae9594 49 | subpackages: 50 | - swagger 51 | - log 52 | - name: github.com/ghodss/yaml 53 | version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee 54 | - name: github.com/go-swagger/go-swagger 55 | version: ff42df2bf231f6a12383b36332f8364254ea86d6 56 | subpackages: 57 | - httpkit/client 58 | - client 59 | - strfmt 60 | - errors 61 | - httpkit 62 | - httpkit/validate 63 | - swag 64 | - name: github.com/golang/glog 65 | version: 44145f04b68cf362d9c4df2182967c2275eaefed 66 | - name: github.com/golang/protobuf 67 | version: b982704f8bb716bb608144408cff30e15fbde841 68 | subpackages: 69 | - proto 70 | - name: github.com/google/cadvisor 71 | version: 546a3771589bdb356777c646c6eca24914fdd48b 72 | subpackages: 73 | - api 74 | - cache/memory 75 | - collector 76 | - container 77 | - events 78 | - fs 79 | - healthz 80 | - http 81 | - info/v1 82 | - info/v2 83 | - manager 84 | - metrics 85 | - pages 86 | - storage 87 | - summary 88 | - utils 89 | - validate 90 | - version 91 | - name: github.com/google/gofuzz 92 | version: bbcb9da2d746f8bdbd6a936686a0a6067ada0ec5 93 | - name: github.com/gorilla/context 94 | version: aed02d124ae4a0e94fea4541c8effd05bf0c8296 95 | - name: github.com/gorilla/mux 96 | version: d391bea3118c9fc17a88d62c9189bb791255e0ef 97 | - name: github.com/juju/ratelimit 98 | version: 77ed1c8a01217656d2080ad51981f6e99adaa177 99 | - name: github.com/kelseyhightower/envconfig 100 | version: 91921eb4cf999321cdbeebdba5a03555800d493b 101 | - name: github.com/matttproud/golang_protobuf_extensions 102 | version: fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a 103 | subpackages: 104 | - pbutil 105 | - name: github.com/opencontainers/runc 106 | version: 7ca2aa4873aea7cb4265b1726acb24b90d8726c6 107 | subpackages: 108 | - libcontainer 109 | - libcontainer/cgroups/fs 110 | - libcontainer/configs 111 | - libcontainer/cgroups 112 | - libcontainer/system 113 | - name: github.com/pborman/uuid 114 | version: ca53cad383cad2479bbba7f7a1a05797ec1386e4 115 | - name: github.com/prometheus/client_golang 116 | version: 3b78d7a77f51ccbc364d4bc170920153022cfd08 117 | subpackages: 118 | - prometheus 119 | - name: github.com/prometheus/client_model 120 | version: fa8ad6fec33561be4280a8f0514318c79d7f6cb6 121 | subpackages: 122 | - go 123 | - name: github.com/prometheus/common 124 | version: ef7a9a5fb138aa5d3a19988537606226869a0390 125 | subpackages: 126 | - expfmt 127 | - model 128 | - name: github.com/prometheus/procfs 129 | version: 490cc6eb5fa45bf8a8b7b73c8bc82a8160e8531d 130 | - name: github.com/satori/go.uuid 131 | version: 0aa62d5ddceb50dbcb909d790b5345affd3669b6 132 | - name: github.com/spf13/pflag 133 | version: 08b1a584251b5b62f458943640fc8ebd4d50aaa5 134 | - name: github.com/ugorji/go 135 | version: f4485b318aadd133842532f841dc205a8e339d74 136 | subpackages: 137 | - codec 138 | - name: golang.org/x/net 139 | version: c2528b2dd8352441850638a8bb678c2ad056fd3e 140 | subpackages: 141 | - context 142 | - html 143 | - http2 144 | - internal/timeseries 145 | - trace 146 | - websocket 147 | - context/ctxhttp 148 | - name: gopkg.in/yaml.v2 149 | version: d466437aa4adc35830964cffc5b5f262c63ddcb4 150 | - name: k8s.io/kubernetes 151 | version: e7503fde8ec6b3911dc7e22cae2619dc5bcec351 152 | subpackages: 153 | - client/unversioned 154 | - labels 155 | - api 156 | - pkg 157 | - pkg/client/unversioned 158 | - pkg/api 159 | - pkg/api/errors 160 | - pkg/apis/extensions 161 | - pkg/labels 162 | - pkg/api/install 163 | - pkg/api/meta 164 | - pkg/api/unversioned 165 | - pkg/apimachinery/registered 166 | - pkg/apis/authorization/install 167 | - pkg/apis/autoscaling 168 | - pkg/apis/autoscaling/install 169 | - pkg/apis/batch 170 | - pkg/apis/batch/install 171 | - pkg/apis/componentconfig/install 172 | - pkg/apis/extensions/install 173 | - pkg/apis/metrics/install 174 | - pkg/client/restclient 175 | - pkg/client/typed/discovery 176 | - pkg/fields 177 | - pkg/runtime 178 | - pkg/util/net 179 | - pkg/util/sets 180 | - pkg/util/wait 181 | - pkg/version 182 | - pkg/watch 183 | - pkg/api/resource 184 | - pkg/auth/user 185 | - pkg/conversion 186 | - pkg/runtime/serializer 187 | - pkg/types 188 | - pkg/util 189 | - pkg/util/intstr 190 | - pkg/util/rand 191 | - pkg/util/validation/field 192 | - pkg/util/validation 193 | - pkg/api/v1 194 | - pkg/apimachinery 195 | - pkg/util/errors 196 | - pkg/apis/authorization 197 | - pkg/apis/authorization/v1beta1 198 | - pkg/apis/autoscaling/v1 199 | - pkg/apis/batch/v1 200 | - pkg/apis/componentconfig 201 | - pkg/apis/componentconfig/v1alpha1 202 | - pkg/apis/extensions/v1beta1 203 | - pkg/apis/metrics 204 | - pkg/apis/metrics/v1alpha1 205 | - pkg/api/validation 206 | - pkg/client/metrics 207 | - pkg/client/transport 208 | - pkg/watch/json 209 | - pkg/conversion/queryparams 210 | - pkg/util/runtime 211 | - third_party/forked/reflect 212 | - pkg/runtime/serializer/json 213 | - pkg/runtime/serializer/recognizer 214 | - pkg/runtime/serializer/versioning 215 | - pkg/util/integer 216 | - pkg/util/parsers 217 | - pkg/kubelet/qos 218 | - pkg/master/ports 219 | - pkg/api/endpoints 220 | - pkg/api/pod 221 | - pkg/api/service 222 | - pkg/api/util 223 | - pkg/capabilities 224 | - pkg/util/yaml 225 | - pkg/util/hash 226 | - pkg/util/net/sets 227 | - name: speter.net/go/exp/math/dec/inf 228 | version: 42ca6cd68aa922bc3f32f1e056e61b65945d9ad7 229 | repo: https://github.com/belua/inf 230 | vcs: git 231 | devImports: [] 232 | -------------------------------------------------------------------------------- /data/data_test.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/arschles/assert" 8 | "github.com/deis/workflow-manager/mocks" 9 | "github.com/deis/workflow-manager/pkg/swagger/models" 10 | ) 11 | 12 | const mockClusterID = "f91378a6-a815-4c20-9b0d-77b205cd3ba4" 13 | const mockComponentName = "component" 14 | const mockComponentDescription = "mock component" 15 | const mockComponentVersion = "v2-beta" 16 | 17 | // Creating a novel mock struct that fulfills the ClusterID interface 18 | type testClusterID struct { 19 | cache string 20 | } 21 | 22 | func (c testClusterID) Get() (string, error) { 23 | return mockClusterID, nil 24 | } 25 | 26 | func (c testClusterID) Cached() string { 27 | return c.cache 28 | } 29 | 30 | func (c *testClusterID) StoreInCache(cid string) { 31 | c.cache = cid 32 | } 33 | 34 | // Creating a novel mock struct that fulfills the InstalledData interface 35 | type mockInstalledComponents struct { 36 | } 37 | 38 | func (g mockInstalledComponents) Get() ([]byte, error) { 39 | return []byte(fmt.Sprintf(`{ 40 | "components": [ 41 | { 42 | "component": { 43 | "name": "%s", 44 | "description": "%s" 45 | }, 46 | "version": { 47 | "version": "%s" 48 | } 49 | } 50 | ] 51 | }`, mockComponentName, mockComponentDescription, mockComponentVersion)), nil 52 | } 53 | 54 | // Creating a novel mock struct that fulfills the InstalledComponentVersion interface 55 | type mockInstalledComponent struct{} 56 | 57 | // Get method for InstalledComponent 58 | func (c mockInstalledComponent) Get(component string) ([]byte, error) { 59 | if component == mockComponentName { 60 | return []byte(fmt.Sprintf(`{ 61 | "component": { 62 | "name": "%s", 63 | "description": "%s" 64 | }, 65 | "version": { 66 | "version": "%s" 67 | } 68 | }`, mockComponentName, mockComponentDescription, mockComponentVersion)), nil 69 | } 70 | return []byte{}, fmt.Errorf("mock getter only accepts %s arg", mockComponentName) 71 | } 72 | 73 | // Calls GetID twice, the first time we expect our passed-in struct w/ Get() method 74 | // to be invoked, the 2nd time we expect to receive the same value back (cached in memory) 75 | // and for the passed-in Get() method to be ignored 76 | func TestGetID(t *testing.T) { 77 | cid := &testClusterID{} 78 | id, err := GetID(cid) 79 | assert.NoErr(t, err) 80 | assert.Equal(t, id, mockClusterID, "cluster ID value") 81 | cid.cache = "something else" 82 | id, err = GetID(cid) 83 | assert.NoErr(t, err) 84 | assert.Equal(t, id, "something else", "cluster ID value") 85 | } 86 | 87 | func TestGetCluster(t *testing.T) { 88 | mockCluster := getMockCluster(t) 89 | cluster, err := GetCluster( 90 | mocks.InstalledMockData{}, 91 | &mocks.ClusterIDMockData{}, 92 | mocks.LatestMockData{}, 93 | ) 94 | assert.NoErr(t, err) 95 | assert.Equal(t, cluster, mockCluster, "clusters") 96 | } 97 | 98 | func TestGetDoctorInfo(t *testing.T) { 99 | mockCluster := getMockCluster(t) 100 | doctorInfo, err := GetDoctorInfo( 101 | mocks.InstalledMockData{}, 102 | mocks.RunningK8sMockData{}, // TODO: add k8s mock data 103 | &mocks.ClusterIDMockData{}, 104 | mocks.LatestMockData{}, 105 | ) 106 | assert.NoErr(t, err) 107 | assert.Equal(t, *doctorInfo.Workflow, mockCluster, "clusters") 108 | } 109 | 110 | func TestAddUpdateData(t *testing.T) { 111 | mockCluster := getMockCluster(t) 112 | // AddUpdateData should add an "UpdateAvailable" field to any components whose versions are out-of-date 113 | err := AddUpdateData(&mockCluster, mocks.LatestMockData{}) 114 | assert.NoErr(t, err) 115 | //TODO: when newestVersion is implemented, actually test for the addition of "UpdateAvailable" fields. 116 | // tracked in https://github.com/deis/workflow-manager/issues/52 117 | } 118 | 119 | func TestGetInstalled(t *testing.T) { 120 | cluster, err := GetInstalled(mockInstalledComponents{}) 121 | assert.NoErr(t, err) 122 | assert.Equal(t, cluster.Components[0].Component.Name, mockComponentName, "Name value") 123 | assert.Equal(t, *cluster.Components[0].Component.Description, mockComponentDescription, "Description value") 124 | assert.Equal(t, cluster.Components[0].Version.Version, mockComponentVersion, "Version value") 125 | } 126 | 127 | func TestParseJSONCluster(t *testing.T) { 128 | const name = "component" 129 | const description = "test component" 130 | const version = "1.0.0" 131 | raw := []byte(fmt.Sprintf(`{ 132 | "id": "%s", 133 | "components": [ 134 | { 135 | "component": { 136 | "name": "%s", 137 | "description": "%s" 138 | }, 139 | "version": { 140 | "version": "%s" 141 | } 142 | } 143 | ] 144 | }`, mockClusterID, name, description, version)) 145 | cluster, err := ParseJSONCluster(raw) 146 | assert.NoErr(t, err) 147 | 148 | assert.Equal(t, cluster.ID, mockClusterID, "ID value") 149 | assert.Equal(t, cluster.Components[0].Component.Name, name, "Name value") 150 | assert.Equal(t, *cluster.Components[0].Component.Description, description, "Description value") 151 | assert.Equal(t, cluster.Components[0].Version.Version, version, "Version value") 152 | } 153 | 154 | func TestNewestSemVer(t *testing.T) { 155 | // Verify that NewestSemVer returns correct semver string for larger major, minor, and patch substrings 156 | const v1Lower = "2.0.0" 157 | v2s := [3]string{"3.0.0", "2.1.0", "2.0.1"} 158 | for _, v2 := range v2s { 159 | newest, err := NewestSemVer(v1Lower, v2) 160 | assert.NoErr(t, err) 161 | if newest != v2 { 162 | fmt.Printf("expected %s to be greater than %s\n", v2, v1Lower) 163 | t.Fatal("semver comparison failure") 164 | } 165 | } 166 | // Verify that NewestSemVer returns correct semver string for smaller major, minor, and patch substrings 167 | const v1Higher = "2.4.5" 168 | v2s = [3]string{"1.99.23", "2.3.99", "2.4.4"} 169 | for _, v2 := range v2s { 170 | newest, err := NewestSemVer(v1Higher, v2) 171 | assert.NoErr(t, err) 172 | if newest != v1Higher { 173 | fmt.Printf("expected %s to be greater than %s\n", v1Higher, v2) 174 | t.Fatal("semver comparison failure") 175 | } 176 | } 177 | // Verify that NewestSemVer returns correct semver string for comparing equal strings 178 | const v1Equal = "1.0.0" 179 | v2 := v1Equal 180 | newest, err := NewestSemVer(v1Equal, v2) 181 | assert.NoErr(t, err) 182 | if newest != v1Equal && newest != v2 { 183 | fmt.Printf("expected %s to be equal to %s and %s\n", newest, v1Equal, v2) 184 | t.Fatal("semver comparison failure") 185 | } 186 | } 187 | 188 | func getMockComponentVersions() []byte { 189 | return []byte(fmt.Sprintf(`[{ 190 | "components": [ 191 | { 192 | "component": { 193 | "name": "%s", 194 | "description": "%s" 195 | }, 196 | "version": { 197 | "version": "%s" 198 | } 199 | } 200 | ] 201 | }]`, mockComponentName, mockComponentDescription, mockComponentVersion)) 202 | } 203 | 204 | func getMockCluster(t *testing.T) models.Cluster { 205 | mockData, err := mocks.GetMockCluster() 206 | assert.NoErr(t, err) 207 | mockCluster, err := ParseJSONCluster(mockData) 208 | assert.NoErr(t, err) 209 | return mockCluster 210 | } 211 | 212 | func getMockLatest(name string, t *testing.T) models.Version { 213 | version, err := mocks.GetMockLatest(name) 214 | assert.NoErr(t, err) 215 | return version 216 | } 217 | -------------------------------------------------------------------------------- /handlers/handlers_test.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "testing" 10 | 11 | "github.com/arschles/assert" 12 | "github.com/deis/workflow-manager/config" 13 | "github.com/deis/workflow-manager/data" 14 | "github.com/deis/workflow-manager/pkg/swagger/models" 15 | "github.com/gorilla/mux" 16 | "github.com/satori/go.uuid" 17 | ) 18 | 19 | const mockInstalledComponentName = "component" 20 | const mockInstalledComponentDescription = "mock component" 21 | const mockInstalledComponentVersion = "1.2.3" 22 | 23 | // Creating a novel mock struct that fulfills the data.InstalledData interface 24 | type mockInstalledComponents struct{} 25 | 26 | func (g mockInstalledComponents) Get() ([]byte, error) { 27 | return []byte(fmt.Sprintf(`{ 28 | "components": [ 29 | { 30 | "component": { 31 | "name": "%s", 32 | "description": "%s" 33 | }, 34 | "version": { 35 | "version": "%s" 36 | } 37 | } 38 | ] 39 | }`, mockInstalledComponentName, mockInstalledComponentDescription, mockInstalledComponentVersion)), nil 40 | } 41 | 42 | // Creating a novel mock struct that fulfills the data.RunningK8sData interface 43 | type mockRunningK8sData struct{} 44 | 45 | func (g mockRunningK8sData) DaemonSets() ([]*models.K8sResource, error) { 46 | // TODO: implement 47 | return []*models.K8sResource{}, nil 48 | } 49 | 50 | func (g mockRunningK8sData) Deployments() ([]*models.K8sResource, error) { 51 | // TODO: implement 52 | return []*models.K8sResource{}, nil 53 | } 54 | 55 | func (g mockRunningK8sData) Events() ([]*models.K8sResource, error) { 56 | // TODO: implement 57 | return []*models.K8sResource{}, nil 58 | } 59 | 60 | func (g mockRunningK8sData) Nodes() ([]*models.K8sResource, error) { 61 | // TODO: implement 62 | return []*models.K8sResource{}, nil 63 | } 64 | 65 | func (g mockRunningK8sData) Pods() ([]*models.K8sResource, error) { 66 | // TODO: implement 67 | return []*models.K8sResource{}, nil 68 | } 69 | 70 | func (g mockRunningK8sData) ReplicaSets() ([]*models.K8sResource, error) { 71 | // TODO: implement 72 | return []*models.K8sResource{}, nil 73 | } 74 | 75 | func (g mockRunningK8sData) ReplicationControllers() ([]*models.K8sResource, error) { 76 | // TODO: implement 77 | return []*models.K8sResource{}, nil 78 | } 79 | 80 | func (g mockRunningK8sData) Services() ([]*models.K8sResource, error) { 81 | // TODO: implement 82 | return []*models.K8sResource{}, nil 83 | } 84 | 85 | const mockID = "faa31f63-d8dc-42e3-9568-405d20a3f755" 86 | 87 | // Creating a novel mock struct that fulfills the data.ClusterID interface 88 | type mockClusterID struct { 89 | cached string 90 | } 91 | 92 | func (c mockClusterID) Get() (string, error) { 93 | return mockID, nil 94 | } 95 | 96 | func (c mockClusterID) Cached() string { 97 | return c.cached 98 | } 99 | 100 | func (c *mockClusterID) StoreInCache(cid string) { 101 | c.cached = cid 102 | } 103 | 104 | const mockAvailableComponentName = "component" 105 | const mockAvailableComponentDescription = "mock component" 106 | const mockAvailableComponentVersion = "3.2.1" 107 | 108 | // Creating a novel mock struct that fulfills the data.AvailableComponentVersion interface 109 | type mockAvailableVersion struct{} 110 | 111 | func (c mockAvailableVersion) Get(component string, cluster models.Cluster) (models.Version, error) { 112 | if component == "component" { 113 | return models.Version{Version: "v2-beta"}, nil 114 | } 115 | return models.Version{}, fmt.Errorf("mock getter only accepts 'component' arg") 116 | } 117 | 118 | type genericJSON struct { 119 | Foo string `json:"foo"` 120 | } 121 | 122 | func TestComponentsHandler(t *testing.T) { 123 | componentsHandler := ComponentsHandler( 124 | mockInstalledComponents{}, 125 | &mockClusterID{}, 126 | mockAvailableVersion{}, 127 | ) 128 | resp, err := getTestHandlerResponse(componentsHandler) 129 | assert.NoErr(t, err) 130 | assert200(t, resp) 131 | respData, err := ioutil.ReadAll(resp.Body) 132 | assert.NoErr(t, err) 133 | cluster, err := data.ParseJSONCluster(respData) 134 | assert.NoErr(t, err) 135 | assert.Equal(t, cluster.ID, mockID, "ID value") 136 | assert.Equal(t, cluster.Components[0].Component.Name, mockInstalledComponentName, "Name value") 137 | assert.Equal(t, *cluster.Components[0].Component.Description, mockInstalledComponentDescription, "Description value") 138 | assert.Equal(t, cluster.Components[0].Version.Version, mockInstalledComponentVersion, "Version value") 139 | //TODO 140 | //assert.Equal(t, cluster.Components[0].UpdateAvailable, mockAvailableComponentVersion, "available Version value") 141 | } 142 | 143 | func TestDoctorHandler(t *testing.T) { 144 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 145 | w.Header().Set("Content-Type", "application/json; charset=UTF-8") 146 | if err := json.NewEncoder(w).Encode([]byte("")); err != nil { 147 | http.Error(w, "error encoding JSON", http.StatusInternalServerError) 148 | return 149 | } 150 | })) 151 | defer ts.Close() 152 | apiClient, err := config.GetSwaggerClient(ts.URL) 153 | doctorHandler := DoctorHandler( 154 | mockInstalledComponents{}, 155 | mockRunningK8sData{}, // TODO: mock k8s node data 156 | &mockClusterID{}, 157 | mockAvailableVersion{}, 158 | apiClient, 159 | ) 160 | resp, err := getTestHandlerResponse(doctorHandler) 161 | assert.NoErr(t, err) 162 | assert200(t, resp) 163 | respData, err := ioutil.ReadAll(resp.Body) 164 | assert.NoErr(t, err) 165 | // verify that the return data is a uuid string 166 | doctorID1, err := uuid.FromString(string(respData[:])) 167 | assert.NoErr(t, err) 168 | // invoke the handler a 2nd time to ensure that unique IDs are created for 169 | // each request 170 | resp2, err := getTestHandlerResponse(doctorHandler) 171 | assert.NoErr(t, err) 172 | assert200(t, resp2) 173 | respData2, err := ioutil.ReadAll(resp2.Body) 174 | assert.NoErr(t, err) 175 | doctorID2, err := uuid.FromString(string(respData2[:])) 176 | assert.NoErr(t, err) 177 | if doctorID1 == doctorID2 { 178 | t.Error("DoctorHandler should return a unique ID for every invocation") 179 | } 180 | } 181 | 182 | func TestIDHandler(t *testing.T) { 183 | idHandler := IDHandler(&mockClusterID{}) 184 | resp, err := getTestHandlerResponse(idHandler) 185 | assert.NoErr(t, err) 186 | assert200(t, resp) 187 | respData, err := ioutil.ReadAll(resp.Body) 188 | assert.NoErr(t, err) 189 | assert.Equal(t, string(respData), mockID, "ID value") 190 | } 191 | 192 | func TestWritePlainText(t *testing.T) { 193 | const text = "foo" 194 | handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 195 | writePlainText(text, w) 196 | }) 197 | resp, err := getTestHandlerResponse(handler) 198 | assert.NoErr(t, err) 199 | assert.Equal(t, resp.Header.Get("Content-Type"), "text/plain", "Content-Type value") 200 | respData, err := ioutil.ReadAll(resp.Body) 201 | assert.NoErr(t, err) 202 | assert.Equal(t, string(respData), text, "text response") 203 | } 204 | 205 | func getTestHandlerResponse(handler http.Handler) (*http.Response, error) { 206 | r := mux.NewRouter() 207 | r.Handle("/", handler) 208 | server := httptest.NewServer(r) 209 | defer server.Close() 210 | resp, err := http.Get(server.URL) 211 | if err != nil { 212 | return nil, err 213 | } 214 | return resp, nil 215 | } 216 | 217 | func assert200(t *testing.T, resp *http.Response) { 218 | if resp.StatusCode != 200 { 219 | t.Fatalf("Received non-200 response: %d\n", resp.StatusCode) 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /k8s/k8s_test.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/arschles/assert" 7 | "k8s.io/kubernetes/pkg/api" 8 | "k8s.io/kubernetes/pkg/api/testapi" 9 | "k8s.io/kubernetes/pkg/api/unversioned" 10 | "k8s.io/kubernetes/pkg/apis/extensions" 11 | "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" 12 | ) 13 | 14 | const namespace = "deis" 15 | 16 | func TestRunningK8sDataDaemonsets(t *testing.T) { 17 | c := getK8sClientForDaemonSets(t) 18 | deisK8sResources := NewResourceInterfaceNamespaced(c, namespace) 19 | runningK8sData := NewRunningK8sData(deisK8sResources) 20 | daemonsets, err := runningK8sData.DaemonSets() 21 | assert.NoErr(t, err) 22 | assert.True(t, len(daemonsets) == 2, "daemonsets response slice was not the expected length") 23 | assert.True(t, daemonsets[0].Data != daemonsets[1].Data, "daemonsets should not be identical") 24 | } 25 | 26 | func TestRunningK8sDataDeployments(t *testing.T) { 27 | c := getK8sClientForDeployments(t) 28 | deisK8sResources := NewResourceInterfaceNamespaced(c, namespace) 29 | runningK8sData := NewRunningK8sData(deisK8sResources) 30 | deployments, err := runningK8sData.Deployments() 31 | assert.NoErr(t, err) 32 | assert.True(t, len(deployments) == 2, "deployments response slice was not the expected length") 33 | assert.True(t, deployments[0].Data != deployments[1].Data, "deployments should not be identical") 34 | } 35 | 36 | func TestRunningK8sDataEvents(t *testing.T) { 37 | c := getK8sClientForEvents(t) 38 | deisK8sResources := NewResourceInterfaceNamespaced(c, namespace) 39 | runningK8sData := NewRunningK8sData(deisK8sResources) 40 | events, err := runningK8sData.Events() 41 | assert.NoErr(t, err) 42 | assert.True(t, len(events) == 2, "events response slice was not the expected length") 43 | assert.True(t, events[0].Data != events[1].Data, "events should not be identical") 44 | } 45 | 46 | func TestRunningK8sDataNodes(t *testing.T) { 47 | c := getK8sClientForNodes(t) 48 | deisK8sResources := NewResourceInterfaceNamespaced(c, namespace) 49 | runningK8sData := NewRunningK8sData(deisK8sResources) 50 | nodes, err := runningK8sData.Nodes() 51 | assert.NoErr(t, err) 52 | assert.True(t, len(nodes) == 3, "nodes response slice was not the expected length") 53 | assert.True(t, nodes[0].Data != nodes[1].Data, "nodes should not be identical") 54 | } 55 | 56 | func TestRunningK8sDataPods(t *testing.T) { 57 | c := getK8sClientForPods(t) 58 | deisK8sResources := NewResourceInterfaceNamespaced(c, namespace) 59 | runningK8sData := NewRunningK8sData(deisK8sResources) 60 | pods, err := runningK8sData.Pods() 61 | assert.NoErr(t, err) 62 | assert.True(t, len(pods) == 2, "pods response slice was not the expected length") 63 | assert.True(t, pods[0].Data != pods[1].Data, "pods should not be identical") 64 | } 65 | 66 | func TestRunningK8sDataReplicaSets(t *testing.T) { 67 | c := getK8sClientForReplicaSets(t) 68 | deisK8sResources := NewResourceInterfaceNamespaced(c, namespace) 69 | runningK8sData := NewRunningK8sData(deisK8sResources) 70 | replicaSets, err := runningK8sData.ReplicaSets() 71 | assert.NoErr(t, err) 72 | assert.True(t, len(replicaSets) == 2, "replica sets response slice was not the expected length") 73 | assert.True(t, replicaSets[0].Data != replicaSets[1].Data, "replica sets should not be identical") 74 | } 75 | 76 | func TestRunningK8sDataReplicationControllers(t *testing.T) { 77 | c := getK8sClientForReplicationControllers(t) 78 | deisK8sResources := NewResourceInterfaceNamespaced(c, namespace) 79 | runningK8sData := NewRunningK8sData(deisK8sResources) 80 | rcs, err := runningK8sData.ReplicationControllers() 81 | assert.NoErr(t, err) 82 | assert.True(t, len(rcs) == 2, "rc response slice was not the expected length") 83 | assert.True(t, rcs[0].Data != rcs[1].Data, "rcs should not be identical") 84 | } 85 | 86 | func TestRunningK8sDataServices(t *testing.T) { 87 | c := getK8sClientForServices(t) 88 | //_, _ = c.Setup(t).Services(namespace).List(api.ListOptions{}) 89 | deisK8sResources := NewResourceInterfaceNamespaced(c, namespace) 90 | runningK8sData := NewRunningK8sData(deisK8sResources) 91 | services, err := runningK8sData.Services() 92 | assert.NoErr(t, err) 93 | assert.True(t, len(services) == 2, "services response slice was not the expected length") 94 | assert.True(t, services[0].Data != services[1].Data, "services should not be identical") 95 | } 96 | 97 | func getK8sClientForDaemonSets(t *testing.T) *simple.Client { 98 | c := &simple.Client{ 99 | Request: simple.Request{ 100 | Method: "GET", 101 | Path: testapi.Extensions.ResourcePath("daemonsets", namespace, ""), 102 | }, 103 | Response: simple.Response{StatusCode: 200, 104 | Body: &extensions.DaemonSetList{ 105 | Items: []extensions.DaemonSet{ 106 | { 107 | ObjectMeta: api.ObjectMeta{ 108 | Name: "deis-logger-fluentd", 109 | }, 110 | }, 111 | { 112 | ObjectMeta: api.ObjectMeta{ 113 | Name: "deis-monitor-telegraf", 114 | }, 115 | }, 116 | }, 117 | }, 118 | }, 119 | } 120 | return c.Setup(t) 121 | } 122 | 123 | func getK8sClientForDeployments(t *testing.T) *simple.Client { 124 | c := &simple.Client{ 125 | Request: simple.Request{ 126 | Method: "GET", 127 | Path: testapi.Extensions.ResourcePath("deployments", namespace, ""), 128 | }, 129 | Response: simple.Response{StatusCode: 200, 130 | Body: &extensions.DeploymentList{ 131 | Items: []extensions.Deployment{ 132 | { 133 | ObjectMeta: api.ObjectMeta{ 134 | Name: "deis-builder", 135 | }, 136 | }, 137 | { 138 | ObjectMeta: api.ObjectMeta{ 139 | Name: "deis-router", 140 | }, 141 | }, 142 | }, 143 | }, 144 | }, 145 | } 146 | return c.Setup(t) 147 | } 148 | 149 | func getK8sClientForEvents(t *testing.T) *simple.Client { 150 | obj1Reference := &api.ObjectReference{ 151 | Kind: "Pod", 152 | Namespace: namespace, 153 | Name: "deis-builder-900960817-h9zmm", 154 | UID: "uid", 155 | APIVersion: "v1", 156 | ResourceVersion: "1477748", 157 | } 158 | obj2Reference := &api.ObjectReference{ 159 | Kind: "Pod", 160 | Namespace: namespace, 161 | Name: "deis-controller-139932026-oltfd", 162 | UID: "uid", 163 | APIVersion: "v1", 164 | ResourceVersion: "1477762", 165 | } 166 | timeStamp := unversioned.Now() 167 | timeStamp2 := unversioned.Now() 168 | eventList := &api.EventList{ 169 | Items: []api.Event{ 170 | { 171 | InvolvedObject: *obj1Reference, 172 | FirstTimestamp: timeStamp, 173 | LastTimestamp: timeStamp, 174 | Count: 1, 175 | Type: api.EventTypeNormal, 176 | }, 177 | { 178 | InvolvedObject: *obj2Reference, 179 | FirstTimestamp: timeStamp2, 180 | LastTimestamp: timeStamp2, 181 | Count: 1, 182 | Type: api.EventTypeNormal, 183 | }, 184 | }, 185 | } 186 | c := &simple.Client{ 187 | Request: simple.Request{ 188 | Method: "GET", 189 | Path: testapi.Default.ResourcePath("events", namespace, ""), 190 | Body: nil, 191 | }, 192 | Response: simple.Response{StatusCode: 200, Body: eventList}, 193 | } 194 | return c.Setup(t) 195 | } 196 | 197 | func getK8sClientForNodes(t *testing.T) *simple.Client { 198 | c := &simple.Client{ 199 | Request: simple.Request{ 200 | Method: "GET", 201 | Path: testapi.Default.ResourcePath("nodes", "", ""), 202 | }, 203 | Response: simple.Response{ 204 | StatusCode: 200, Body: &api.NodeList{ 205 | Items: []api.Node{ 206 | { 207 | ObjectMeta: api.ObjectMeta{ 208 | Labels: map[string]string{ 209 | "kubernetes.io/hostname": "gke-test-default-pool-0e781ee9-1xz6", 210 | }, 211 | Name: "gke-test-default-pool-0e781ee9-1xz6", 212 | }, 213 | }, 214 | { 215 | ObjectMeta: api.ObjectMeta{ 216 | Labels: map[string]string{ 217 | "kubernetes.io/hostname": "gke-test-default-pool-0e781ee9-9j80", 218 | }, 219 | Name: "gke-test-default-pool-0e781ee9-9j80", 220 | }, 221 | }, 222 | { 223 | ObjectMeta: api.ObjectMeta{ 224 | Labels: map[string]string{ 225 | "kubernetes.io/hostname": "gke-francis-default-pool-0e781ee9-z02l", 226 | }, 227 | Name: "gke-francis-default-pool-0e781ee9-z02l", 228 | }, 229 | }, 230 | }, 231 | }, 232 | }, 233 | } 234 | return c.Setup(t) 235 | } 236 | 237 | func getK8sClientForPods(t *testing.T) *simple.Client { 238 | c := &simple.Client{ 239 | Request: simple.Request{ 240 | Method: "GET", 241 | Path: testapi.Default.ResourcePath("pods", namespace, ""), 242 | }, 243 | Response: simple.Response{StatusCode: 200, 244 | Body: &api.PodList{ 245 | Items: []api.Pod{ 246 | { 247 | ObjectMeta: api.ObjectMeta{ 248 | Name: "deis-builder-900960817-h9zmm", 249 | }, 250 | }, 251 | { 252 | ObjectMeta: api.ObjectMeta{ 253 | Name: "deis-controller-139932026-oltfd", 254 | }, 255 | }, 256 | }, 257 | }, 258 | }, 259 | } 260 | return c.Setup(t) 261 | } 262 | 263 | func getK8sClientForReplicaSets(t *testing.T) *simple.Client { 264 | c := &simple.Client{ 265 | Request: simple.Request{ 266 | Method: "GET", 267 | Path: testapi.Extensions.ResourcePath("replicasets", namespace, ""), 268 | }, 269 | Response: simple.Response{StatusCode: 200, 270 | Body: &extensions.ReplicaSetList{ 271 | Items: []extensions.ReplicaSet{ 272 | { 273 | ObjectMeta: api.ObjectMeta{ 274 | Name: "deis-builder-900960817", 275 | }, 276 | }, 277 | { 278 | ObjectMeta: api.ObjectMeta{ 279 | Name: "deis-controller-139932026", 280 | }, 281 | }, 282 | }, 283 | }, 284 | }, 285 | } 286 | return c.Setup(t) 287 | } 288 | 289 | func getK8sClientForReplicationControllers(t *testing.T) *simple.Client { 290 | c := &simple.Client{ 291 | Request: simple.Request{ 292 | Method: "GET", 293 | Path: testapi.Default.ResourcePath("replicationcontrollers", namespace, ""), 294 | }, 295 | Response: simple.Response{StatusCode: 200, 296 | Body: &api.ReplicationControllerList{ 297 | Items: []api.ReplicationController{ 298 | { 299 | ObjectMeta: api.ObjectMeta{ 300 | Name: "deis-builder", 301 | }, 302 | }, 303 | { 304 | ObjectMeta: api.ObjectMeta{ 305 | Name: "deis-controller", 306 | }, 307 | }, 308 | }, 309 | }, 310 | }, 311 | } 312 | return c.Setup(t) 313 | } 314 | 315 | func getK8sClientForServices(t *testing.T) *simple.Client { 316 | c := &simple.Client{ 317 | Request: simple.Request{ 318 | Method: "GET", 319 | Path: testapi.Default.ResourcePath("services", namespace, ""), 320 | }, 321 | Response: simple.Response{StatusCode: 200, 322 | Body: &api.ServiceList{ 323 | Items: []api.Service{ 324 | { 325 | ObjectMeta: api.ObjectMeta{ 326 | Name: "deis-builder", 327 | }, 328 | }, 329 | { 330 | ObjectMeta: api.ObjectMeta{ 331 | Name: "deis-controller", 332 | }, 333 | }, 334 | }, 335 | }, 336 | }, 337 | } 338 | return c.Setup(t) 339 | } 340 | --------------------------------------------------------------------------------