This commit is contained in:
David Dollar 2019-08-16 15:49:19 -04:00
commit 473a4937f3
No known key found for this signature in database
GPG Key ID: AFAF263FB45B2124
2812 changed files with 845519 additions and 0 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
terraform
terraform.tfvars

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.terraform

51
Dockerfile Normal file
View File

@ -0,0 +1,51 @@
## development #################################################################
FROM golang:1.12 AS development
RUN curl -Ls https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kubectl -o /usr/bin/kubectl && \
chmod +x /usr/bin/kubectl
RUN curl -Ls https://github.com/mattgreen/watchexec/releases/download/1.8.6/watchexec-1.8.6-x86_64-unknown-linux-gnu.tar.gz | \
tar -C /usr/bin --strip-components 1 -xz
ENV DEVELOPMENT=true
WORKDIR /usr/src/convox
COPY go.mod go.sum ./
COPY vendor vendor
RUN go build -mod=vendor --ldflags="-s -w" $(go list -mod=vendor ./vendor/...)
COPY . .
RUN make build
# ## package #####################################################################
# FROM golang:1.12 AS package
# RUN apt-get update && apt-get -y install upx-ucl
# RUN go get -u github.com/gobuffalo/packr/packr
# WORKDIR /usr/src/convox
# COPY --from=development /usr/src/convox .
# RUN make package build compress
# ## production ##################################################################
# FROM ubuntu:18.04
# RUN apt-get -qq update && apt-get -qq -y install curl
# RUN curl -Ls https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kubectl -o /usr/bin/kubectl && \
# chmod +x /usr/bin/kubectl
# ENV DEVELOPMENT=false
# WORKDIR /rack
# COPY --from=package /go/bin/router /usr/bin/

36
Makefile Normal file
View File

@ -0,0 +1,36 @@
.PHONY: all build clean clean-package compress dev-aws package release test
commands = atom router
binaries = $(addprefix $(GOPATH)/bin/, $(commands))
sources = $(shell find . -name '*.go')
version := dev-$(shell date +%Y%m%d%H%M%S)
all: build
build: $(binaries)
clean: clean-package
clean-package:
find . -name '*-packr.go' -delete
compress: $(binaries)
upx-ucl -1 $^
dev-aws: release
echo "release = \"$(version)\"" > terraform/rack/aws/terraform.tfvars
cd terraform/rack/aws && terraform apply
package:
$(GOPATH)/bin/packr
release:
docker build -t convox/convox:$(version) .
docker push convox/convox:$(version)
test:
env PROVIDER=test go test -covermode atomic -coverprofile coverage.txt ./...
$(binaries): $(GOPATH)/bin/%: $(sources)
go install -mod=vendor --ldflags="-s -w" ./cmd/$*

31
cmd/atom/main.go Normal file
View File

@ -0,0 +1,31 @@
package main
import (
"fmt"
"os"
"github.com/convox/convox/pkg/atom"
"k8s.io/client-go/rest"
)
func main() {
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
}
}
func run() error {
cfg, err := rest.InClusterConfig()
if err != nil {
return err
}
ac, err := atom.NewController(cfg)
if err != nil {
return err
}
ac.Run()
return nil
}

17
cmd/cx/main.go Normal file
View File

@ -0,0 +1,17 @@
package main
import (
"os"
"github.com/convox/convox/pkg/cli"
)
var (
version = "dev"
)
func main() {
c := cli.New("cx", version)
os.Exit(c.Execute(os.Args[1:]))
}

50
cmd/router/main.go Normal file
View File

@ -0,0 +1,50 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/convox/convox/pkg/router"
"k8s.io/apimachinery/pkg/util/runtime"
)
func main() {
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
}
}
func run() error {
// hack to make glog stop complaining about flag parsing
fs := flag.NewFlagSet("", flag.ContinueOnError)
_ = fs.Parse([]string{})
flag.CommandLine = fs
runtime.ErrorHandlers = []func(error){}
r, err := router.New()
if err != nil {
return err
}
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
go handleSignals(r, ch)
return r.Serve()
}
func handleSignals(r *router.Router, ch <-chan os.Signal) {
sig := <-ch
fmt.Printf("ns=rack at=signal signal=%v terminate=true\n", sig)
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
r.Shutdown(ctx)
os.Exit(0)
}

43
go.mod Normal file
View File

@ -0,0 +1,43 @@
module github.com/convox/convox
go 1.12
require (
github.com/Microsoft/hcsshim v0.8.7-0.20190801035247-8694eade7dd3 // indirect
github.com/aws/aws-sdk-go v1.21.10
github.com/convox/logger v0.0.0-20180522214415-e39179955b52
github.com/convox/stdcli v0.0.0-20190326115454-b78bee159e98
github.com/docker/docker v1.4.2-0.20190710153559-aa8249ae1b8b
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 // indirect
github.com/dustin/go-humanize v1.0.0
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e // indirect
github.com/fsouza/go-dockerclient v1.4.2
github.com/gobuffalo/packr v1.30.1
github.com/gobwas/glob v0.2.3
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d // indirect
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 // indirect
github.com/google/btree v0.0.0-20160524151835-7d79101e329e // indirect
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d // indirect
github.com/gorilla/websocket v1.4.0
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7 // indirect
github.com/imdario/mergo v0.3.7 // indirect
github.com/json-iterator/go v1.1.7 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/miekg/dns v1.1.15
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.8.1
github.com/stretchr/testify v1.3.0
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc // indirect
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
gopkg.in/inf.v0 v0.9.0 // indirect
gopkg.in/yaml.v2 v2.2.2
k8s.io/api v0.0.0-20190118113203-912cbe2bfef3
k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841
k8s.io/client-go v0.0.0-20190117233410-4022682532b3
k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058 // indirect
k8s.io/metrics v0.0.0-20181204004050-3d8dd1c3a53c
)

306
go.sum Normal file
View File

@ -0,0 +1,306 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.13-0.20190625174015-d2ef9cfdac5d/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.13 h1:Hmi80lzZuI/CaYmlJp/b+FjZdRZhKu9c2mDVqKlLWVs=
github.com/Microsoft/go-winio v0.4.13/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/Microsoft/hcsshim v0.8.7-0.20190801035247-8694eade7dd3 h1:agmzOP0aQ42FpfjsjcI4cMDbT1GFCHFVYlv7zprTBh4=
github.com/Microsoft/hcsshim v0.8.7-0.20190801035247-8694eade7dd3/go.mod h1:8OYooOlLyAIH/z8IDfdQH0L58ksBjNzsisBpmW2vg0Y=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.21.10 h1:lTRdgyxraKbnNhx7kWeoW/Uow1TKnSNDpQGTtEXJQgk=
github.com/aws/aws-sdk-go v1.21.10/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/containerd v0.0.0-20190214164719-faec567304bb/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/ttrpc v0.0.0-20180920185216-2a805f718635/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/convox/logger v0.0.0-20180522214415-e39179955b52 h1:HTaloo6by+4NE1A1mRYprWzsOy1PGH2tPGfgZ60dyHU=
github.com/convox/logger v0.0.0-20180522214415-e39179955b52/go.mod h1:IcbSD+Pq+bQV2z/otiMCHLAYBrDR/jPFopFatrWjlMM=
github.com/convox/stdcli v0.0.0-20190326115454-b78bee159e98 h1:jSUPA5xGpERhDUevVwj8YPXhIbKq+YJPXpF8U9M4p1o=
github.com/convox/stdcli v0.0.0-20190326115454-b78bee159e98/go.mod h1:D+mhXWLSLHQ+I2zZzYfrSzONMlE6FnPFw9hM4oDcN8Y=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.4.2-0.20190710153559-aa8249ae1b8b h1:+Ga+YpCDpcY1fln6GI0fiiirpqHGcob5/Vk3oKNuGdU=
github.com/docker/docker v1.4.2-0.20190710153559-aa8249ae1b8b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsouza/go-dockerclient v1.4.2 h1:dl6GfIWS5Qn4C6OfSnnoe6YuOV8lvKAE8W/YD1Q7udo=
github.com/fsouza/go-dockerclient v1.4.2/go.mod h1:COunfLZrsdwX/j3YVDAG8gIw3KutrI0x1+vGEJ5zxdI=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680 h1:ZktWZesgun21uEDrwW7iEV1zPCGQldM2atlJZ3TdvVM=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v0.0.0-20160524151835-7d79101e329e h1:JHB7F/4TJCrYBW8+GZO8VkWDj1jxcWuCl6uxKODiyi4=
github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7 h1:6TSoaYExHper8PYsJu23GWVNOyYRCSnIFyxKgLSZ54w=
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd h1:anPrsicrIi2ColgWTVPk+TrN42hJIWlfPHSBP9S0ZkM=
github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84=
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/miekg/dns v1.1.15 h1:CSSIDtllwGLMoA6zjdKnaE6Tx6eVUxQ29LUgGetiDCI=
github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runtime-spec v0.0.0-20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68=
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb h1:i1Ppqkc3WQXikh8bXiwHqAN5Rv3/qDCcRk0/Otx73BY=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw=
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.0.0-20190118113203-912cbe2bfef3 h1:lV0+KGoNkvZOt4zGT4H83hQrzWMt/US/LSz4z4+BQS4=
k8s.io/api v0.0.0-20190118113203-912cbe2bfef3/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841 h1:Q4RZrHNtlC/mSdC1sTrcZ5RchC/9vxLVj57pWiCBKv4=
k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/client-go v0.0.0-20190117233410-4022682532b3 h1:7VVBo3+/iX6dzB8dshNuo6Duds/6AoNP5R59IUnwoxg=
k8s.io/client-go v0.0.0-20190117233410-4022682532b3/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058 h1:di3XCwddOR9cWBNpfgXaskhh6cgJuwcK54rvtwUaC10=
k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/metrics v0.0.0-20181204004050-3d8dd1c3a53c h1:C5pPzP70ZI2ZxBm/5YHVwcIFZzxJ7lhW/5qOXgQIc+E=
k8s.io/metrics v0.0.0-20181204004050-3d8dd1c3a53c/go.mod h1:a25VAbm3QT3xiVl1jtoF1ueAKQM149UdZ+L93ePfV3M=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=

8
pkg/atom/Makefile Normal file
View File

@ -0,0 +1,8 @@
all: generate
generate:
# go get -u k8s.io/code-generator/...
../../vendor/k8s.io/code-generator/generate-groups.sh all \
github.com/convox/convox/pkg/atom/pkg/client \
github.com/convox/convox/pkg/atom/pkg/apis \
convox:v1,v2

591
pkg/atom/atom.go Normal file
View File

@ -0,0 +1,591 @@
package atom
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"html/template"
"os/exec"
"sort"
"strings"
"time"
ca "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
cv "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned"
"github.com/convox/convox/pkg/templater"
"github.com/gobuffalo/packr"
"github.com/pkg/errors"
yaml "gopkg.in/yaml.v2"
ac "k8s.io/api/core/v1"
ae "k8s.io/apimachinery/pkg/api/errors"
am "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
)
var (
templates = templater.New(packr.NewBox("../atom/templates"), templateHelpers())
)
type Client struct {
config *rest.Config
convox cv.Interface
k8s kubernetes.Interface
}
func New(cfg *rest.Config) (*Client, error) {
cc, err := cv.NewForConfig(cfg)
if err != nil {
return nil, errors.WithStack(err)
}
kc, err := kubernetes.NewForConfig(cfg)
if err != nil {
return nil, errors.WithStack(err)
}
c := &Client{
config: cfg,
convox: cc,
k8s: kc,
}
return c, nil
}
func Apply(namespace, name, release string, template []byte, timeout int32) error {
params := map[string]interface{}{
"Name": name,
"Namespace": namespace,
"Release": release,
"Template": base64.StdEncoding.EncodeToString(template),
"Timeout": timeout,
"Version": time.Now().UTC().UnixNano(),
}
if err := exec.Command("kubectl", "get", fmt.Sprintf("ns/%s", namespace)).Run(); err != nil {
if err := kubectlApplyTemplate("namespace.yml.tmpl", params); err != nil {
return err
}
for {
if err := exec.Command("kubectl", "get", fmt.Sprintf("ns/%s", namespace)).Run(); err == nil {
break
}
time.Sleep(1 * time.Second)
}
}
data, err := templates.Render("version.yml.tmpl", params)
if err != nil {
return err
}
if data, err := kubectlCreate(data); err != nil {
return fmt.Errorf("could not create atom version for %s/%s: %s", name, release, strings.TrimSpace(string(data)))
}
if err := kubectlApplyTemplate("atom.yml.tmpl", params); err != nil {
return err
}
return nil
}
func Wait(namespace, name string) error {
for {
data, err := exec.Command("kubectl", "get", fmt.Sprintf("atom/%s", name), "-n", namespace, "-o", "jsonpath={.status}").CombinedOutput()
if err != nil {
return errors.WithStack(err)
}
switch string(data) {
case "Success":
return nil
case "Failure":
return errors.WithStack(fmt.Errorf("atom failed"))
}
}
}
func (c *Client) Apply(ns, name string, release string, template []byte, timeout int32) error {
if _, err := c.k8s.CoreV1().Namespaces().Get(ns, am.GetOptions{}); ae.IsNotFound(err) {
_, err := c.k8s.CoreV1().Namespaces().Create(&ac.Namespace{
ObjectMeta: am.ObjectMeta{
Name: ns,
},
})
if err != nil {
return errors.WithStack(err)
}
for {
if ns, err := c.k8s.CoreV1().Namespaces().Get(ns, am.GetOptions{}); err == nil && ns != nil {
break
}
time.Sleep(1 * time.Second)
}
}
v, err := c.convox.ConvoxV2().AtomVersions(ns).Create(&ca.AtomVersion{
ObjectMeta: am.ObjectMeta{
Name: fmt.Sprintf("%s-%d", name, time.Now().UTC().UnixNano()),
},
Spec: ca.AtomVersionSpec{
Release: release,
Template: template,
},
})
if err != nil {
return errors.WithStack(err)
}
a, err := c.convox.ConvoxV2().Atoms(ns).Get(name, am.GetOptions{})
switch {
case ae.IsNotFound(err):
a, err = c.convox.ConvoxV2().Atoms(ns).Create(&ca.Atom{
ObjectMeta: am.ObjectMeta{
Name: name,
},
})
if err != nil {
return errors.WithStack(err)
}
case err != nil:
return errors.WithStack(err)
default:
a.Spec.PreviousVersion = a.Spec.CurrentVersion
}
a.Spec.CurrentVersion = v.Name
a.Spec.ProgressDeadlineSeconds = timeout
a.Status = "Pending"
if _, err := c.convox.ConvoxV2().Atoms(ns).Update(a); err != nil {
return errors.WithStack(err)
}
return nil
}
func (c *Client) Status(ns, name string) (string, string, error) {
a, err := c.convox.ConvoxV2().Atoms(ns).Get(name, am.GetOptions{})
if ae.IsNotFound(err) {
return "", "", nil
}
if err != nil {
return "", "", errors.WithStack(err)
}
release := ""
if a.Spec.CurrentVersion != "" {
v, err := c.convox.ConvoxV2().AtomVersions(ns).Get(a.Spec.CurrentVersion, am.GetOptions{})
if err != nil {
return "", "", err
}
release = v.Spec.Release
}
return string(a.Status), release, nil
}
func (c *Client) Wait(ns, name string) error {
for {
a, err := c.convox.ConvoxV2().Atoms(ns).Get(name, am.GetOptions{})
if err != nil {
return errors.WithStack(err)
}
switch a.Status {
case "Success":
return nil
case "Failure":
return errors.WithStack(fmt.Errorf("atom failed"))
}
}
}
func (c *Client) apply(a *ca.Atom) error {
var err error
a.Status = "Building"
a, err = c.convox.ConvoxV2().Atoms(a.Namespace).Update(a)
if err != nil {
return errors.WithStack(err)
}
v, err := c.convox.ConvoxV2().AtomVersions(a.Namespace).Get(a.Spec.CurrentVersion, am.GetOptions{})
if err != nil {
return err
}
cs, err := extractConditions(v.Spec.Template)
if err != nil {
return errors.WithStack(err)
}
a.Spec.Conditions = cs
fmt.Printf("string(v.Spec.Template) = %+v\n", string(v.Spec.Template))
out, err := applyTemplate(v.Spec.Template, fmt.Sprintf("atom=%s.%s", a.Namespace, a.Name))
fmt.Printf("string(out) = %+v\n", string(out))
fmt.Printf("err = %+v\n", err)
if err != nil {
return errors.WithStack(errors.New(strings.TrimSpace(string(out))))
}
time.Sleep(1 * time.Second)
a.Started = am.Now()
a.Status = "Running"
a, err = c.convox.ConvoxV2().Atoms(a.Namespace).Update(a)
if err != nil {
return errors.WithStack(err)
}
return nil
}
func (c *Client) check(a *ca.Atom) (bool, error) {
cfg := *c.config
cfg.APIPath = "/apis"
cfg.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
for _, c := range a.Spec.Conditions {
gv, err := schema.ParseGroupVersion(c.ApiVersion)
if err != nil {
return false, errors.WithStack(err)
}
cfg.GroupVersion = &gv
rc, err := rest.RESTClientFor(&cfg)
if err != nil {
return false, errors.WithStack(err)
}
data, err := rc.Get().Namespace(c.Namespace).Name(c.Name).VersionedParams(&am.GetOptions{}, scheme.ParameterCodec).Resource(fmt.Sprintf("%ss", strings.ToLower(c.Kind))).Do().Raw()
if err != nil {
return false, errors.WithStack(err)
}
var o struct {
Status struct {
Conditions []struct {
Type string
Status string
Reason string
}
}
}
if err := json.Unmarshal(data, &o); err != nil {
return false, errors.WithStack(err)
}
css := map[string]string{}
crs := map[string]string{}
for _, c := range o.Status.Conditions {
css[c.Type] = c.Status
crs[c.Type] = c.Reason
}
for k, c := range c.Conditions {
if c.Status != css[k] {
return false, nil
}
if c.Reason != "" && c.Reason != crs[k] {
return false, nil
}
}
}
return true, nil
}
func (c *Client) rollback(a *ca.Atom) error {
v, err := c.convox.ConvoxV2().AtomVersions(a.Namespace).Get(a.Spec.PreviousVersion, am.GetOptions{})
if err != nil {
return err
}
out, err := applyTemplate(v.Spec.Template, fmt.Sprintf("atom=%s.%s", a.Namespace, a.Name))
if err != nil {
return errors.WithStack(errors.New(strings.TrimSpace(string(out))))
}
time.Sleep(1 * time.Second)
a.Spec.CurrentVersion = v.Name
a.Spec.PreviousVersion = ""
a.Started = am.Now()
a.Status = "Rollback"
a, err = c.convox.ConvoxV2().Atoms(a.Namespace).Update(a)
if err != nil {
return errors.WithStack(err)
}
return nil
}
func (c *Client) status(a *ca.Atom, status string) error {
var err error
a.Status = ca.AtomStatus(status)
a, err = c.convox.ConvoxV2().Atoms(a.Namespace).Update(a)
if err != nil {
return errors.WithStack(err)
}
return nil
}
func applyLabels(data []byte, labels map[string]string) ([]byte, error) {
var v map[string]interface{}
if err := yaml.Unmarshal(data, &v); err != nil {
return nil, errors.WithStack(err)
}
if len(v) == 0 {
return data, nil
}
switch t := v["metadata"].(type) {
case nil:
v["metadata"] = map[string]interface{}{"labels": labels}
case map[interface{}]interface{}:
switch u := t["labels"].(type) {
case nil:
t["labels"] = labels
v["metadata"] = t
case map[interface{}]interface{}:
for k, v := range labels {
u[k] = v
}
t["labels"] = u
v["metadata"] = t
default:
return nil, errors.WithStack(fmt.Errorf("unknown labels type: %T", u))
}
default:
return nil, errors.WithStack(fmt.Errorf("unknown metadata type: %T", t))
}
pd, err := yaml.Marshal(v)
if err != nil {
return nil, errors.WithStack(err)
}
return pd, nil
}
func applyTemplate(data []byte, filter string) ([]byte, error) {
rs, err := templateResources(filter)
if err != nil {
return nil, errors.WithStack(err)
}
labels := parseLabels(filter)
parts := bytes.Split(data, []byte("---\n"))
for i := range parts {
dp, err := applyLabels(parts[i], labels)
if err != nil {
return nil, errors.WithStack(err)
}
parts[i] = dp
}
data = bytes.Join(parts, []byte("---\n"))
args := []string{"--prune", "-l", filter}
for _, r := range rs {
args = append(args, "--prune-whitelist", r)
}
out, err := kubectlApply(data, args...)
if err != nil {
if !strings.Contains(string(out), "is immutable") {
return out, errors.WithStack(err)
}
out, err := kubectlApply(data, "--force")
if err != nil {
return out, errors.WithStack(err)
}
}
return out, nil
}
func extractConditions(data []byte) ([]ca.AtomCondition, error) {
cs := []ca.AtomCondition{}
parts := bytes.Split(data, []byte("---\n"))
for _, p := range parts {
var o struct {
ApiVersion string `yaml:"apiVersion"`
Kind string
Metadata struct {
Annotations map[string]string
Name string
Namespace string
}
}
if err := yaml.Unmarshal(p, &o); err != nil {
return nil, errors.WithStack(err)
}
if ac, ok := o.Metadata.Annotations["atom.conditions"]; ok {
acps := strings.Split(ac, ",")
acs := map[string]ca.AtomConditionMatch{}
for _, acp := range acps {
if acpps := strings.SplitN(acp, "=", 2); len(acpps) == 2 {
if vps := strings.SplitN(acpps[1], "/", 2); len(vps) == 2 {
acs[acpps[0]] = ca.AtomConditionMatch{Status: vps[0], Reason: vps[1]}
} else {
acs[acpps[0]] = ca.AtomConditionMatch{Status: vps[0]}
}
}
}
cs = append(cs, ca.AtomCondition{
ApiVersion: o.ApiVersion,
Conditions: acs,
Kind: o.Kind,
Name: o.Metadata.Name,
Namespace: o.Metadata.Namespace,
})
}
}
return cs, nil
}
func kubectlApply(data []byte, args ...string) ([]byte, error) {
ka := append([]string{"apply", "-f", "-"}, args...)
cmd := exec.Command("kubectl", ka...)
cmd.Stdin = bytes.NewReader(data)
return cmd.CombinedOutput()
}
func kubectlCreate(data []byte, args ...string) ([]byte, error) {
ka := append([]string{"create", "-f", "-"}, args...)
cmd := exec.Command("kubectl", ka...)
cmd.Stdin = bytes.NewReader(data)
return cmd.CombinedOutput()
}
func kubectlApplyTemplate(template string, params map[string]interface{}) error {
data, err := templates.Render(template, params)
if err != nil {
return err
}
if out, err := kubectlApply(data); err != nil {
return errors.New(strings.TrimSpace(string(out)))
}
return nil
}
func parseLabels(labels string) map[string]string {
ls := map[string]string{}
for _, part := range strings.Split(labels, ",") {
ps := strings.SplitN(strings.TrimSpace(part), "=", 2)
if len(ps) == 2 {
ls[ps[0]] = ps[1]
}
}
return ls
}
func templateResources(filter string) ([]string, error) {
data, err := exec.Command("kubectl", "api-resources", "--verbs=list", "--namespaced", "-o", "name").CombinedOutput()
if err != nil {
return []string{}, nil
}
ars := strings.Split(strings.TrimSpace(string(data)), "\n")
rsh := map[string]bool{}
data, err = exec.Command("kubectl", "get", "-l", filter, "--all-namespaces", "-o", "json", strings.Join(ars, ",")).CombinedOutput()
if err != nil {
return []string{}, nil
}
if strings.TrimSpace(string(data)) == "" {
return []string{}, nil
}
var res struct {
Items []struct {
ApiVersion string `json:"apiVersion"`
Kind string `json:"kind"`
}
}
if err := json.Unmarshal(data, &res); err != nil {
return nil, errors.WithStack(err)
}
for _, i := range res.Items {
av := i.ApiVersion
if !strings.Contains(av, "/") {
av = fmt.Sprintf("core/%s", av)
}
rsh[fmt.Sprintf("%s/%s", av, i.Kind)] = true
}
rs := []string{}
for r := range rsh {
rs = append(rs, r)
}
sort.Strings(rs)
return rs, nil
}
func templateHelpers() map[string]interface{} {
return map[string]interface{}{
"safe": func(s string) template.HTML {
return template.HTML(fmt.Sprintf("%q", s))
},
}
}

175
pkg/atom/controller.go Normal file
View File

@ -0,0 +1,175 @@
package atom
import (
"fmt"
"time"
ct "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
cv "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned"
ic "github.com/convox/convox/pkg/atom/pkg/client/informers/externalversions/convox/v2"
"github.com/convox/convox/pkg/kctl"
"github.com/pkg/errors"
ac "k8s.io/api/core/v1"
am "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
)
type AtomController struct {
atom *Client
controller *kctl.Controller
convox cv.Interface
kubernetes kubernetes.Interface
}
func NewController(cfg *rest.Config) (*AtomController, error) {
ac, err := New(cfg)
if err != nil {
return nil, errors.WithStack(err)
}
cc, err := cv.NewForConfig(cfg)
if err != nil {
return nil, errors.WithStack(err)
}
kc, err := kubernetes.NewForConfig(cfg)
if err != nil {
return nil, errors.WithStack(err)
}
acc := &AtomController{
atom: ac,
convox: cc,
kubernetes: kc,
}
c, err := kctl.NewController("kube-system", "convox-atom", acc)
if err != nil {
return nil, errors.WithStack(err)
}
acc.controller = c
return acc, nil
}
func (c *AtomController) Client() kubernetes.Interface {
return c.kubernetes
}
func (c *AtomController) ListOptions(opts *am.ListOptions) {
}
func (c *AtomController) Run() {
i := ic.NewFilteredAtomInformer(c.convox, ac.NamespaceAll, 5*time.Second, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, c.ListOptions)
ch := make(chan error)
go c.controller.Run(i, ch)
for err := range ch {
fmt.Printf("err = %+v\n", err)
}
}
func (c *AtomController) Start() error {
return nil
}
func (c *AtomController) Stop() error {
return nil
}
func (c *AtomController) Add(obj interface{}) error {
return nil
}
func (c *AtomController) Delete(obj interface{}) error {
return nil
}
func (c *AtomController) Update(prev, cur interface{}) error {
pa, err := assertAtom(prev)
if err != nil {
return errors.WithStack(err)
}
ca, err := assertAtom(cur)
if err != nil {
return errors.WithStack(err)
}
switch ca.Status {
case "Failed", "Reverted", "Success":
if pa.ResourceVersion == ca.ResourceVersion {
return nil
}
}
fmt.Printf("atom: %s/%s (%s)\n", ca.Namespace, ca.Name, ca.Status)
// if ca.Spec.Current != pa.Spec.Current {
// fmt.Printf("atom changed: %s/%s\n", ca.Namespace, ca.Name)
// return nil
// }
switch ca.Status {
case "Cancelled", "Deadline", "Error":
if err := c.atom.rollback(ca); err != nil {
c.atom.status(ca, "Failed")
return errors.WithStack(err)
}
case "Cleanup":
ca.Spec.PreviousVersion = ""
c.atom.status(ca, "Success")
case "Pending":
if err := c.atom.apply(ca); err != nil {
c.atom.status(ca, "Rollback")
return errors.WithStack(err)
}
case "Rollback":
if deadline := am.NewTime(time.Now().UTC().Add(-1 * time.Duration(ca.Spec.ProgressDeadlineSeconds) * time.Second)); ca.Started.Before(&deadline) {
c.atom.status(ca, "Failed")
return nil
}
success, err := c.atom.check(ca)
if err != nil {
c.atom.status(ca, "Failed")
return errors.WithStack(err)
}
if success {
c.atom.status(ca, "Reverted")
}
case "Running":
if deadline := am.NewTime(time.Now().UTC().Add(-1 * time.Duration(ca.Spec.ProgressDeadlineSeconds) * time.Second)); ca.Started.Before(&deadline) {
c.atom.status(ca, "Deadline")
return nil
}
success, err := c.atom.check(ca)
if err != nil {
c.atom.status(ca, "Error")
return errors.WithStack(err)
}
if success {
c.atom.status(ca, "Cleanup")
}
}
return nil
}
func assertAtom(v interface{}) (*ct.Atom, error) {
a, ok := v.(*ct.Atom)
if !ok {
return nil, errors.WithStack(fmt.Errorf("could not assert atom for type: %T", v))
}
return a, nil
}

View File

@ -0,0 +1,5 @@
package convox
const (
GroupName = "convox.com"
)

View File

@ -0,0 +1,4 @@
// +k8s:deepcopy-gen=package
// +groupName=convox.com
package v1

View File

@ -0,0 +1,37 @@
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
convox "github.com/convox/convox/provider/k8s/pkg/apis/convox"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: convox.GroupName, Version: "v1"}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Atom{},
&AtomList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,53 @@
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type Atom struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Started metav1.Time `json:"started"`
Status AtomStatus `json:"status"`
Spec AtomSpec `json:"spec"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type AtomList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Atom `json:"items"`
}
type AtomCondition struct {
ApiVersion string `json:"apiVersion"`
Conditions map[string]AtomConditionMatch `json:"conditions"`
Kind string `json:"kind"`
Name string `json:"name"`
Namespace string `json:"namespace"`
}
type AtomConditionMatch struct {
Reason string `json:"reason"`
Status string `json:"status"`
}
type AtomSpec struct {
Conditions []AtomCondition `json:"conditions"`
Current AtomState `json:"current"`
Previous AtomState `json:"previous"`
ProgressDeadlineSeconds int32 `json:"progressDeadlineSeconds"`
}
type AtomStatus string
type AtomState struct {
Template []byte `json:"template"`
Version string `json:"version"`
}

View File

@ -0,0 +1,171 @@
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Atom) DeepCopyInto(out *Atom) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Started.DeepCopyInto(&out.Started)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Atom.
func (in *Atom) DeepCopy() *Atom {
if in == nil {
return nil
}
out := new(Atom)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Atom) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AtomCondition) DeepCopyInto(out *AtomCondition) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make(map[string]AtomConditionMatch, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AtomCondition.
func (in *AtomCondition) DeepCopy() *AtomCondition {
if in == nil {
return nil
}
out := new(AtomCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AtomConditionMatch) DeepCopyInto(out *AtomConditionMatch) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AtomConditionMatch.
func (in *AtomConditionMatch) DeepCopy() *AtomConditionMatch {
if in == nil {
return nil
}
out := new(AtomConditionMatch)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AtomList) DeepCopyInto(out *AtomList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Atom, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AtomList.
func (in *AtomList) DeepCopy() *AtomList {
if in == nil {
return nil
}
out := new(AtomList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *AtomList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AtomSpec) DeepCopyInto(out *AtomSpec) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]AtomCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
in.Current.DeepCopyInto(&out.Current)
in.Previous.DeepCopyInto(&out.Previous)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AtomSpec.
func (in *AtomSpec) DeepCopy() *AtomSpec {
if in == nil {
return nil
}
out := new(AtomSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AtomState) DeepCopyInto(out *AtomState) {
*out = *in
if in.Template != nil {
in, out := &in.Template, &out.Template
*out = make([]byte, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AtomState.
func (in *AtomState) DeepCopy() *AtomState {
if in == nil {
return nil
}
out := new(AtomState)
in.DeepCopyInto(out)
return out
}

View File

@ -0,0 +1,4 @@
// +k8s:deepcopy-gen=package
// +groupName=convox.com
package v2

View File

@ -0,0 +1,39 @@
package v2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
convox "github.com/convox/convox/provider/k8s/pkg/apis/convox"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: convox.GroupName, Version: "v2"}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Atom{},
&AtomList{},
&AtomVersion{},
&AtomVersionList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,75 @@
package v2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type Atom struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Started metav1.Time `json:"started"`
Status AtomStatus `json:"status"`
Spec AtomSpec `json:"spec"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type AtomList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Atom `json:"items"`
}
type AtomCondition struct {
ApiVersion string `json:"apiVersion"`
Conditions map[string]AtomConditionMatch `json:"conditions"`
Kind string `json:"kind"`
Name string `json:"name"`
Namespace string `json:"namespace"`
}
type AtomConditionMatch struct {
Reason string `json:"reason"`
Status string `json:"status"`
}
type AtomSpec struct {
Conditions []AtomCondition `json:"conditions"`
CurrentVersion string `json:"currentVersion"`
PreviousVersion string `json:"previousVersion"`
ProgressDeadlineSeconds int32 `json:"progressDeadlineSeconds"`
}
type AtomStatus string
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type AtomVersion struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Status AtomVersionStatus `json:"status"`
Spec AtomVersionSpec `json:"spec"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type AtomVersionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []AtomVersion `json:"items"`
}
type AtomVersionSpec struct {
Release string `json:"release"`
Template []byte `json:"template"`
}
type AtomVersionStatus string

View File

@ -0,0 +1,229 @@
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v2
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Atom) DeepCopyInto(out *Atom) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Started.DeepCopyInto(&out.Started)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Atom.
func (in *Atom) DeepCopy() *Atom {
if in == nil {
return nil
}
out := new(Atom)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Atom) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AtomCondition) DeepCopyInto(out *AtomCondition) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make(map[string]AtomConditionMatch, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AtomCondition.
func (in *AtomCondition) DeepCopy() *AtomCondition {
if in == nil {
return nil
}
out := new(AtomCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AtomConditionMatch) DeepCopyInto(out *AtomConditionMatch) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AtomConditionMatch.
func (in *AtomConditionMatch) DeepCopy() *AtomConditionMatch {
if in == nil {
return nil
}
out := new(AtomConditionMatch)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AtomList) DeepCopyInto(out *AtomList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Atom, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AtomList.
func (in *AtomList) DeepCopy() *AtomList {
if in == nil {
return nil
}
out := new(AtomList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *AtomList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AtomSpec) DeepCopyInto(out *AtomSpec) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]AtomCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AtomSpec.
func (in *AtomSpec) DeepCopy() *AtomSpec {
if in == nil {
return nil
}
out := new(AtomSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AtomVersion) DeepCopyInto(out *AtomVersion) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AtomVersion.
func (in *AtomVersion) DeepCopy() *AtomVersion {
if in == nil {
return nil
}
out := new(AtomVersion)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *AtomVersion) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AtomVersionList) DeepCopyInto(out *AtomVersionList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]AtomVersion, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AtomVersionList.
func (in *AtomVersionList) DeepCopy() *AtomVersionList {
if in == nil {
return nil
}
out := new(AtomVersionList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *AtomVersionList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AtomVersionSpec) DeepCopyInto(out *AtomVersionSpec) {
*out = *in
if in.Template != nil {
in, out := &in.Template, &out.Template
*out = make([]byte, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AtomVersionSpec.
func (in *AtomVersionSpec) DeepCopy() *AtomVersionSpec {
if in == nil {
return nil
}
out := new(AtomVersionSpec)
in.DeepCopyInto(out)
return out
}

View File

@ -0,0 +1,112 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package versioned
import (
convoxv1 "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/typed/convox/v1"
convoxv2 "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/typed/convox/v2"
discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest"
flowcontrol "k8s.io/client-go/util/flowcontrol"
)
type Interface interface {
Discovery() discovery.DiscoveryInterface
ConvoxV1() convoxv1.ConvoxV1Interface
ConvoxV2() convoxv2.ConvoxV2Interface
// Deprecated: please explicitly pick a version if possible.
Convox() convoxv2.ConvoxV2Interface
}
// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
*discovery.DiscoveryClient
convoxV1 *convoxv1.ConvoxV1Client
convoxV2 *convoxv2.ConvoxV2Client
}
// ConvoxV1 retrieves the ConvoxV1Client
func (c *Clientset) ConvoxV1() convoxv1.ConvoxV1Interface {
return c.convoxV1
}
// ConvoxV2 retrieves the ConvoxV2Client
func (c *Clientset) ConvoxV2() convoxv2.ConvoxV2Interface {
return c.convoxV2
}
// Deprecated: Convox retrieves the default version of ConvoxClient.
// Please explicitly pick a version.
func (c *Clientset) Convox() convoxv2.ConvoxV2Interface {
return c.convoxV2
}
// Discovery retrieves the DiscoveryClient
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
if c == nil {
return nil
}
return c.DiscoveryClient
}
// NewForConfig creates a new Clientset for the given config.
func NewForConfig(c *rest.Config) (*Clientset, error) {
configShallowCopy := *c
if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
}
var cs Clientset
var err error
cs.convoxV1, err = convoxv1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
cs.convoxV2, err = convoxv2.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
return &cs, nil
}
// NewForConfigOrDie creates a new Clientset for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *Clientset {
var cs Clientset
cs.convoxV1 = convoxv1.NewForConfigOrDie(c)
cs.convoxV2 = convoxv2.NewForConfigOrDie(c)
cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
return &cs
}
// New creates a new Clientset for the given RESTClient.
func New(c rest.Interface) *Clientset {
var cs Clientset
cs.convoxV1 = convoxv1.New(c)
cs.convoxV2 = convoxv2.New(c)
cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
return &cs
}

View File

@ -0,0 +1,20 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated clientset.
package versioned

View File

@ -0,0 +1,89 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
clientset "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned"
convoxv1 "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/typed/convox/v1"
fakeconvoxv1 "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/typed/convox/v1/fake"
convoxv2 "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/typed/convox/v2"
fakeconvoxv2 "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/typed/convox/v2/fake"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
fakediscovery "k8s.io/client-go/discovery/fake"
"k8s.io/client-go/testing"
)
// NewSimpleClientset returns a clientset that will respond with the provided objects.
// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
// without applying any validations and/or defaults. It shouldn't be considered a replacement
// for a real clientset and is mostly useful in simple unit tests.
func NewSimpleClientset(objects ...runtime.Object) *Clientset {
o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
for _, obj := range objects {
if err := o.Add(obj); err != nil {
panic(err)
}
}
cs := &Clientset{}
cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
cs.AddReactor("*", "*", testing.ObjectReaction(o))
cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
gvr := action.GetResource()
ns := action.GetNamespace()
watch, err := o.Watch(gvr, ns)
if err != nil {
return false, nil, err
}
return true, watch, nil
})
return cs
}
// Clientset implements clientset.Interface. Meant to be embedded into a
// struct to get a default implementation. This makes faking out just the method
// you want to test easier.
type Clientset struct {
testing.Fake
discovery *fakediscovery.FakeDiscovery
}
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
return c.discovery
}
var _ clientset.Interface = &Clientset{}
// ConvoxV1 retrieves the ConvoxV1Client
func (c *Clientset) ConvoxV1() convoxv1.ConvoxV1Interface {
return &fakeconvoxv1.FakeConvoxV1{Fake: &c.Fake}
}
// ConvoxV2 retrieves the ConvoxV2Client
func (c *Clientset) ConvoxV2() convoxv2.ConvoxV2Interface {
return &fakeconvoxv2.FakeConvoxV2{Fake: &c.Fake}
}
// Convox retrieves the ConvoxV2Client
func (c *Clientset) Convox() convoxv2.ConvoxV2Interface {
return &fakeconvoxv2.FakeConvoxV2{Fake: &c.Fake}
}

View File

@ -0,0 +1,20 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated fake clientset.
package fake

View File

@ -0,0 +1,56 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
convoxv1 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v1"
convoxv2 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
)
var scheme = runtime.NewScheme()
var codecs = serializer.NewCodecFactory(scheme)
var parameterCodec = runtime.NewParameterCodec(scheme)
func init() {
v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
AddToScheme(scheme)
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.
func AddToScheme(scheme *runtime.Scheme) {
convoxv1.AddToScheme(scheme)
convoxv2.AddToScheme(scheme)
}

View File

@ -0,0 +1,20 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package contains the scheme of the automatically generated clientset.
package scheme

View File

@ -0,0 +1,56 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package scheme
import (
convoxv1 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v1"
convoxv2 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
)
var Scheme = runtime.NewScheme()
var Codecs = serializer.NewCodecFactory(Scheme)
var ParameterCodec = runtime.NewParameterCodec(Scheme)
func init() {
v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
AddToScheme(Scheme)
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.
func AddToScheme(scheme *runtime.Scheme) {
convoxv1.AddToScheme(scheme)
convoxv2.AddToScheme(scheme)
}

View File

@ -0,0 +1,174 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1
import (
v1 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v1"
scheme "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/scheme"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// AtomsGetter has a method to return a AtomInterface.
// A group's client should implement this interface.
type AtomsGetter interface {
Atoms(namespace string) AtomInterface
}
// AtomInterface has methods to work with Atom resources.
type AtomInterface interface {
Create(*v1.Atom) (*v1.Atom, error)
Update(*v1.Atom) (*v1.Atom, error)
UpdateStatus(*v1.Atom) (*v1.Atom, error)
Delete(name string, options *metav1.DeleteOptions) error
DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error
Get(name string, options metav1.GetOptions) (*v1.Atom, error)
List(opts metav1.ListOptions) (*v1.AtomList, error)
Watch(opts metav1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Atom, err error)
AtomExpansion
}
// atoms implements AtomInterface
type atoms struct {
client rest.Interface
ns string
}
// newAtoms returns a Atoms
func newAtoms(c *ConvoxV1Client, namespace string) *atoms {
return &atoms{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the atom, and returns the corresponding atom object, and an error if there is any.
func (c *atoms) Get(name string, options metav1.GetOptions) (result *v1.Atom, err error) {
result = &v1.Atom{}
err = c.client.Get().
Namespace(c.ns).
Resource("atoms").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of Atoms that match those selectors.
func (c *atoms) List(opts metav1.ListOptions) (result *v1.AtomList, err error) {
result = &v1.AtomList{}
err = c.client.Get().
Namespace(c.ns).
Resource("atoms").
VersionedParams(&opts, scheme.ParameterCodec).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested atoms.
func (c *atoms) Watch(opts metav1.ListOptions) (watch.Interface, error) {
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("atoms").
VersionedParams(&opts, scheme.ParameterCodec).
Watch()
}
// Create takes the representation of a atom and creates it. Returns the server's representation of the atom, and an error, if there is any.
func (c *atoms) Create(atom *v1.Atom) (result *v1.Atom, err error) {
result = &v1.Atom{}
err = c.client.Post().
Namespace(c.ns).
Resource("atoms").
Body(atom).
Do().
Into(result)
return
}
// Update takes the representation of a atom and updates it. Returns the server's representation of the atom, and an error, if there is any.
func (c *atoms) Update(atom *v1.Atom) (result *v1.Atom, err error) {
result = &v1.Atom{}
err = c.client.Put().
Namespace(c.ns).
Resource("atoms").
Name(atom.Name).
Body(atom).
Do().
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *atoms) UpdateStatus(atom *v1.Atom) (result *v1.Atom, err error) {
result = &v1.Atom{}
err = c.client.Put().
Namespace(c.ns).
Resource("atoms").
Name(atom.Name).
SubResource("status").
Body(atom).
Do().
Into(result)
return
}
// Delete takes name of the atom and deletes it. Returns an error if one occurs.
func (c *atoms) Delete(name string, options *metav1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("atoms").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *atoms) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("atoms").
VersionedParams(&listOptions, scheme.ParameterCodec).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched atom.
func (c *atoms) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Atom, err error) {
result = &v1.Atom{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("atoms").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@ -0,0 +1,90 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1
import (
v1 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v1"
"github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/scheme"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
rest "k8s.io/client-go/rest"
)
type ConvoxV1Interface interface {
RESTClient() rest.Interface
AtomsGetter
}
// ConvoxV1Client is used to interact with features provided by the convox.com group.
type ConvoxV1Client struct {
restClient rest.Interface
}
func (c *ConvoxV1Client) Atoms(namespace string) AtomInterface {
return newAtoms(c, namespace)
}
// NewForConfig creates a new ConvoxV1Client for the given config.
func NewForConfig(c *rest.Config) (*ConvoxV1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &ConvoxV1Client{client}, nil
}
// NewForConfigOrDie creates a new ConvoxV1Client for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *ConvoxV1Client {
client, err := NewForConfig(c)
if err != nil {
panic(err)
}
return client
}
// New creates a new ConvoxV1Client for the given RESTClient.
func New(c rest.Interface) *ConvoxV1Client {
return &ConvoxV1Client{c}
}
func setConfigDefaults(config *rest.Config) error {
gv := v1.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return nil
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *ConvoxV1Client) RESTClient() rest.Interface {
if c == nil {
return nil
}
return c.restClient
}

View File

@ -0,0 +1,20 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated typed clients.
package v1

View File

@ -0,0 +1,20 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// Package fake has the automatically generated clients.
package fake

View File

@ -0,0 +1,140 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
convoxv1 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeAtoms implements AtomInterface
type FakeAtoms struct {
Fake *FakeConvoxV1
ns string
}
var atomsResource = schema.GroupVersionResource{Group: "convox.com", Version: "v1", Resource: "atoms"}
var atomsKind = schema.GroupVersionKind{Group: "convox.com", Version: "v1", Kind: "Atom"}
// Get takes name of the atom, and returns the corresponding atom object, and an error if there is any.
func (c *FakeAtoms) Get(name string, options v1.GetOptions) (result *convoxv1.Atom, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(atomsResource, c.ns, name), &convoxv1.Atom{})
if obj == nil {
return nil, err
}
return obj.(*convoxv1.Atom), err
}
// List takes label and field selectors, and returns the list of Atoms that match those selectors.
func (c *FakeAtoms) List(opts v1.ListOptions) (result *convoxv1.AtomList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(atomsResource, atomsKind, c.ns, opts), &convoxv1.AtomList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &convoxv1.AtomList{ListMeta: obj.(*convoxv1.AtomList).ListMeta}
for _, item := range obj.(*convoxv1.AtomList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested atoms.
func (c *FakeAtoms) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(atomsResource, c.ns, opts))
}
// Create takes the representation of a atom and creates it. Returns the server's representation of the atom, and an error, if there is any.
func (c *FakeAtoms) Create(atom *convoxv1.Atom) (result *convoxv1.Atom, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(atomsResource, c.ns, atom), &convoxv1.Atom{})
if obj == nil {
return nil, err
}
return obj.(*convoxv1.Atom), err
}
// Update takes the representation of a atom and updates it. Returns the server's representation of the atom, and an error, if there is any.
func (c *FakeAtoms) Update(atom *convoxv1.Atom) (result *convoxv1.Atom, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(atomsResource, c.ns, atom), &convoxv1.Atom{})
if obj == nil {
return nil, err
}
return obj.(*convoxv1.Atom), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeAtoms) UpdateStatus(atom *convoxv1.Atom) (*convoxv1.Atom, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(atomsResource, "status", c.ns, atom), &convoxv1.Atom{})
if obj == nil {
return nil, err
}
return obj.(*convoxv1.Atom), err
}
// Delete takes name of the atom and deletes it. Returns an error if one occurs.
func (c *FakeAtoms) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(atomsResource, c.ns, name), &convoxv1.Atom{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeAtoms) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(atomsResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &convoxv1.AtomList{})
return err
}
// Patch applies the patch and returns the patched atom.
func (c *FakeAtoms) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *convoxv1.Atom, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(atomsResource, c.ns, name, data, subresources...), &convoxv1.Atom{})
if obj == nil {
return nil, err
}
return obj.(*convoxv1.Atom), err
}

View File

@ -0,0 +1,40 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1 "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/typed/convox/v1"
rest "k8s.io/client-go/rest"
testing "k8s.io/client-go/testing"
)
type FakeConvoxV1 struct {
*testing.Fake
}
func (c *FakeConvoxV1) Atoms(namespace string) v1.AtomInterface {
return &FakeAtoms{c, namespace}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeConvoxV1) RESTClient() rest.Interface {
var ret *rest.RESTClient
return ret
}

View File

@ -0,0 +1,21 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1
type AtomExpansion interface{}

View File

@ -0,0 +1,174 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v2
import (
v2 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
scheme "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/scheme"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// AtomsGetter has a method to return a AtomInterface.
// A group's client should implement this interface.
type AtomsGetter interface {
Atoms(namespace string) AtomInterface
}
// AtomInterface has methods to work with Atom resources.
type AtomInterface interface {
Create(*v2.Atom) (*v2.Atom, error)
Update(*v2.Atom) (*v2.Atom, error)
UpdateStatus(*v2.Atom) (*v2.Atom, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v2.Atom, error)
List(opts v1.ListOptions) (*v2.AtomList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2.Atom, err error)
AtomExpansion
}
// atoms implements AtomInterface
type atoms struct {
client rest.Interface
ns string
}
// newAtoms returns a Atoms
func newAtoms(c *ConvoxV2Client, namespace string) *atoms {
return &atoms{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the atom, and returns the corresponding atom object, and an error if there is any.
func (c *atoms) Get(name string, options v1.GetOptions) (result *v2.Atom, err error) {
result = &v2.Atom{}
err = c.client.Get().
Namespace(c.ns).
Resource("atoms").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of Atoms that match those selectors.
func (c *atoms) List(opts v1.ListOptions) (result *v2.AtomList, err error) {
result = &v2.AtomList{}
err = c.client.Get().
Namespace(c.ns).
Resource("atoms").
VersionedParams(&opts, scheme.ParameterCodec).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested atoms.
func (c *atoms) Watch(opts v1.ListOptions) (watch.Interface, error) {
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("atoms").
VersionedParams(&opts, scheme.ParameterCodec).
Watch()
}
// Create takes the representation of a atom and creates it. Returns the server's representation of the atom, and an error, if there is any.
func (c *atoms) Create(atom *v2.Atom) (result *v2.Atom, err error) {
result = &v2.Atom{}
err = c.client.Post().
Namespace(c.ns).
Resource("atoms").
Body(atom).
Do().
Into(result)
return
}
// Update takes the representation of a atom and updates it. Returns the server's representation of the atom, and an error, if there is any.
func (c *atoms) Update(atom *v2.Atom) (result *v2.Atom, err error) {
result = &v2.Atom{}
err = c.client.Put().
Namespace(c.ns).
Resource("atoms").
Name(atom.Name).
Body(atom).
Do().
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *atoms) UpdateStatus(atom *v2.Atom) (result *v2.Atom, err error) {
result = &v2.Atom{}
err = c.client.Put().
Namespace(c.ns).
Resource("atoms").
Name(atom.Name).
SubResource("status").
Body(atom).
Do().
Into(result)
return
}
// Delete takes name of the atom and deletes it. Returns an error if one occurs.
func (c *atoms) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("atoms").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *atoms) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("atoms").
VersionedParams(&listOptions, scheme.ParameterCodec).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched atom.
func (c *atoms) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2.Atom, err error) {
result = &v2.Atom{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("atoms").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@ -0,0 +1,174 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v2
import (
v2 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
scheme "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/scheme"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// AtomVersionsGetter has a method to return a AtomVersionInterface.
// A group's client should implement this interface.
type AtomVersionsGetter interface {
AtomVersions(namespace string) AtomVersionInterface
}
// AtomVersionInterface has methods to work with AtomVersion resources.
type AtomVersionInterface interface {
Create(*v2.AtomVersion) (*v2.AtomVersion, error)
Update(*v2.AtomVersion) (*v2.AtomVersion, error)
UpdateStatus(*v2.AtomVersion) (*v2.AtomVersion, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v2.AtomVersion, error)
List(opts v1.ListOptions) (*v2.AtomVersionList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2.AtomVersion, err error)
AtomVersionExpansion
}
// atomVersions implements AtomVersionInterface
type atomVersions struct {
client rest.Interface
ns string
}
// newAtomVersions returns a AtomVersions
func newAtomVersions(c *ConvoxV2Client, namespace string) *atomVersions {
return &atomVersions{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the atomVersion, and returns the corresponding atomVersion object, and an error if there is any.
func (c *atomVersions) Get(name string, options v1.GetOptions) (result *v2.AtomVersion, err error) {
result = &v2.AtomVersion{}
err = c.client.Get().
Namespace(c.ns).
Resource("atomversions").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of AtomVersions that match those selectors.
func (c *atomVersions) List(opts v1.ListOptions) (result *v2.AtomVersionList, err error) {
result = &v2.AtomVersionList{}
err = c.client.Get().
Namespace(c.ns).
Resource("atomversions").
VersionedParams(&opts, scheme.ParameterCodec).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested atomVersions.
func (c *atomVersions) Watch(opts v1.ListOptions) (watch.Interface, error) {
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("atomversions").
VersionedParams(&opts, scheme.ParameterCodec).
Watch()
}
// Create takes the representation of a atomVersion and creates it. Returns the server's representation of the atomVersion, and an error, if there is any.
func (c *atomVersions) Create(atomVersion *v2.AtomVersion) (result *v2.AtomVersion, err error) {
result = &v2.AtomVersion{}
err = c.client.Post().
Namespace(c.ns).
Resource("atomversions").
Body(atomVersion).
Do().
Into(result)
return
}
// Update takes the representation of a atomVersion and updates it. Returns the server's representation of the atomVersion, and an error, if there is any.
func (c *atomVersions) Update(atomVersion *v2.AtomVersion) (result *v2.AtomVersion, err error) {
result = &v2.AtomVersion{}
err = c.client.Put().
Namespace(c.ns).
Resource("atomversions").
Name(atomVersion.Name).
Body(atomVersion).
Do().
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *atomVersions) UpdateStatus(atomVersion *v2.AtomVersion) (result *v2.AtomVersion, err error) {
result = &v2.AtomVersion{}
err = c.client.Put().
Namespace(c.ns).
Resource("atomversions").
Name(atomVersion.Name).
SubResource("status").
Body(atomVersion).
Do().
Into(result)
return
}
// Delete takes name of the atomVersion and deletes it. Returns an error if one occurs.
func (c *atomVersions) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("atomversions").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *atomVersions) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("atomversions").
VersionedParams(&listOptions, scheme.ParameterCodec).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched atomVersion.
func (c *atomVersions) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2.AtomVersion, err error) {
result = &v2.AtomVersion{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("atomversions").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@ -0,0 +1,95 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v2
import (
v2 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
"github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/scheme"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
rest "k8s.io/client-go/rest"
)
type ConvoxV2Interface interface {
RESTClient() rest.Interface
AtomsGetter
AtomVersionsGetter
}
// ConvoxV2Client is used to interact with features provided by the convox.com group.
type ConvoxV2Client struct {
restClient rest.Interface
}
func (c *ConvoxV2Client) Atoms(namespace string) AtomInterface {
return newAtoms(c, namespace)
}
func (c *ConvoxV2Client) AtomVersions(namespace string) AtomVersionInterface {
return newAtomVersions(c, namespace)
}
// NewForConfig creates a new ConvoxV2Client for the given config.
func NewForConfig(c *rest.Config) (*ConvoxV2Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &ConvoxV2Client{client}, nil
}
// NewForConfigOrDie creates a new ConvoxV2Client for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *ConvoxV2Client {
client, err := NewForConfig(c)
if err != nil {
panic(err)
}
return client
}
// New creates a new ConvoxV2Client for the given RESTClient.
func New(c rest.Interface) *ConvoxV2Client {
return &ConvoxV2Client{c}
}
func setConfigDefaults(config *rest.Config) error {
gv := v2.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return nil
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *ConvoxV2Client) RESTClient() rest.Interface {
if c == nil {
return nil
}
return c.restClient
}

View File

@ -0,0 +1,20 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated typed clients.
package v2

View File

@ -0,0 +1,20 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// Package fake has the automatically generated clients.
package fake

View File

@ -0,0 +1,140 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v2 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeAtoms implements AtomInterface
type FakeAtoms struct {
Fake *FakeConvoxV2
ns string
}
var atomsResource = schema.GroupVersionResource{Group: "convox.com", Version: "v2", Resource: "atoms"}
var atomsKind = schema.GroupVersionKind{Group: "convox.com", Version: "v2", Kind: "Atom"}
// Get takes name of the atom, and returns the corresponding atom object, and an error if there is any.
func (c *FakeAtoms) Get(name string, options v1.GetOptions) (result *v2.Atom, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(atomsResource, c.ns, name), &v2.Atom{})
if obj == nil {
return nil, err
}
return obj.(*v2.Atom), err
}
// List takes label and field selectors, and returns the list of Atoms that match those selectors.
func (c *FakeAtoms) List(opts v1.ListOptions) (result *v2.AtomList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(atomsResource, atomsKind, c.ns, opts), &v2.AtomList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v2.AtomList{ListMeta: obj.(*v2.AtomList).ListMeta}
for _, item := range obj.(*v2.AtomList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested atoms.
func (c *FakeAtoms) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(atomsResource, c.ns, opts))
}
// Create takes the representation of a atom and creates it. Returns the server's representation of the atom, and an error, if there is any.
func (c *FakeAtoms) Create(atom *v2.Atom) (result *v2.Atom, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(atomsResource, c.ns, atom), &v2.Atom{})
if obj == nil {
return nil, err
}
return obj.(*v2.Atom), err
}
// Update takes the representation of a atom and updates it. Returns the server's representation of the atom, and an error, if there is any.
func (c *FakeAtoms) Update(atom *v2.Atom) (result *v2.Atom, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(atomsResource, c.ns, atom), &v2.Atom{})
if obj == nil {
return nil, err
}
return obj.(*v2.Atom), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeAtoms) UpdateStatus(atom *v2.Atom) (*v2.Atom, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(atomsResource, "status", c.ns, atom), &v2.Atom{})
if obj == nil {
return nil, err
}
return obj.(*v2.Atom), err
}
// Delete takes name of the atom and deletes it. Returns an error if one occurs.
func (c *FakeAtoms) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(atomsResource, c.ns, name), &v2.Atom{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeAtoms) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(atomsResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &v2.AtomList{})
return err
}
// Patch applies the patch and returns the patched atom.
func (c *FakeAtoms) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2.Atom, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(atomsResource, c.ns, name, data, subresources...), &v2.Atom{})
if obj == nil {
return nil, err
}
return obj.(*v2.Atom), err
}

View File

@ -0,0 +1,140 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v2 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeAtomVersions implements AtomVersionInterface
type FakeAtomVersions struct {
Fake *FakeConvoxV2
ns string
}
var atomversionsResource = schema.GroupVersionResource{Group: "convox.com", Version: "v2", Resource: "atomversions"}
var atomversionsKind = schema.GroupVersionKind{Group: "convox.com", Version: "v2", Kind: "AtomVersion"}
// Get takes name of the atomVersion, and returns the corresponding atomVersion object, and an error if there is any.
func (c *FakeAtomVersions) Get(name string, options v1.GetOptions) (result *v2.AtomVersion, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(atomversionsResource, c.ns, name), &v2.AtomVersion{})
if obj == nil {
return nil, err
}
return obj.(*v2.AtomVersion), err
}
// List takes label and field selectors, and returns the list of AtomVersions that match those selectors.
func (c *FakeAtomVersions) List(opts v1.ListOptions) (result *v2.AtomVersionList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(atomversionsResource, atomversionsKind, c.ns, opts), &v2.AtomVersionList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v2.AtomVersionList{ListMeta: obj.(*v2.AtomVersionList).ListMeta}
for _, item := range obj.(*v2.AtomVersionList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested atomVersions.
func (c *FakeAtomVersions) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(atomversionsResource, c.ns, opts))
}
// Create takes the representation of a atomVersion and creates it. Returns the server's representation of the atomVersion, and an error, if there is any.
func (c *FakeAtomVersions) Create(atomVersion *v2.AtomVersion) (result *v2.AtomVersion, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(atomversionsResource, c.ns, atomVersion), &v2.AtomVersion{})
if obj == nil {
return nil, err
}
return obj.(*v2.AtomVersion), err
}
// Update takes the representation of a atomVersion and updates it. Returns the server's representation of the atomVersion, and an error, if there is any.
func (c *FakeAtomVersions) Update(atomVersion *v2.AtomVersion) (result *v2.AtomVersion, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(atomversionsResource, c.ns, atomVersion), &v2.AtomVersion{})
if obj == nil {
return nil, err
}
return obj.(*v2.AtomVersion), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeAtomVersions) UpdateStatus(atomVersion *v2.AtomVersion) (*v2.AtomVersion, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(atomversionsResource, "status", c.ns, atomVersion), &v2.AtomVersion{})
if obj == nil {
return nil, err
}
return obj.(*v2.AtomVersion), err
}
// Delete takes name of the atomVersion and deletes it. Returns an error if one occurs.
func (c *FakeAtomVersions) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(atomversionsResource, c.ns, name), &v2.AtomVersion{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeAtomVersions) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(atomversionsResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &v2.AtomVersionList{})
return err
}
// Patch applies the patch and returns the patched atomVersion.
func (c *FakeAtomVersions) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2.AtomVersion, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(atomversionsResource, c.ns, name, data, subresources...), &v2.AtomVersion{})
if obj == nil {
return nil, err
}
return obj.(*v2.AtomVersion), err
}

View File

@ -0,0 +1,44 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v2 "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned/typed/convox/v2"
rest "k8s.io/client-go/rest"
testing "k8s.io/client-go/testing"
)
type FakeConvoxV2 struct {
*testing.Fake
}
func (c *FakeConvoxV2) Atoms(namespace string) v2.AtomInterface {
return &FakeAtoms{c, namespace}
}
func (c *FakeConvoxV2) AtomVersions(namespace string) v2.AtomVersionInterface {
return &FakeAtomVersions{c, namespace}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeConvoxV2) RESTClient() rest.Interface {
var ret *rest.RESTClient
return ret
}

View File

@ -0,0 +1,23 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v2
type AtomExpansion interface{}
type AtomVersionExpansion interface{}

View File

@ -0,0 +1,54 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package convox
import (
v1 "github.com/convox/convox/pkg/atom/pkg/client/informers/externalversions/convox/v1"
v2 "github.com/convox/convox/pkg/atom/pkg/client/informers/externalversions/convox/v2"
internalinterfaces "github.com/convox/convox/pkg/atom/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to each of this group's versions.
type Interface interface {
// V1 provides access to shared informers for resources in V1.
V1() v1.Interface
// V2 provides access to shared informers for resources in V2.
V2() v2.Interface
}
type group struct {
factory internalinterfaces.SharedInformerFactory
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// V1 returns a new v1.Interface.
func (g *group) V1() v1.Interface {
return v1.New(g.factory, g.namespace, g.tweakListOptions)
}
// V2 returns a new v2.Interface.
func (g *group) V2() v2.Interface {
return v2.New(g.factory, g.namespace, g.tweakListOptions)
}

View File

@ -0,0 +1,89 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1
import (
time "time"
convoxv1 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v1"
versioned "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned"
internalinterfaces "github.com/convox/convox/pkg/atom/pkg/client/informers/externalversions/internalinterfaces"
v1 "github.com/convox/convox/pkg/atom/pkg/client/listers/convox/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// AtomInformer provides access to a shared informer and lister for
// Atoms.
type AtomInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1.AtomLister
}
type atomInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewAtomInformer constructs a new informer for Atom type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewAtomInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredAtomInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredAtomInformer constructs a new informer for Atom type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredAtomInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.ConvoxV1().Atoms(namespace).List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.ConvoxV1().Atoms(namespace).Watch(options)
},
},
&convoxv1.Atom{},
resyncPeriod,
indexers,
)
}
func (f *atomInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredAtomInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *atomInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&convoxv1.Atom{}, f.defaultInformer)
}
func (f *atomInformer) Lister() v1.AtomLister {
return v1.NewAtomLister(f.Informer().GetIndexer())
}

View File

@ -0,0 +1,45 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1
import (
internalinterfaces "github.com/convox/convox/pkg/atom/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to all the informers in this group version.
type Interface interface {
// Atoms returns a AtomInformer.
Atoms() AtomInformer
}
type version struct {
factory internalinterfaces.SharedInformerFactory
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// Atoms returns a AtomInformer.
func (v *version) Atoms() AtomInformer {
return &atomInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}

View File

@ -0,0 +1,89 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v2
import (
time "time"
convoxv2 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
versioned "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned"
internalinterfaces "github.com/convox/convox/pkg/atom/pkg/client/informers/externalversions/internalinterfaces"
v2 "github.com/convox/convox/pkg/atom/pkg/client/listers/convox/v2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// AtomInformer provides access to a shared informer and lister for
// Atoms.
type AtomInformer interface {
Informer() cache.SharedIndexInformer
Lister() v2.AtomLister
}
type atomInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewAtomInformer constructs a new informer for Atom type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewAtomInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredAtomInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredAtomInformer constructs a new informer for Atom type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredAtomInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.ConvoxV2().Atoms(namespace).List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.ConvoxV2().Atoms(namespace).Watch(options)
},
},
&convoxv2.Atom{},
resyncPeriod,
indexers,
)
}
func (f *atomInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredAtomInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *atomInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&convoxv2.Atom{}, f.defaultInformer)
}
func (f *atomInformer) Lister() v2.AtomLister {
return v2.NewAtomLister(f.Informer().GetIndexer())
}

View File

@ -0,0 +1,89 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v2
import (
time "time"
convoxv2 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
versioned "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned"
internalinterfaces "github.com/convox/convox/pkg/atom/pkg/client/informers/externalversions/internalinterfaces"
v2 "github.com/convox/convox/pkg/atom/pkg/client/listers/convox/v2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// AtomVersionInformer provides access to a shared informer and lister for
// AtomVersions.
type AtomVersionInformer interface {
Informer() cache.SharedIndexInformer
Lister() v2.AtomVersionLister
}
type atomVersionInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewAtomVersionInformer constructs a new informer for AtomVersion type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewAtomVersionInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredAtomVersionInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredAtomVersionInformer constructs a new informer for AtomVersion type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredAtomVersionInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.ConvoxV2().AtomVersions(namespace).List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.ConvoxV2().AtomVersions(namespace).Watch(options)
},
},
&convoxv2.AtomVersion{},
resyncPeriod,
indexers,
)
}
func (f *atomVersionInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredAtomVersionInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *atomVersionInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&convoxv2.AtomVersion{}, f.defaultInformer)
}
func (f *atomVersionInformer) Lister() v2.AtomVersionLister {
return v2.NewAtomVersionLister(f.Informer().GetIndexer())
}

View File

@ -0,0 +1,52 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v2
import (
internalinterfaces "github.com/convox/convox/pkg/atom/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to all the informers in this group version.
type Interface interface {
// Atoms returns a AtomInformer.
Atoms() AtomInformer
// AtomVersions returns a AtomVersionInformer.
AtomVersions() AtomVersionInformer
}
type version struct {
factory internalinterfaces.SharedInformerFactory
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// Atoms returns a AtomInformer.
func (v *version) Atoms() AtomInformer {
return &atomInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// AtomVersions returns a AtomVersionInformer.
func (v *version) AtomVersions() AtomVersionInformer {
return &atomVersionInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}

View File

@ -0,0 +1,180 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package externalversions
import (
reflect "reflect"
sync "sync"
time "time"
versioned "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned"
convox "github.com/convox/convox/pkg/atom/pkg/client/informers/externalversions/convox"
internalinterfaces "github.com/convox/convox/pkg/atom/pkg/client/informers/externalversions/internalinterfaces"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
)
// SharedInformerOption defines the functional option type for SharedInformerFactory.
type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory
type sharedInformerFactory struct {
client versioned.Interface
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
lock sync.Mutex
defaultResync time.Duration
customResync map[reflect.Type]time.Duration
informers map[reflect.Type]cache.SharedIndexInformer
// startedInformers is used for tracking which informers have been started.
// This allows Start() to be called multiple times safely.
startedInformers map[reflect.Type]bool
}
// WithCustomResyncConfig sets a custom resync period for the specified informer types.
func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption {
return func(factory *sharedInformerFactory) *sharedInformerFactory {
for k, v := range resyncConfig {
factory.customResync[reflect.TypeOf(k)] = v
}
return factory
}
}
// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory.
func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption {
return func(factory *sharedInformerFactory) *sharedInformerFactory {
factory.tweakListOptions = tweakListOptions
return factory
}
}
// WithNamespace limits the SharedInformerFactory to the specified namespace.
func WithNamespace(namespace string) SharedInformerOption {
return func(factory *sharedInformerFactory) *sharedInformerFactory {
factory.namespace = namespace
return factory
}
}
// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces.
func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory {
return NewSharedInformerFactoryWithOptions(client, defaultResync)
}
// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory.
// Listers obtained via this SharedInformerFactory will be subject to the same filters
// as specified here.
// Deprecated: Please use NewSharedInformerFactoryWithOptions instead
func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory {
return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions))
}
// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options.
func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory {
factory := &sharedInformerFactory{
client: client,
namespace: v1.NamespaceAll,
defaultResync: defaultResync,
informers: make(map[reflect.Type]cache.SharedIndexInformer),
startedInformers: make(map[reflect.Type]bool),
customResync: make(map[reflect.Type]time.Duration),
}
// Apply all options
for _, opt := range options {
factory = opt(factory)
}
return factory
}
// Start initializes all requested informers.
func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
f.lock.Lock()
defer f.lock.Unlock()
for informerType, informer := range f.informers {
if !f.startedInformers[informerType] {
go informer.Run(stopCh)
f.startedInformers[informerType] = true
}
}
}
// WaitForCacheSync waits for all started informers' cache were synced.
func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {
informers := func() map[reflect.Type]cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
informers := map[reflect.Type]cache.SharedIndexInformer{}
for informerType, informer := range f.informers {
if f.startedInformers[informerType] {
informers[informerType] = informer
}
}
return informers
}()
res := map[reflect.Type]bool{}
for informType, informer := range informers {
res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)
}
return res
}
// InternalInformerFor returns the SharedIndexInformer for obj using an internal
// client.
func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
informerType := reflect.TypeOf(obj)
informer, exists := f.informers[informerType]
if exists {
return informer
}
resyncPeriod, exists := f.customResync[informerType]
if !exists {
resyncPeriod = f.defaultResync
}
informer = newFunc(f.client, resyncPeriod)
f.informers[informerType] = informer
return informer
}
// SharedInformerFactory provides shared informers for resources in all known
// API group versions.
type SharedInformerFactory interface {
internalinterfaces.SharedInformerFactory
ForResource(resource schema.GroupVersionResource) (GenericInformer, error)
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
Convox() convox.Interface
}
func (f *sharedInformerFactory) Convox() convox.Interface {
return convox.New(f, f.namespace, f.tweakListOptions)
}

View File

@ -0,0 +1,69 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package externalversions
import (
"fmt"
v1 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v1"
v2 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
)
// GenericInformer is type of SharedIndexInformer which will locate and delegate to other
// sharedInformers based on type
type GenericInformer interface {
Informer() cache.SharedIndexInformer
Lister() cache.GenericLister
}
type genericInformer struct {
informer cache.SharedIndexInformer
resource schema.GroupResource
}
// Informer returns the SharedIndexInformer.
func (f *genericInformer) Informer() cache.SharedIndexInformer {
return f.informer
}
// Lister returns the GenericLister.
func (f *genericInformer) Lister() cache.GenericLister {
return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource)
}
// ForResource gives generic access to a shared informer of the matching type
// TODO extend this to unknown resources with a client pool
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
switch resource {
// Group=convox.com, Version=v1
case v1.SchemeGroupVersion.WithResource("atoms"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Convox().V1().Atoms().Informer()}, nil
// Group=convox.com, Version=v2
case v2.SchemeGroupVersion.WithResource("atoms"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Convox().V2().Atoms().Informer()}, nil
case v2.SchemeGroupVersion.WithResource("atomversions"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Convox().V2().AtomVersions().Informer()}, nil
}
return nil, fmt.Errorf("no informer found for %v", resource)
}

View File

@ -0,0 +1,38 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package internalinterfaces
import (
time "time"
versioned "github.com/convox/convox/pkg/atom/pkg/client/clientset/versioned"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
cache "k8s.io/client-go/tools/cache"
)
type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer
// SharedInformerFactory a small interface to allow for adding an informer without an import cycle
type SharedInformerFactory interface {
Start(stopCh <-chan struct{})
InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
}
type TweakListOptionsFunc func(*v1.ListOptions)

View File

@ -0,0 +1,94 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1
import (
v1 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// AtomLister helps list Atoms.
type AtomLister interface {
// List lists all Atoms in the indexer.
List(selector labels.Selector) (ret []*v1.Atom, err error)
// Atoms returns an object that can list and get Atoms.
Atoms(namespace string) AtomNamespaceLister
AtomListerExpansion
}
// atomLister implements the AtomLister interface.
type atomLister struct {
indexer cache.Indexer
}
// NewAtomLister returns a new AtomLister.
func NewAtomLister(indexer cache.Indexer) AtomLister {
return &atomLister{indexer: indexer}
}
// List lists all Atoms in the indexer.
func (s *atomLister) List(selector labels.Selector) (ret []*v1.Atom, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1.Atom))
})
return ret, err
}
// Atoms returns an object that can list and get Atoms.
func (s *atomLister) Atoms(namespace string) AtomNamespaceLister {
return atomNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// AtomNamespaceLister helps list and get Atoms.
type AtomNamespaceLister interface {
// List lists all Atoms in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1.Atom, err error)
// Get retrieves the Atom from the indexer for a given namespace and name.
Get(name string) (*v1.Atom, error)
AtomNamespaceListerExpansion
}
// atomNamespaceLister implements the AtomNamespaceLister
// interface.
type atomNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all Atoms in the indexer for a given namespace.
func (s atomNamespaceLister) List(selector labels.Selector) (ret []*v1.Atom, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1.Atom))
})
return ret, err
}
// Get retrieves the Atom from the indexer for a given namespace and name.
func (s atomNamespaceLister) Get(name string) (*v1.Atom, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1.Resource("atom"), name)
}
return obj.(*v1.Atom), nil
}

View File

@ -0,0 +1,27 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1
// AtomListerExpansion allows custom methods to be added to
// AtomLister.
type AtomListerExpansion interface{}
// AtomNamespaceListerExpansion allows custom methods to be added to
// AtomNamespaceLister.
type AtomNamespaceListerExpansion interface{}

View File

@ -0,0 +1,94 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v2
import (
v2 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// AtomLister helps list Atoms.
type AtomLister interface {
// List lists all Atoms in the indexer.
List(selector labels.Selector) (ret []*v2.Atom, err error)
// Atoms returns an object that can list and get Atoms.
Atoms(namespace string) AtomNamespaceLister
AtomListerExpansion
}
// atomLister implements the AtomLister interface.
type atomLister struct {
indexer cache.Indexer
}
// NewAtomLister returns a new AtomLister.
func NewAtomLister(indexer cache.Indexer) AtomLister {
return &atomLister{indexer: indexer}
}
// List lists all Atoms in the indexer.
func (s *atomLister) List(selector labels.Selector) (ret []*v2.Atom, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v2.Atom))
})
return ret, err
}
// Atoms returns an object that can list and get Atoms.
func (s *atomLister) Atoms(namespace string) AtomNamespaceLister {
return atomNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// AtomNamespaceLister helps list and get Atoms.
type AtomNamespaceLister interface {
// List lists all Atoms in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v2.Atom, err error)
// Get retrieves the Atom from the indexer for a given namespace and name.
Get(name string) (*v2.Atom, error)
AtomNamespaceListerExpansion
}
// atomNamespaceLister implements the AtomNamespaceLister
// interface.
type atomNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all Atoms in the indexer for a given namespace.
func (s atomNamespaceLister) List(selector labels.Selector) (ret []*v2.Atom, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v2.Atom))
})
return ret, err
}
// Get retrieves the Atom from the indexer for a given namespace and name.
func (s atomNamespaceLister) Get(name string) (*v2.Atom, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v2.Resource("atom"), name)
}
return obj.(*v2.Atom), nil
}

View File

@ -0,0 +1,94 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v2
import (
v2 "github.com/convox/convox/pkg/atom/pkg/apis/convox/v2"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// AtomVersionLister helps list AtomVersions.
type AtomVersionLister interface {
// List lists all AtomVersions in the indexer.
List(selector labels.Selector) (ret []*v2.AtomVersion, err error)
// AtomVersions returns an object that can list and get AtomVersions.
AtomVersions(namespace string) AtomVersionNamespaceLister
AtomVersionListerExpansion
}
// atomVersionLister implements the AtomVersionLister interface.
type atomVersionLister struct {
indexer cache.Indexer
}
// NewAtomVersionLister returns a new AtomVersionLister.
func NewAtomVersionLister(indexer cache.Indexer) AtomVersionLister {
return &atomVersionLister{indexer: indexer}
}
// List lists all AtomVersions in the indexer.
func (s *atomVersionLister) List(selector labels.Selector) (ret []*v2.AtomVersion, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v2.AtomVersion))
})
return ret, err
}
// AtomVersions returns an object that can list and get AtomVersions.
func (s *atomVersionLister) AtomVersions(namespace string) AtomVersionNamespaceLister {
return atomVersionNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// AtomVersionNamespaceLister helps list and get AtomVersions.
type AtomVersionNamespaceLister interface {
// List lists all AtomVersions in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v2.AtomVersion, err error)
// Get retrieves the AtomVersion from the indexer for a given namespace and name.
Get(name string) (*v2.AtomVersion, error)
AtomVersionNamespaceListerExpansion
}
// atomVersionNamespaceLister implements the AtomVersionNamespaceLister
// interface.
type atomVersionNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all AtomVersions in the indexer for a given namespace.
func (s atomVersionNamespaceLister) List(selector labels.Selector) (ret []*v2.AtomVersion, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v2.AtomVersion))
})
return ret, err
}
// Get retrieves the AtomVersion from the indexer for a given namespace and name.
func (s atomVersionNamespaceLister) Get(name string) (*v2.AtomVersion, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v2.Resource("atomversion"), name)
}
return obj.(*v2.AtomVersion), nil
}

View File

@ -0,0 +1,35 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v2
// AtomListerExpansion allows custom methods to be added to
// AtomLister.
type AtomListerExpansion interface{}
// AtomNamespaceListerExpansion allows custom methods to be added to
// AtomNamespaceLister.
type AtomNamespaceListerExpansion interface{}
// AtomVersionListerExpansion allows custom methods to be added to
// AtomVersionLister.
type AtomVersionListerExpansion interface{}
// AtomVersionNamespaceListerExpansion allows custom methods to be added to
// AtomVersionNamespaceLister.
type AtomVersionNamespaceListerExpansion interface{}

View File

@ -0,0 +1,9 @@
apiVersion: convox.com/v2
kind: Atom
metadata:
namespace: {{.Namespace}}
name: {{.Name}}
spec:
currentVersion: {{.Name}}-{{.Version}}
progressDeadlineSeconds: {{.Timeout}}
status: Pending

View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: {{.Namespace}}

View File

@ -0,0 +1,8 @@
apiVersion: convox.com/v2
kind: AtomVersion
metadata:
namespace: {{.Namespace}}
name: {{.Name}}-{{.Version}}
spec:
release: {{ safe .Release }}
template: {{ safe .Template }}

90
pkg/cli/apps.go Normal file
View File

@ -0,0 +1,90 @@
package cli
import (
"fmt"
"strings"
"github.com/convox/convox/pkg/common"
"github.com/convox/convox/pkg/structs"
"github.com/convox/stdcli"
)
func init() {
register("apps", "list apps", Apps, stdcli.CommandOptions{
Flags: []stdcli.Flag{flagRack},
Validate: stdcli.Args(0),
})
register("apps create", "create an app", AppsCreate, stdcli.CommandOptions{
Flags: append(stdcli.OptionFlags(structs.AppCreateOptions{}), flagRack, flagWait),
Usage: "[name]",
Validate: stdcli.ArgsMax(1),
})
register("apps delete", "delete an app", AppsDelete, stdcli.CommandOptions{
Flags: []stdcli.Flag{flagRack, flagWait},
Usage: "<app>",
Validate: stdcli.Args(1),
})
}
func Apps(p structs.Provider, c *stdcli.Context) error {
as, err := p.AppList()
if err != nil {
return err
}
t := c.Table("APP", "STATUS", "RELEASE")
for _, a := range as {
t.AddRow(a.Name, a.Status, a.Release)
}
return t.Print()
}
func AppsCreate(p structs.Provider, c *stdcli.Context) error {
app := common.CoalesceString(c.Arg(0), app(c))
if strings.TrimSpace(app) == "" {
return fmt.Errorf("must specify an app name")
}
var opts structs.AppCreateOptions
if err := c.Options(&opts); err != nil {
return err
}
c.Startf("Creating <app>%s</app>", app)
if _, err := p.AppCreate(app, opts); err != nil {
return err
}
if c.Bool("wait") {
if err := common.WaitForAppRunning(p, app); err != nil {
return err
}
}
return c.OK()
}
func AppsDelete(p structs.Provider, c *stdcli.Context) error {
app := c.Args[0]
c.Startf("Deleting <app>%s</app>", app)
if err := p.AppDelete(app); err != nil {
return err
}
if c.Bool("wait") {
if err := common.WaitForAppDeleted(p, c, app); err != nil {
return err
}
}
return c.OK()
}

46
pkg/cli/cli.go Normal file
View File

@ -0,0 +1,46 @@
package cli
import (
"fmt"
"os"
"github.com/convox/stdcli"
)
var (
flagApp = stdcli.StringFlag("app", "a", "app name")
flagId = stdcli.BoolFlag("id", "", "put logs on stderr, release id on stdout")
flagNoFollow = stdcli.BoolFlag("no-follow", "", "do not follow logs")
flagRack = stdcli.StringFlag("rack", "r", "rack name")
flagWait = stdcli.BoolFlag("wait", "w", "wait for completion")
)
func New(name, version string) *Engine {
e := &Engine{
Engine: stdcli.New(name, version),
}
e.Writer.Tags["app"] = stdcli.RenderColors(39)
e.Writer.Tags["command"] = stdcli.RenderColors(244)
e.Writer.Tags["dir"] = stdcli.RenderColors(246)
e.Writer.Tags["build"] = stdcli.RenderColors(23)
e.Writer.Tags["fail"] = stdcli.RenderColors(160)
e.Writer.Tags["rack"] = stdcli.RenderColors(26)
e.Writer.Tags["process"] = stdcli.RenderColors(27)
e.Writer.Tags["release"] = stdcli.RenderColors(24)
e.Writer.Tags["service"] = stdcli.RenderColors(33)
e.Writer.Tags["setting"] = stdcli.RenderColors(246)
e.Writer.Tags["system"] = stdcli.RenderColors(15)
for i := 0; i < 18; i++ {
e.Writer.Tags[fmt.Sprintf("color%d", i)] = stdcli.RenderColors(237 + i)
}
if dir := os.Getenv("CONVOX_CONFIG"); dir != "" {
e.Settings = dir
}
e.RegisterCommands()
return e
}

88
pkg/cli/engine.go Normal file
View File

@ -0,0 +1,88 @@
package cli
import (
"fmt"
"strings"
"github.com/convox/convox/pkg/structs"
"github.com/convox/convox/provider/k8s"
"github.com/convox/stdcli"
)
type Engine struct {
*stdcli.Engine
Provider structs.Provider
}
type HandlerFunc func(structs.Provider, *stdcli.Context) error
func (e *Engine) Command(command, description string, fn HandlerFunc, opts stdcli.CommandOptions) {
wfn := func(c *stdcli.Context) error {
p, err := e.currentProvider(c)
if err != nil {
return err
}
return fn(p, c)
}
e.Engine.Command(command, description, wfn, opts)
}
func (e *Engine) CommandWithoutProvider(command, description string, fn HandlerFunc, opts stdcli.CommandOptions) {
wfn := func(c *stdcli.Context) error {
return fn(nil, c)
}
e.Engine.Command(command, description, wfn, opts)
}
func (e *Engine) RegisterCommands() {
for _, c := range commands {
if c.Rack {
e.Command(c.Command, c.Description, c.Handler, c.Opts)
} else {
e.CommandWithoutProvider(c.Command, c.Description, c.Handler, c.Opts)
}
}
}
func (e *Engine) currentProvider(c *stdcli.Context) (structs.Provider, error) {
name := rack(c)
if strings.TrimSpace(name) == "" {
return nil, fmt.Errorf("must specify a rack name")
}
return k8s.New(name)
}
var commands = []command{}
type command struct {
Command string
Description string
Handler HandlerFunc
Opts stdcli.CommandOptions
Rack bool
}
func register(cmd, description string, fn HandlerFunc, opts stdcli.CommandOptions) {
commands = append(commands, command{
Command: cmd,
Description: description,
Handler: fn,
Opts: opts,
Rack: true,
})
}
func registerWithoutProvider(cmd, description string, fn HandlerFunc, opts stdcli.CommandOptions) {
commands = append(commands, command{
Command: cmd,
Description: description,
Handler: fn,
Opts: opts,
Rack: false,
})
}

22
pkg/cli/helpers.go Normal file
View File

@ -0,0 +1,22 @@
package cli
import (
"os"
"path/filepath"
"github.com/convox/convox/pkg/common"
"github.com/convox/stdcli"
)
func app(c *stdcli.Context) string {
wd, err := os.Getwd()
if err != nil {
panic(err)
}
return common.CoalesceString(c.String("app"), filepath.Base(wd))
}
func rack(c *stdcli.Context) string {
return common.CoalesceString(c.String("rack"), os.Getenv("CONVOX_RACK"))
}

14
pkg/common/atom.go Normal file
View File

@ -0,0 +1,14 @@
package common
func AtomStatus(status string) string {
switch status {
case "Failed":
return "failed"
case "Rollback":
return "rollback"
case "Deadline", "Error", "Pending", "Running":
return "updating"
default:
return "running"
}
}

104
pkg/common/certificate.go Normal file
View File

@ -0,0 +1,104 @@
package common
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"time"
)
func CertificateCA(host string, ca *tls.Certificate) (*tls.Certificate, error) {
rkey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return nil, err
}
cpub, err := x509.ParseCertificate(ca.Certificate[0])
if err != nil {
return nil, err
}
template := x509.Certificate{
SerialNumber: serial,
Subject: pkix.Name{
CommonName: host,
Organization: []string{"convox"},
},
Issuer: cpub.Subject,
NotBefore: time.Now(),
NotAfter: time.Now().Add(365 * 24 * time.Hour),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
DNSNames: []string{host, fmt.Sprintf("*.%s", host)},
}
data, err := x509.CreateCertificate(rand.Reader, &template, cpub, &rkey.PublicKey, ca.PrivateKey)
if err != nil {
return nil, err
}
pub := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: data})
key := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rkey)})
cert, err := tls.X509KeyPair(pub, key)
if err != nil {
return nil, err
}
return &cert, nil
}
func CertificateSelfSigned(host string) (*tls.Certificate, error) {
rkey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return nil, err
}
template := x509.Certificate{
SerialNumber: serial,
Subject: pkix.Name{
CommonName: host,
Organization: []string{"convox"},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(365 * 24 * time.Hour),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
DNSNames: []string{host},
}
data, err := x509.CreateCertificate(rand.Reader, &template, &template, &rkey.PublicKey, rkey)
if err != nil {
return nil, err
}
pub := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: data})
key := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rkey)})
cert, err := tls.X509KeyPair(pub, key)
if err != nil {
return nil, err
}
return &cert, nil
}

20
pkg/common/coalesce.go Normal file
View File

@ -0,0 +1,20 @@
package common
func CoalesceInt(ii ...int) int {
for _, i := range ii {
if i != 0 {
return i
}
}
return 0
}
func CoalesceString(ss ...string) string {
for _, s := range ss {
if s != "" {
return s
}
}
return ""
}

43
pkg/common/default.go Normal file
View File

@ -0,0 +1,43 @@
package common
import "time"
func DefaultBool(v *bool, def bool) bool {
if v == nil {
return def
}
return *v
}
func DefaultDuration(v *time.Duration, def time.Duration) time.Duration {
if v == nil {
return def
}
return *v
}
func DefaultInt(v *int, def int) int {
if v == nil {
return def
}
return *v
}
func DefaultInt32(v *int32, def int32) int32 {
if v == nil {
return def
}
return *v
}
func DefaultString(v *string, def string) string {
if v == nil {
return def
}
return *v
}

39
pkg/common/endpoint.go Normal file
View File

@ -0,0 +1,39 @@
package common
import (
"crypto/tls"
"fmt"
"net/http"
"time"
)
func EndpointCheck(url string) error {
ht := NewDefaultTransport()
ht.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
hc := &http.Client{Timeout: 2 * time.Second, Transport: ht}
res, err := hc.Get(fmt.Sprintf("%s/apps", url))
if err == nil && res.StatusCode == 200 {
return nil
}
return fmt.Errorf("check failed")
}
func EndpointWait(url string) error {
tick := time.NewTicker(2 * time.Second)
defer tick.Stop()
timeout := time.After(5 * time.Minute)
for {
select {
case <-tick.C:
if err := EndpointCheck(url); err == nil {
return nil
}
case <-timeout:
return fmt.Errorf("timeout")
}
}
}

27
pkg/common/file.go Normal file
View File

@ -0,0 +1,27 @@
package common
import (
"io/ioutil"
"os"
"path/filepath"
)
func FileExists(filename string) bool {
if _, err := os.Stat(filename); os.IsNotExist(err) {
return false
}
return true
}
func WriteFile(filename string, data []byte, mode os.FileMode) error {
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
return err
}
if err := ioutil.WriteFile(filename, data, mode); err != nil {
return err
}
return nil
}

34
pkg/common/format.go Normal file
View File

@ -0,0 +1,34 @@
package common
import (
"bytes"
"regexp"
yaml "gopkg.in/yaml.v2"
)
var (
yamlSplitter = regexp.MustCompile(`(?m)^\s*---\s*$`)
)
func FormatYAML(data []byte) ([]byte, error) {
ps := yamlSplitter.Split(string(data), -1)
bs := make([][]byte, len(ps))
for i, p := range ps {
var v interface{}
if err := yaml.Unmarshal([]byte(p), &v); err != nil {
return nil, err
}
data, err := yaml.Marshal(v)
if err != nil {
return nil, err
}
bs[i] = data
}
return bytes.Join(bs, []byte("---\n")), nil
}

1
pkg/common/helpers.go Normal file
View File

@ -0,0 +1 @@
package common

38
pkg/common/http.go Normal file
View File

@ -0,0 +1,38 @@
package common
import (
"io/ioutil"
"net"
"net/http"
"time"
)
func Get(url string) ([]byte, error) {
res, err := http.Get(url)
if err != nil {
return nil, err
}
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
return data, nil
}
func NewDefaultTransport() *http.Transport {
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
}

39
pkg/common/linux.go Normal file
View File

@ -0,0 +1,39 @@
package common
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"regexp"
)
var reLinuxAttributes = regexp.MustCompile(`\s*(\w+)\s*=\s*"?([^"]*)`)
func LinuxRelease() (string, error) {
attrs, err := linuxReleaseAttributes()
if err != nil {
return "", err
}
return fmt.Sprintf("%s-%s", attrs["ID"], attrs["VERSION_ID"]), nil
}
func linuxReleaseAttributes() (map[string]string, error) {
attrs := map[string]string{}
data, err := ioutil.ReadFile("/etc/os-release")
if err != nil {
return nil, err
}
s := bufio.NewScanner(bytes.NewReader(data))
for s.Scan() {
if p := reLinuxAttributes.FindStringSubmatch(s.Text()); len(p) == 3 {
attrs[p[1]] = p[2]
}
}
return attrs, nil
}

7
pkg/common/number.go Normal file
View File

@ -0,0 +1,7 @@
package common
import "fmt"
func Percent(num float64) string {
return fmt.Sprintf("%0.2f%%", num*100)
}

238
pkg/common/provider.go Normal file
View File

@ -0,0 +1,238 @@
package common
import (
"bufio"
"context"
"fmt"
"io"
"strings"
"time"
"github.com/convox/convox/pkg/manifest"
"github.com/convox/convox/pkg/options"
"github.com/convox/convox/pkg/structs"
"github.com/pkg/errors"
)
var (
ProviderWaitDuration = 5 * time.Second
)
func AppEnvironment(p structs.Provider, app string) (structs.Environment, error) {
rs, err := ReleaseLatest(p, app)
if err != nil {
return nil, err
}
if rs == nil {
return structs.Environment{}, nil
}
env := structs.Environment{}
if err := env.Load([]byte(rs.Env)); err != nil {
return nil, err
}
return env, nil
}
func AppManifest(p structs.Provider, app string) (*manifest.Manifest, *structs.Release, error) {
a, err := p.AppGet(app)
if err != nil {
return nil, nil, err
}
if a.Release == "" {
return nil, nil, errors.WithStack(fmt.Errorf("no release for app: %s", app))
}
return ReleaseManifest(p, app, a.Release)
}
func ReleaseLatest(p structs.Provider, app string) (*structs.Release, error) {
rs, err := p.ReleaseList(app, structs.ReleaseListOptions{Limit: options.Int(1)})
if err != nil {
return nil, err
}
if len(rs) < 1 {
return nil, nil
}
return p.ReleaseGet(app, rs[0].Id)
}
func ReleaseManifest(p structs.Provider, app, release string) (*manifest.Manifest, *structs.Release, error) {
r, err := p.ReleaseGet(app, release)
if err != nil {
return nil, nil, err
}
env := structs.Environment{}
if err := env.Load([]byte(r.Env)); err != nil {
return nil, nil, err
}
m, err := manifest.Load([]byte(r.Manifest), env)
if err != nil {
return nil, nil, err
}
return m, r, nil
}
func StreamAppLogs(ctx context.Context, p structs.Provider, w io.Writer, app string) {
for {
select {
case <-ctx.Done():
return
default:
}
r, err := p.AppLogs(app, structs.LogsOptions{Prefix: options.Bool(true), Since: options.Duration(5 * time.Second)})
if err != nil {
return
}
copySystemLogs(ctx, w, r)
time.Sleep(1 * time.Second)
}
}
func StreamSystemLogs(ctx context.Context, p structs.Provider, w io.Writer) {
for {
select {
case <-ctx.Done():
return
default:
}
r, err := p.SystemLogs(structs.LogsOptions{Prefix: options.Bool(true), Since: options.Duration(5 * time.Second)})
if err != nil {
return
}
copySystemLogs(ctx, w, r)
time.Sleep(1 * time.Second)
}
}
func WaitForAppDeleted(p structs.Provider, w io.Writer, app string) error {
time.Sleep(ProviderWaitDuration) // give the stack time to start updating
return Wait(ProviderWaitDuration, 35*time.Minute, 2, func() (bool, error) {
_, err := p.AppGet(app)
if err == nil {
return false, nil
}
if strings.Contains(err.Error(), "no such app") {
return true, nil
}
if strings.Contains(err.Error(), "app not found") {
return true, nil
}
return false, err
})
}
func WaitForAppRunning(p structs.Provider, app string) error {
return WaitForAppRunningContext(context.Background(), p, app)
}
func WaitForAppRunningContext(ctx context.Context, p structs.Provider, app string) error {
time.Sleep(ProviderWaitDuration) // give the stack time to start updating
var waitError error
return WaitContext(ctx, ProviderWaitDuration, 35*time.Minute, 2, func() (bool, error) {
a, err := p.AppGet(app)
if err != nil {
return false, err
}
if a.Status == "rollback" {
waitError = fmt.Errorf("rollback")
}
return a.Status == "running", waitError
})
}
func WaitForAppWithLogs(p structs.Provider, w io.Writer, app string) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
return WaitForAppWithLogsContext(ctx, p, w, app)
}
func WaitForAppWithLogsContext(ctx context.Context, p structs.Provider, w io.Writer, app string) error {
go StreamAppLogs(ctx, p, w, app)
if err := WaitForAppRunningContext(ctx, p, app); err != nil {
return err
}
return nil
}
func WaitForProcessRunning(p structs.Provider, w io.Writer, app, pid string) error {
return Wait(1*time.Second, 5*time.Minute, 2, func() (bool, error) {
ps, err := p.ProcessGet(app, pid)
if err != nil {
return false, err
}
return ps.Status == "running", nil
})
}
func WaitForRackRunning(p structs.Provider, w io.Writer) error {
time.Sleep(ProviderWaitDuration) // give the stack time to start updating
return Wait(ProviderWaitDuration, 35*time.Minute, 2, func() (bool, error) {
s, err := p.SystemGet()
if err != nil {
return false, err
}
return s.Status == "running", nil
})
}
func WaitForRackWithLogs(p structs.Provider, w io.Writer) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go StreamSystemLogs(ctx, p, w)
if err := WaitForRackRunning(p, w); err != nil {
return err
}
return nil
}
func copySystemLogs(ctx context.Context, w io.Writer, r io.Reader) {
s := bufio.NewScanner(r)
for s.Scan() {
select {
case <-ctx.Done():
return
default:
}
parts := strings.SplitN(s.Text(), " ", 3)
if len(parts) < 3 {
continue
}
if strings.HasPrefix(parts[1], "system/") {
w.Write([]byte(fmt.Sprintf("%s\n", s.Text())))
}
}
}

42
pkg/common/random.go Normal file
View File

@ -0,0 +1,42 @@
package common
import (
"crypto/sha256"
"fmt"
"math/rand"
"time"
)
var (
alphabet = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
)
func init() {
rand.Seed(time.Now().UnixNano())
}
func Id(prefix string, length int) string {
id := prefix
for i := 0; i < length-len(prefix); i++ {
id += string(alphabet[rand.Intn(len(alphabet))])
}
return id
}
func RandomString(length int) (string, error) {
data := make([]byte, 1024)
if _, err := rand.Read(data); err != nil {
return "", err
}
key := fmt.Sprintf("%x", sha256.Sum256(data))
if len(key) < length {
return "", fmt.Errorf("key too long")
}
return key[0:length], nil
}

26
pkg/common/retry.go Normal file
View File

@ -0,0 +1,26 @@
package common
import (
"math/rand"
"time"
)
func Retry(times int, interval time.Duration, fn func() error) error {
i := 0
for {
err := fn()
if err == nil {
return nil
}
// add 20% jitter
time.Sleep(interval + time.Duration(rand.Intn(int(interval/20))))
i++
if i > times {
return err
}
}
}

61
pkg/common/stream.go Normal file
View File

@ -0,0 +1,61 @@
package common
import (
"io"
"net/http"
)
type ReadWriter struct {
io.Reader
io.Writer
}
func Pipe(a, b io.ReadWriter) error {
ch := make(chan error)
go halfPipe(a, b, ch)
go halfPipe(b, a, ch)
if err := <-ch; err != nil {
return err
}
if err := <-ch; err != nil {
return err
}
return nil
}
func halfPipe(w io.Writer, r io.Reader, ch chan error) {
ch <- Stream(w, r)
if c, ok := w.(io.Closer); ok {
c.Close()
}
}
func Stream(w io.Writer, r io.Reader) error {
buf := make([]byte, 1024)
for {
n, err := r.Read(buf)
if n > 0 {
if _, err := w.Write(buf[0:n]); err != nil {
if err == io.ErrClosedPipe {
return nil
}
return err
}
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}
if err == io.EOF {
return nil
}
if err != nil {
return err
}
}
}

148
pkg/common/tar.go Normal file
View File

@ -0,0 +1,148 @@
package common
import (
"archive/tar"
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/docker/docker/builder/dockerignore"
"github.com/docker/docker/pkg/archive"
)
func Archive(file string) (io.Reader, error) {
opts := &archive.TarOptions{
IncludeFiles: []string{file},
}
r, err := archive.TarWithOptions("/", opts)
if err != nil {
return nil, err
}
return r, nil
}
func RebaseArchive(r io.Reader, src, dst string) (io.Reader, error) {
tr := tar.NewReader(r)
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
for {
h, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if !strings.HasPrefix(h.Name, "/") {
h.Name = fmt.Sprintf("/%s", h.Name)
}
if !strings.HasPrefix(h.Name, src) {
continue
}
h.Name = filepath.Join(dst, strings.TrimPrefix(h.Name, src))
tw.WriteHeader(h)
if _, err := io.Copy(tw, tr); err != nil {
return nil, err
}
}
tw.Close()
return &buf, nil
}
func Tarball(dir string) ([]byte, error) {
abs, err := filepath.Abs(dir)
if err != nil {
return nil, err
}
sym, err := filepath.EvalSymlinks(abs)
if err != nil {
return nil, err
}
data, err := ioutil.ReadFile(filepath.Join(sym, ".dockerignore"))
if err != nil && !os.IsNotExist(err) {
return nil, err
}
excludes, err := dockerignore.ReadAll(bytes.NewReader(data))
if err != nil {
return nil, err
}
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
defer os.Chdir(cwd)
if err := os.Chdir(sym); err != nil {
return nil, err
}
opts := &archive.TarOptions{
Compression: archive.Gzip,
ExcludePatterns: excludes,
IncludeFiles: []string{"."},
}
r, err := archive.TarWithOptions(sym, opts)
if err != nil {
return nil, err
}
return ioutil.ReadAll(r)
}
func Unarchive(r io.Reader, target string) error {
tr := tar.NewReader(r)
for {
h, err := tr.Next()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
file := filepath.Join(target, h.Name)
switch h.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(file, os.FileMode(h.Mode)); err != nil {
return err
}
case tar.TypeReg:
if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
return err
}
fd, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY, os.FileMode(h.Mode))
if err != nil {
return err
}
if _, err := io.Copy(fd, tr); err != nil {
return err
}
}
}
}

10
pkg/common/test.go Normal file
View File

@ -0,0 +1,10 @@
package common
import (
"fmt"
"io/ioutil"
)
func Testdata(name string) ([]byte, error) {
return ioutil.ReadFile(fmt.Sprintf("testdata/%s.yml", name))
}

13
pkg/common/tick.go Normal file
View File

@ -0,0 +1,13 @@
package common
import (
"time"
)
func Tick(d time.Duration, fn func()) {
fn()
for range time.Tick(d) {
fn()
}
}

41
pkg/common/time.go Normal file
View File

@ -0,0 +1,41 @@
package common
import (
"fmt"
"time"
humanize "github.com/dustin/go-humanize"
)
var (
CompactSortableTime = "20060102150405000000000"
PrintableTime = "2006-01-02 15:04:05"
SortableTime = "20060102.150405.000000000"
)
func Ago(t time.Time) string {
if t.IsZero() {
return ""
}
return humanize.Time(t)
}
func Duration(start, end time.Time) string {
d := end.Sub(start)
if end.IsZero() {
return ""
}
total := int64(d.Seconds())
sec := total % 60
min := total / 60
dur := fmt.Sprintf("%ds", sec)
if min >= 1 {
dur = fmt.Sprintf("%dm", min) + dur
}
return dur
}

52
pkg/common/wait.go Normal file
View File

@ -0,0 +1,52 @@
package common
import (
"context"
"fmt"
"time"
)
func Wait(interval time.Duration, timeout time.Duration, times int, fn func() (bool, error)) error {
return WaitContext(context.Background(), interval, timeout, times, fn)
}
func WaitContext(ctx context.Context, interval time.Duration, timeout time.Duration, times int, fn func() (bool, error)) error {
successes := 0
errors := 0
start := time.Now().UTC()
tick := time.NewTicker(interval)
defer tick.Stop()
for {
select {
case <-ctx.Done():
return nil
case <-tick.C:
if start.Add(timeout).Before(time.Now().UTC()) {
return fmt.Errorf("timeout")
}
success, err := fn()
if err != nil {
errors += 1
} else {
errors = 0
}
if errors >= times {
return err
}
if success {
successes += 1
} else {
successes = 0
}
if successes >= times {
return nil
}
}
}
}

137
pkg/kctl/kctl.go Normal file
View File

@ -0,0 +1,137 @@
package kctl
import (
"fmt"
"os"
"time"
ac "k8s.io/api/core/v1"
am "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
tc "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/leaderelection"
"k8s.io/client-go/tools/leaderelection/resourcelock"
"k8s.io/client-go/tools/record"
)
type Controller struct {
Handler ControllerHandler
Identifier string
Name string
Namespace string
errch chan error
recorder record.EventRecorder
}
type ControllerHandler interface {
Add(interface{}) error
Client() kubernetes.Interface
Delete(interface{}) error
Start() error
Stop() error
Update(interface{}, interface{}) error
}
func NewController(namespace, name string, handler ControllerHandler) (*Controller, error) {
hostname, err := os.Hostname()
if err != nil {
return nil, err
}
c := &Controller{
Handler: handler,
Identifier: hostname,
Name: name,
Namespace: namespace,
}
return c, nil
}
func (c *Controller) Event(object runtime.Object, eventtype, reason, message string) {
c.recorder.Event(object, eventtype, reason, message)
}
func (c *Controller) Run(informer cache.SharedInformer, ch chan error) {
c.errch = ch
eb := record.NewBroadcaster()
eb.StartRecordingToSink(&tc.EventSinkImpl{Interface: c.Handler.Client().CoreV1().Events("")})
c.recorder = eb.NewRecorder(scheme.Scheme, ac.EventSource{Component: c.Name})
rl := &resourcelock.ConfigMapLock{
ConfigMapMeta: am.ObjectMeta{Namespace: c.Namespace, Name: c.Name},
Client: c.Handler.Client().CoreV1(),
LockConfig: resourcelock.ResourceLockConfig{
Identity: c.Identifier,
EventRecorder: c.recorder,
},
}
el, err := leaderelection.NewLeaderElector(leaderelection.LeaderElectionConfig{
Lock: rl,
LeaseDuration: 15 * time.Second,
RenewDeadline: 10 * time.Second,
RetryPeriod: 2 * time.Second,
Callbacks: leaderelection.LeaderCallbacks{
OnStartedLeading: c.leaderStart(informer),
},
})
if err != nil {
ch <- err
return
}
go el.Run()
}
func (c *Controller) leaderStart(informer cache.SharedInformer) func(<-chan struct{}) {
return func(stop <-chan struct{}) {
fmt.Printf("started leading: %s/%s (%s)\n", c.Namespace, c.Name, c.Identifier)
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: c.addHandler,
DeleteFunc: c.deleteHandler,
UpdateFunc: c.updateHandler,
})
if err := c.Handler.Start(); err != nil {
c.errch <- err
}
go informer.Run(stop)
fmt.Printf("stopped leading: %s/%s (%s)\n", c.Namespace, c.Name, c.Identifier)
if err := c.Handler.Stop(); err != nil {
c.errch <- err
}
}
}
func (c *Controller) addHandler(obj interface{}) {
if err := c.Handler.Add(obj); err != nil {
c.errch <- err
}
}
func (c *Controller) deleteHandler(obj interface{}) {
if err := c.Handler.Delete(obj); err != nil {
c.errch <- err
}
}
func (c *Controller) updateHandler(prev, cur interface{}) {
// if reflect.DeepEqual(prev, cur) {
// return
// }
if err := c.Handler.Update(prev, cur); err != nil {
c.errch <- err
}
}

View File

@ -0,0 +1,3 @@
package manifest
type Environment []string

36
pkg/manifest/helpers.go Normal file
View File

@ -0,0 +1,36 @@
package manifest
import (
"math/rand"
"regexp"
)
func coalesce(ss ...string) string {
for _, s := range ss {
if s != "" {
return s
}
}
return ""
}
var regexpInterpolation = regexp.MustCompile(`\$\{([^}]*?)\}`)
func interpolate(data []byte, env map[string]string) ([]byte, error) {
p := regexpInterpolation.ReplaceAllFunc(data, func(m []byte) []byte {
return []byte(env[string(m)[2:len(m)-1]])
})
return p, nil
}
var randomAlphabet = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
func randomString(size int) string {
b := make([]rune, size)
for i := range b {
b[i] = randomAlphabet[rand.Intn(len(randomAlphabet))]
}
return string(b)
}

288
pkg/manifest/manifest.go Normal file
View File

@ -0,0 +1,288 @@
package manifest
import (
"fmt"
"io"
"math/rand"
"sort"
"strings"
"time"
yaml "gopkg.in/yaml.v2"
)
var (
DefaultCpu = 256
DefaultMem = 512
)
type Manifest struct {
Environment Environment `yaml:"environment,omitempty"`
Params Params `yaml:"params,omitempty"`
Resources Resources `yaml:"resources,omitempty"`
Services Services `yaml:"services,omitempty"`
Timers Timers `yaml:"timers,omitempty"`
attributes map[string]bool
env map[string]string
}
func init() {
rand.Seed(time.Now().UnixNano())
}
func Load(data []byte, env map[string]string) (*Manifest, error) {
var m Manifest
p, err := interpolate(data, env)
if err != nil {
return nil, err
}
if err := yaml.Unmarshal(p, &m); err != nil {
return nil, err
}
m.attributes, err = yamlAttributes(p)
if err != nil {
return nil, err
}
m.env = map[string]string{}
for k, v := range env {
m.env[k] = v
}
if err := m.ApplyDefaults(); err != nil {
return nil, err
}
if err := m.CombineEnv(); err != nil {
return nil, err
}
if err := m.ValidateEnv(); err != nil {
return nil, err
}
return &m, nil
}
func (m *Manifest) Agents() []string {
a := []string{}
for _, s := range m.Services {
if s.Agent.Enabled {
a = append(a, s.Name)
}
}
return a
}
func (m *Manifest) Attributes() []string {
attrs := []string{}
for k := range m.attributes {
attrs = append(attrs, k)
}
sort.Strings(attrs)
return attrs
}
func (m *Manifest) AttributesByPrefix(prefix string) []string {
attrs := []string{}
for _, a := range m.Attributes() {
if strings.HasPrefix(a, prefix) {
attrs = append(attrs, a)
}
}
return attrs
}
func (m *Manifest) AttributeSet(name string) bool {
return m.attributes[name]
}
func (m *Manifest) Env() map[string]string {
return m.env
}
// used only for tests
func (m *Manifest) SetAttributes(attrs []string) {
m.attributes = map[string]bool{}
for _, a := range attrs {
m.attributes[a] = true
}
}
// used only for tests
func (m *Manifest) SetEnv(env map[string]string) {
m.env = env
}
func (m *Manifest) CombineEnv() error {
for i, s := range m.Services {
me := make([]string, len(m.Environment))
copy(me, m.Environment)
m.Services[i].Environment = append(me, s.Environment...)
}
return nil
}
func (m *Manifest) Service(name string) (*Service, error) {
for _, s := range m.Services {
if s.Name == name {
return &s, nil
}
}
return nil, fmt.Errorf("no such service: %s", name)
}
func (m *Manifest) ServiceEnvironment(service string) (map[string]string, error) {
s, err := m.Service(service)
if err != nil {
return nil, err
}
env := map[string]string{}
missing := []string{}
for _, e := range s.Environment {
parts := strings.SplitN(e, "=", 2)
switch len(parts) {
case 1:
if parts[0] == "*" {
for k, v := range m.env {
env[k] = v
}
} else {
v, ok := m.env[parts[0]]
if !ok {
missing = append(missing, parts[0])
}
env[parts[0]] = v
}
case 2:
v, ok := m.env[parts[0]]
if ok {
env[parts[0]] = v
} else {
env[parts[0]] = parts[1]
}
default:
return nil, fmt.Errorf("invalid environment declaration: %s", e)
}
}
if len(missing) > 0 {
sort.Strings(missing)
return nil, fmt.Errorf("required env: %s", strings.Join(missing, ", "))
}
return env, nil
}
// ValidateEnv returns an error if required env vars for a service are not available
// It also filters m.env to the union of all service env vars defined in the manifest
func (m *Manifest) ValidateEnv() error {
keys := map[string]bool{}
for _, s := range m.Services {
env, err := m.ServiceEnvironment(s.Name)
if err != nil {
return err
}
for k := range env {
keys[k] = true
}
}
for k := range m.env {
if !keys[k] {
delete(m.env, k)
}
}
return nil
}
func (m *Manifest) ApplyDefaults() error {
for i, s := range m.Services {
if s.Build.Path == "" && s.Image == "" {
m.Services[i].Build.Path = "."
}
if m.Services[i].Build.Path != "" && s.Build.Manifest == "" {
m.Services[i].Build.Manifest = "Dockerfile"
}
if s.Drain == 0 {
m.Services[i].Drain = 30
}
if s.Health.Path == "" {
m.Services[i].Health.Path = "/"
}
if s.Health.Interval == 0 {
m.Services[i].Health.Interval = 5
}
if s.Health.Grace == 0 {
m.Services[i].Health.Grace = m.Services[i].Health.Interval
}
if s.Health.Timeout == 0 {
m.Services[i].Health.Timeout = m.Services[i].Health.Interval - 1
}
if s.Port.Port > 0 && s.Port.Scheme == "" {
m.Services[i].Port.Scheme = "http"
}
sp := fmt.Sprintf("services.%s.scale", s.Name)
// if no scale attributes set
if len(m.AttributesByPrefix(sp)) == 0 {
m.Services[i].Scale.Count = ServiceScaleCount{Min: 1, Max: 1}
}
// if no explicit count attribute set yet has multiple scale attributes other than count
if !m.AttributeSet(fmt.Sprintf("%s.count", sp)) && len(m.AttributesByPrefix(sp)) > 1 {
m.Services[i].Scale.Count = ServiceScaleCount{Min: 1, Max: 1}
}
if m.Services[i].Scale.Cpu == 0 {
m.Services[i].Scale.Cpu = DefaultCpu
}
if m.Services[i].Scale.Memory == 0 {
m.Services[i].Scale.Memory = DefaultMem
}
if !m.AttributeSet(fmt.Sprintf("services.%s.sticky", s.Name)) {
m.Services[i].Sticky = true
}
}
return nil
}
func message(w io.Writer, format string, args ...interface{}) {
if w != nil {
w.Write([]byte(fmt.Sprintf(format, args...) + "\n"))
}
}

View File

@ -0,0 +1,404 @@
package manifest_test
import (
"testing"
"github.com/convox/convox/pkg/common"
"github.com/convox/convox/pkg/manifest"
"github.com/stretchr/testify/require"
)
func TestManifestLoad(t *testing.T) {
n := &manifest.Manifest{
Environment: manifest.Environment{
"DEVELOPMENT=true",
"GLOBAL=true",
"OTHERGLOBAL",
},
Params: manifest.Params{
"Foo": "bar",
},
Resources: manifest.Resources{
manifest.Resource{
Name: "database",
Type: "postgres",
Options: map[string]string{
"size": "db.t2.large",
},
},
},
Services: manifest.Services{
manifest.Service{
Name: "api",
Build: manifest.ServiceBuild{
Manifest: "Dockerfile2",
Path: "api",
},
Command: "",
Domains: []string{"foo.example.org"},
Drain: 30,
Environment: []string{
"DEFAULT=test",
"DEVELOPMENT=false",
"SECRET",
},
Health: manifest.ServiceHealth{
Grace: 10,
Path: "/",
Interval: 10,
Timeout: 9,
},
Init: true,
Port: manifest.ServicePort{Port: 1000, Scheme: "http"},
Resources: []string{"database"},
Scale: manifest.ServiceScale{
Count: manifest.ServiceScaleCount{Min: 3, Max: 10},
Cpu: 256,
Memory: 512,
},
Sticky: true,
Test: "make test",
},
manifest.Service{
Name: "proxy",
Command: "bash",
Domains: []string{"bar.example.org", "*.example.org"},
Drain: 30,
Health: manifest.ServiceHealth{
Grace: 5,
Path: "/auth",
Interval: 5,
Timeout: 4,
},
Image: "ubuntu:16.04",
Environment: []string{
"SECRET",
},
Port: manifest.ServicePort{Port: 2000, Scheme: "https"},
Scale: manifest.ServiceScale{
Count: manifest.ServiceScaleCount{Min: 1, Max: 1},
Cpu: 512,
Memory: 1024,
},
Sticky: true,
},
manifest.Service{
Name: "foo",
Build: manifest.ServiceBuild{
Manifest: "Dockerfile",
Path: ".",
},
Command: "foo",
Domains: []string{"baz.example.org", "qux.example.org"},
Drain: 60,
Health: manifest.ServiceHealth{
Grace: 2,
Interval: 5,
Path: "/",
Timeout: 3,
},
Port: manifest.ServicePort{Port: 3000, Scheme: "https"},
Scale: manifest.ServiceScale{
Count: manifest.ServiceScaleCount{Min: 0, Max: 0},
Cpu: 256,
Memory: 512,
},
Singleton: true,
Sticky: false,
},
manifest.Service{
Name: "bar",
Build: manifest.ServiceBuild{
Manifest: "Dockerfile",
Path: ".",
},
Command: "",
Drain: 30,
Health: manifest.ServiceHealth{
Grace: 5,
Interval: 5,
Path: "/",
Timeout: 4,
},
Scale: manifest.ServiceScale{
Count: manifest.ServiceScaleCount{Min: 1, Max: 1},
Cpu: 256,
Memory: 512,
},
Sticky: true,
},
manifest.Service{
Name: "scaler",
Build: manifest.ServiceBuild{
Manifest: "Dockerfile",
Path: ".",
},
Command: "",
Drain: 30,
Health: manifest.ServiceHealth{
Grace: 5,
Interval: 5,
Path: "/",
Timeout: 4,
},
Scale: manifest.ServiceScale{
Count: manifest.ServiceScaleCount{Min: 1, Max: 5},
Cpu: 256,
Memory: 512,
Targets: manifest.ServiceScaleTargets{
Cpu: 50,
Memory: 75,
Requests: 200,
Custom: manifest.ServiceScaleMetrics{
{
Aggregate: "max",
Dimensions: map[string]string{"QueueName": "testqueue"},
Namespace: "AWS/SQS",
Name: "ApproximateNumberOfMessagesVisible",
Value: float64(200),
},
},
},
},
Sticky: true,
},
manifest.Service{
Name: "inherit",
Command: "inherit",
Domains: []string{"bar.example.org", "*.example.org"},
Drain: 30,
Health: manifest.ServiceHealth{
Grace: 5,
Path: "/auth",
Interval: 5,
Timeout: 4,
},
Image: "ubuntu:16.04",
Environment: []string{
"SECRET",
},
Port: manifest.ServicePort{Port: 2000, Scheme: "https"},
Scale: manifest.ServiceScale{
Count: manifest.ServiceScaleCount{Min: 1, Max: 1},
Cpu: 512,
Memory: 1024,
},
Sticky: true,
},
manifest.Service{
Name: "agent",
Agent: manifest.ServiceAgent{
Enabled: true,
Ports: []manifest.ServiceAgentPort{
{Port: 5000, Protocol: "udp"},
{Port: 5001, Protocol: "tcp"},
{Port: 5002, Protocol: "tcp"},
},
},
Build: manifest.ServiceBuild{
Manifest: "Dockerfile",
Path: ".",
},
Drain: 30,
Health: manifest.ServiceHealth{
Grace: 5,
Path: "/",
Interval: 5,
Timeout: 4,
},
Scale: manifest.ServiceScale{
Count: manifest.ServiceScaleCount{Min: 1, Max: 1},
Cpu: 256,
Memory: 512,
},
Sticky: true,
},
},
}
attrs := []string{"services.proxy.environment",
"environment",
"params",
"params.Foo",
"resources",
"resources.database",
"resources.database.options",
"resources.database.options.size",
"resources.database.type",
"services",
"services.agent",
"services.agent.agent",
"services.agent.agent.ports",
"services.api",
"services.api.build",
"services.api.build.manifest",
"services.api.build.path",
"services.api.domain",
"services.api.environment",
"services.api.health",
"services.api.health.interval",
"services.api.init",
"services.api.port",
"services.api.resources",
"services.api.scale",
"services.api.test",
"services.bar",
"services.foo",
"services.foo.command",
"services.foo.domain",
"services.foo.drain",
"services.foo.health",
"services.foo.health.grace",
"services.foo.health.timeout",
"services.foo.port",
"services.foo.port.port",
"services.foo.port.scheme",
"services.foo.scale",
"services.foo.singleton",
"services.foo.sticky",
"services.inherit",
"services.inherit.command",
"services.inherit.domain",
"services.inherit.environment",
"services.inherit.health",
"services.inherit.image",
"services.inherit.port",
"services.inherit.scale",
"services.inherit.scale.cpu",
"services.inherit.scale.memory",
"services.proxy",
"services.proxy.command",
"services.proxy.domain",
"services.proxy.health",
"services.proxy.image",
"services.proxy.port",
"services.proxy.scale",
"services.proxy.scale.cpu",
"services.proxy.scale.memory",
"services.scaler",
"services.scaler.scale",
"services.scaler.scale.count",
"services.scaler.scale.targets",
"services.scaler.scale.targets.cpu",
"services.scaler.scale.targets.custom",
"services.scaler.scale.targets.custom.AWS/SQS/ApproximateNumberOfMessagesVisible",
"services.scaler.scale.targets.custom.AWS/SQS/ApproximateNumberOfMessagesVisible.aggregate",
"services.scaler.scale.targets.custom.AWS/SQS/ApproximateNumberOfMessagesVisible.dimensions",
"services.scaler.scale.targets.custom.AWS/SQS/ApproximateNumberOfMessagesVisible.dimensions.QueueName",
"services.scaler.scale.targets.custom.AWS/SQS/ApproximateNumberOfMessagesVisible.value",
"services.scaler.scale.targets.memory",
"services.scaler.scale.targets.requests",
}
env := map[string]string{"FOO": "bar", "SECRET": "shh", "OTHERGLOBAL": "test"}
n.SetAttributes(attrs)
n.SetEnv(env)
// env processing that normally happens as part of load
require.NoError(t, n.CombineEnv())
require.NoError(t, n.ValidateEnv())
m, err := testdataManifest("full", env)
require.NoError(t, err)
require.Equal(t, n, m)
senv, err := m.ServiceEnvironment("api")
require.NoError(t, err)
require.Equal(t, map[string]string{"DEFAULT": "test", "DEVELOPMENT": "false", "GLOBAL": "true", "OTHERGLOBAL": "test", "SECRET": "shh"}, senv)
s1, err := m.Service("api")
require.NoError(t, err)
require.Equal(t, map[string]string{"DEFAULT": "test", "DEVELOPMENT": "false", "GLOBAL": "true"}, s1.EnvironmentDefaults())
require.Equal(t, "DEFAULT,DEVELOPMENT,GLOBAL,OTHERGLOBAL,SECRET", s1.EnvironmentKeys())
s2, err := m.Service("proxy")
require.NoError(t, err)
require.Equal(t, map[string]string{"DEVELOPMENT": "true", "GLOBAL": "true"}, s2.EnvironmentDefaults())
require.Equal(t, "DEVELOPMENT,GLOBAL,OTHERGLOBAL,SECRET", s2.EnvironmentKeys())
}
func TestManifestLoadSimple(t *testing.T) {
_, err := testdataManifest("simple", map[string]string{})
require.EqualError(t, err, "required env: REQUIRED")
n := &manifest.Manifest{
Services: manifest.Services{
manifest.Service{
Name: "web",
Build: manifest.ServiceBuild{
Manifest: "Dockerfile",
Path: ".",
},
Drain: 30,
Environment: manifest.Environment{
"REQUIRED",
"DEFAULT=true",
},
Health: manifest.ServiceHealth{
Grace: 5,
Interval: 5,
Path: "/",
Timeout: 4,
},
Scale: manifest.ServiceScale{
Count: manifest.ServiceScaleCount{Min: 1, Max: 1},
Cpu: 256,
Memory: 512,
},
Sticky: true,
},
},
}
n.SetAttributes([]string{"services", "services.web", "services.web.build", "services.web.environment"})
n.SetEnv(map[string]string{"REQUIRED": "test"})
// env processing that normally happens as part of load
require.NoError(t, n.CombineEnv())
require.NoError(t, n.ValidateEnv())
m, err := testdataManifest("simple", map[string]string{"REQUIRED": "test"})
require.NoError(t, err)
require.Equal(t, n, m)
}
func TestManifestLoadClobberEnv(t *testing.T) {
env := map[string]string{"FOO": "bar", "REQUIRED": "false"}
_, err := testdataManifest("simple", env)
require.NoError(t, err)
require.Equal(t, map[string]string{"FOO": "bar", "REQUIRED": "false"}, env)
}
func TestManifestLoadInvalid(t *testing.T) {
m, err := testdataManifest("full", map[string]string{})
require.Nil(t, m)
require.Error(t, err, "required env: OTHERGLOBAL, SECRET")
m, err = testdataManifest("invalid.1", map[string]string{})
require.Nil(t, m)
require.Error(t, err, "yaml: line 2: did not find expected comment or line break")
m, err = testdataManifest("invalid.2", map[string]string{})
require.NotNil(t, m)
require.Len(t, m.Services, 0)
}
func TestManifestEnvManipulation(t *testing.T) {
m, err := testdataManifest("env", map[string]string{})
require.NotNil(t, m)
require.NoError(t, err)
require.Equal(t, "train-intent", m.Services[0].EnvironmentDefaults()["QUEUE_NAME"])
require.Equal(t, "delete-intent", m.Services[1].EnvironmentDefaults()["QUEUE_NAME"])
}
func testdataManifest(name string, env map[string]string) (*manifest.Manifest, error) {
data, err := common.Testdata(name)
if err != nil {
return nil, err
}
return manifest.Load(data, env)
}

3
pkg/manifest/params.go Normal file
View File

@ -0,0 +1,3 @@
package manifest
type Params map[string]string

13
pkg/manifest/resource.go Normal file
View File

@ -0,0 +1,13 @@
package manifest
type Resource struct {
Name string `yaml:"-"`
Type string `yaml:"type"`
Options map[string]string `yaml:"options"`
}
type Resources []Resource
func (r Resource) GetName() string {
return r.Name
}

161
pkg/manifest/service.go Normal file
View File

@ -0,0 +1,161 @@
package manifest
import (
"crypto/sha1"
"fmt"
"sort"
"strings"
)
type Service struct {
Name string `yaml:"-"`
Agent ServiceAgent `yaml:"agent,omitempty"`
Build ServiceBuild `yaml:"build,omitempty"`
Command string `yaml:"command,omitempty"`
Domains ServiceDomains `yaml:"domain,omitempty"`
Drain int `yaml:"drain,omitempty"`
Environment Environment `yaml:"environment,omitempty"`
Health ServiceHealth `yaml:"health,omitempty"`
Image string `yaml:"image,omitempty"`
Init bool `yaml:"init,omitempty"`
Internal bool `yaml:"internal,omitempty"`
Links []string `yaml:"links,omitempty"`
Port ServicePort `yaml:"port,omitempty"`
Privileged bool `yaml:"privileged,omitempty"`
Resources []string `yaml:"resources,omitempty"`
Scale ServiceScale `yaml:"scale,omitempty"`
Singleton bool `yaml:"singleton,omitempty"`
Sticky bool `yaml:"sticky,omitempty"`
Test string `yaml:"test,omitempty"`
Volumes []string `yaml:"volumes,omitempty"`
}
type Services []Service
type ServiceAgent struct {
Enabled bool `yaml:"enabled,omitempty"`
Ports []ServiceAgentPort `yaml:"ports,omitempty"`
}
type ServiceAgentPort struct {
Port int `yaml:"port,omitempty"`
Protocol string `yaml:"protocol,omitempty"`
}
type ServiceBuild struct {
Args []string `yaml:"args,omitempty"`
Manifest string `yaml:"manifest,omitempty"`
Path string `yaml:"path,omitempty"`
}
type ServiceDomains []string
type ServiceHealth struct {
Grace int
Interval int
Path string
Timeout int
}
type ServicePort struct {
Port int `yaml:"port,omitempty"`
Scheme string `yaml:"scheme,omitempty"`
}
type ServiceScale struct {
Count ServiceScaleCount
Cpu int
Memory int
Targets ServiceScaleTargets `yaml:"targets,omitempty"`
}
type ServiceScaleCount struct {
Min int
Max int
}
type ServiceScaleMetric struct {
Aggregate string
Dimensions map[string]string
Namespace string
Name string
Value float64
}
type ServiceScaleMetrics []ServiceScaleMetric
type ServiceScaleTargets struct {
Cpu int
Custom ServiceScaleMetrics
Memory int
Requests int
}
func (s Service) BuildHash(key string) string {
return fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprintf("key=%q build[path=%q, manifest=%q, args=%v] image=%q", key, s.Build.Path, s.Build.Manifest, s.Build.Args, s.Image))))
}
func (s Service) Domain() string {
if len(s.Domains) < 1 {
return ""
}
return s.Domains[0]
}
func (s Service) EnvironmentDefaults() map[string]string {
defaults := map[string]string{}
for _, e := range s.Environment {
switch parts := strings.Split(e, "="); len(parts) {
case 2:
defaults[parts[0]] = parts[1]
}
}
return defaults
}
func (s Service) EnvironmentKeys() string {
kh := map[string]bool{}
for _, e := range s.Environment {
kh[strings.Split(e, "=")[0]] = true
}
keys := []string{}
for k := range kh {
keys = append(keys, k)
}
sort.Strings(keys)
return strings.Join(keys, ",")
}
func (s Service) GetName() string {
return s.Name
}
func (s Service) Autoscale() bool {
if s.Agent.Enabled {
return false
}
switch {
case s.Scale.Count.Min == s.Scale.Count.Max:
return false
case s.Scale.Targets.Cpu > 0:
return true
case len(s.Scale.Targets.Custom) > 0:
return true
case s.Scale.Targets.Memory > 0:
return true
case s.Scale.Targets.Requests > 0:
return true
}
return false
}

3
pkg/manifest/testdata/api/Dockerfile vendored Normal file
View File

@ -0,0 +1,3 @@
FROM ubuntu:16.04
CMD bash

13
pkg/manifest/testdata/env.yml vendored Normal file
View File

@ -0,0 +1,13 @@
environment:
- NODE_ENV=${STAGE}
- SQS_AWS_ACCOUNT_ID=${AWS_ACCOUNT_ID}
- SQS_REGION=${AWS_REGION}
- SQS_ENDPOINT=https://sqs.${AWS_REGION}.amazonaws.com
- FOO=bar
services:
q-train-intent:
environment:
- QUEUE_NAME=train-intent
q-delete-intent:
environment:
- QUEUE_NAME=delete-intent

78
pkg/manifest/testdata/full.yml vendored Normal file
View File

@ -0,0 +1,78 @@
environment:
- DEVELOPMENT=true
- GLOBAL=true
- OTHERGLOBAL
params:
Foo: bar
resources:
database:
type: postgres
options:
size: db.t2.large
services:
api:
build:
manifest: Dockerfile2
path: api
domain: foo.example.org
environment:
- DEFAULT=test
- DEVELOPMENT=false
- SECRET
health:
interval: 10
init: true
resources:
- database
port: 1000
scale: 3-10
test: make ${BAR} test
proxy: &proxy
command: bash
domain:
- bar.example.org
- "*.example.org"
image: ubuntu:16.04
environment:
- SECRET
health: /auth
port: https:2000
scale:
cpu: 512
memory: 1024
foo:
command: foo
domain: baz.example.org, qux.example.org
drain: 60
health:
grace: 2
timeout: 3
port:
scheme: https
port: 3000
scale: 0
singleton: true
sticky: false
bar:
scaler:
scale:
count: 1-5
targets:
cpu: 50
memory: 75
requests: 200
custom:
AWS/SQS/ApproximateNumberOfMessagesVisible:
aggregate: max
value: 200
dimensions:
QueueName: testqueue
inherit:
"<<": *proxy
command: inherit
agent:
agent:
ports:
- 5000/udp
- 5001
- 5002/tcp

1
pkg/manifest/testdata/invalid.1.yml vendored Normal file
View File

@ -0,0 +1 @@
foo

3
pkg/manifest/testdata/invalid.2.yml vendored Normal file
View File

@ -0,0 +1,3 @@
servicess:
web:
build: .

6
pkg/manifest/testdata/simple.yml vendored Normal file
View File

@ -0,0 +1,6 @@
services:
web:
build: .
environment:
- REQUIRED
- DEFAULT=true

36
pkg/manifest/timer.go Normal file
View File

@ -0,0 +1,36 @@
package manifest
import (
"fmt"
"strings"
)
type Timer struct {
Name string `yaml:"-"`
Command string `yaml:"command"`
Schedule string `yaml:"schedule"`
Service string `yaml:"service"`
}
type Timers []Timer
func (t Timer) Cron() (string, error) {
switch len(strings.Split(t.Schedule, " ")) {
case 5:
return fmt.Sprintf("%s *", t.Schedule), nil
case 6:
return t.Schedule, nil
default:
return "", fmt.Errorf("invalid schedule expression: %s", t.Schedule)
}
}
func (t Timer) GetName() string {
return t.Name
}
func (t *Timer) SetName(name string) error {
t.Name = name
return nil
}

Some files were not shown because too many files have changed in this diff Show More