diff --git a/.gitignore b/.gitignore index 8327d97..9361ad8 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,5 @@ vendor/ out/ tmp/ -pkg/client/listers/ -pkg/client/informers/ +.vscode/ diff --git a/.travis.yml b/.travis.yml index d06dd43..834d073 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,20 +4,13 @@ sudo: required services: - docker go: -- 1.11.x +- 1.12.x go_import_path: github.com/inwinstack/ipam -before_install: -- go get -u github.com/golang/dep/cmd/dep +env: +- GO111MODULE=on script: -- make dep +- go mod download - make test - make after_success: -- bash <(curl -s https://codecov.io/bash) -- | - if [[ "$TRAVIS_BRANCH" =~ release-* ]]; then - # Log into Docker Hub. - docker login -u="$DOCKER_USER" -p="$DOCKER_PASSWORD" - make build_image - make push_image - fi \ No newline at end of file +- bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 40f9d94..13ba30a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,15 @@ -# Building stage -FROM inwinstack/golang:1.11-alpine AS build-env +FROM kairen/golang-dep:1.12-alpine AS build LABEL maintainer="Kyle Bai " ENV GOPATH "/go" ENV PROJECT_PATH "$GOPATH/src/github.com/inwinstack/ipam" +ENV GO111MODULE "on" COPY . $PROJECT_PATH RUN cd $PROJECT_PATH && \ - make dep && \ make && mv out/controller /tmp/controller # Running stage FROM alpine:3.7 -COPY --from=build-env /tmp/controller /bin/controller -ENTRYPOINT ["controller"] +COPY --from=build /tmp/controller /bin/controller +ENTRYPOINT ["controller"] \ No newline at end of file diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 645ab1f..0000000 --- a/Gopkg.lock +++ /dev/null @@ -1,529 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" - name = "github.com/davecgh/go-spew" - packages = ["spew"] - pruneopts = "UT" - revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" - version = "v1.1.1" - -[[projects]] - digest = "1:2cd7915ab26ede7d95b8749e6b1f933f1c6d5398030684e6505940a10f31cfda" - name = "github.com/ghodss/yaml" - packages = ["."] - pruneopts = "UT" - revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" - version = "v1.0.0" - -[[projects]] - digest = "1:b402bb9a24d108a9405a6f34675091b036c8b056aac843bf6ef2389a65c5cf48" - name = "github.com/gogo/protobuf" - packages = [ - "proto", - "sortkeys", - ] - pruneopts = "UT" - revision = "4cbf7e384e768b4e01799441fdf2a706a5635ae7" - version = "v1.2.0" - -[[projects]] - branch = "master" - digest = "1:1ba1d79f2810270045c328ae5d674321db34e3aae468eb4233883b473c5c0467" - name = "github.com/golang/glog" - packages = ["."] - pruneopts = "UT" - revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" - -[[projects]] - digest = "1:4c0989ca0bcd10799064318923b9bc2db6b4d6338dd75f3f2d86c3511aaaf5cf" - name = "github.com/golang/protobuf" - packages = [ - "proto", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/timestamp", - ] - pruneopts = "UT" - revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" - version = "v1.2.0" - -[[projects]] - branch = "master" - digest = "1:0bfbe13936953a98ae3cfe8ed6670d396ad81edf069a806d2f6515d7bb6950df" - name = "github.com/google/btree" - packages = ["."] - pruneopts = "UT" - revision = "4030bb1f1f0c35b30ca7009e9ebd06849dd45306" - -[[projects]] - branch = "master" - digest = "1:3ee90c0d94da31b442dde97c99635aaafec68d0b8a3c12ee2075c6bdabeec6bb" - name = "github.com/google/gofuzz" - packages = ["."] - pruneopts = "UT" - revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1" - -[[projects]] - digest = "1:65c4414eeb350c47b8de71110150d0ea8a281835b1f386eacaa3ad7325929c21" - name = "github.com/googleapis/gnostic" - packages = [ - "OpenAPIv2", - "compiler", - "extensions", - ] - pruneopts = "UT" - revision = "7c663266750e7d82587642f65e60bc4083f1f84e" - version = "v0.2.0" - -[[projects]] - branch = "master" - digest = "1:86c1210529e69d69860f2bb3ee9ccce0b595aa3f9165e7dd1388e5c612915888" - name = "github.com/gregjones/httpcache" - packages = [ - ".", - "diskcache", - ] - pruneopts = "UT" - revision = "c63ab54fda8f77302f8d414e19933f2b6026a089" - -[[projects]] - digest = "1:8ec8d88c248041a6df5f6574b87bc00e7e0b493881dad2e7ef47b11dc69093b5" - name = "github.com/hashicorp/golang-lru" - packages = [ - ".", - "simplelru", - ] - pruneopts = "UT" - revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768" - version = "v0.5.0" - -[[projects]] - digest = "1:8eb1de8112c9924d59bf1d3e5c26f5eaa2bfc2a5fcbb92dc1c2e4546d695f277" - name = "github.com/imdario/mergo" - packages = ["."] - pruneopts = "UT" - revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4" - version = "v0.3.6" - -[[projects]] - digest = "1:8ee586a23a1d8c39d8e0e31c92b222968ed1e5431c5328be358371f9b5e8fc65" - name = "github.com/inwinstack/blended" - packages = [ - "apis/inwinstack", - "apis/inwinstack/v1", - "client/clientset/versioned", - "client/clientset/versioned/fake", - "client/clientset/versioned/scheme", - "client/clientset/versioned/typed/inwinstack/v1", - "client/clientset/versioned/typed/inwinstack/v1/fake", - ] - pruneopts = "UT" - revision = "2fbd03d59d533599f216cba59ad57de37f34648b" - version = "v0.6.0" - -[[projects]] - branch = "v1.11.0" - digest = "1:aeeb73c8c978ae736982bfbc23e3104648471899012f2cfc9225187cc26798f3" - name = "github.com/inwinstack/operator-kit" - packages = ["."] - pruneopts = "UT" - revision = "183b801f95579b01eb29cd70276ddd15cd685fe1" - -[[projects]] - digest = "1:3e551bbb3a7c0ab2a2bf4660e7fcad16db089fdcfbb44b0199e62838038623ea" - name = "github.com/json-iterator/go" - packages = ["."] - pruneopts = "UT" - revision = "1624edc4454b8682399def8740d46db5e4362ba4" - version = "v1.1.5" - -[[projects]] - branch = "master" - digest = "1:1cf9298399ee987b39e824c68eae06c6d74ba646f4a8908646aa5af2b614c27c" - name = "github.com/mikioh/ipaddr" - packages = ["."] - pruneopts = "UT" - revision = "d9961065b564c8981f7f32d2dadba1db61818bef" - -[[projects]] - digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563" - name = "github.com/modern-go/concurrent" - packages = ["."] - pruneopts = "UT" - revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" - version = "1.0.3" - -[[projects]] - digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" - name = "github.com/modern-go/reflect2" - packages = ["."] - pruneopts = "UT" - revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" - version = "1.0.1" - -[[projects]] - branch = "master" - digest = "1:3bf17a6e6eaa6ad24152148a631d18662f7212e21637c2699bff3369b7f00fa2" - name = "github.com/petar/GoLLRB" - packages = ["llrb"] - pruneopts = "UT" - revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" - -[[projects]] - digest = "1:0e7775ebbcf00d8dd28ac663614af924411c868dca3d5aa762af0fae3808d852" - name = "github.com/peterbourgon/diskv" - packages = ["."] - pruneopts = "UT" - revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" - version = "v2.0.1" - -[[projects]] - digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - pruneopts = "UT" - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - -[[projects]] - digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2" - name = "github.com/spf13/pflag" - packages = ["."] - pruneopts = "UT" - revision = "298182f68c66c05229eb03ac171abe6e309ee79a" - version = "v1.0.3" - -[[projects]] - digest = "1:972c2427413d41a1e06ca4897e8528e5a1622894050e2f527b38ddf0f343f759" - name = "github.com/stretchr/testify" - packages = ["assert"] - pruneopts = "UT" - revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053" - version = "v1.3.0" - -[[projects]] - digest = "1:d1816f8f4877e87cf0a80a25fbc0e7c7a6937bd728ccbf85e2c151c792445a1b" - name = "github.com/thoas/go-funk" - packages = ["."] - pruneopts = "UT" - revision = "9132db0aefe2a09565f5124e267ac9a89845d8f6" - version = "0.4" - -[[projects]] - branch = "master" - digest = "1:38f553aff0273ad6f367cb0a0f8b6eecbaef8dc6cb8b50e57b6a81c1d5b1e332" - name = "golang.org/x/crypto" - packages = ["ssh/terminal"] - pruneopts = "UT" - revision = "ff983b9c42bc9fbf91556e191cc8efb585c16908" - -[[projects]] - branch = "master" - digest = "1:b6bbc489acd7bf1d4c6782138ffb75f46634007713795667ed7ad287fd80c070" - name = "golang.org/x/net" - packages = [ - "http/httpguts", - "http2", - "http2/hpack", - "idna", - ] - pruneopts = "UT" - revision = "915654e7eabcea33ae277abbecf52f0d8b7a9fdc" - -[[projects]] - branch = "master" - digest = "1:0150ad9329793acacd98bcbd88492d9e4731824b5d3fbc73a8a1b45eb21acf6b" - name = "golang.org/x/sys" - packages = [ - "unix", - "windows", - ] - pruneopts = "UT" - revision = "a457fd036447854c0c02e89ea439481bdcf941a2" - -[[projects]] - digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" - name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "language", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable", - ] - pruneopts = "UT" - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - branch = "master" - digest = "1:9fdc2b55e8e0fafe4b41884091e51e77344f7dc511c5acedcfd98200003bff90" - name = "golang.org/x/time" - packages = ["rate"] - pruneopts = "UT" - revision = "85acf8d2951cb2a3bde7632f9ff273ef0379bcbd" - -[[projects]] - digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a" - name = "gopkg.in/inf.v0" - packages = ["."] - pruneopts = "UT" - revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf" - version = "v0.9.1" - -[[projects]] - digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "UT" - revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" - version = "v2.2.2" - -[[projects]] - digest = "1:74142cd2275f77547c35ac51514108d9798a09aa0cf377a5c1084718ef7aa225" - name = "k8s.io/api" - packages = [ - "admissionregistration/v1alpha1", - "admissionregistration/v1beta1", - "apps/v1", - "apps/v1beta1", - "apps/v1beta2", - "authentication/v1", - "authentication/v1beta1", - "authorization/v1", - "authorization/v1beta1", - "autoscaling/v1", - "autoscaling/v2beta1", - "batch/v1", - "batch/v1beta1", - "batch/v2alpha1", - "certificates/v1beta1", - "core/v1", - "events/v1beta1", - "extensions/v1beta1", - "networking/v1", - "policy/v1beta1", - "rbac/v1", - "rbac/v1alpha1", - "rbac/v1beta1", - "scheduling/v1alpha1", - "scheduling/v1beta1", - "settings/v1alpha1", - "storage/v1", - "storage/v1alpha1", - "storage/v1beta1", - ] - pruneopts = "UT" - revision = "072894a440bdee3a891dea811fe42902311cd2a3" - version = "kubernetes-1.11.0" - -[[projects]] - digest = "1:828cfcb0e8dda021b784143c02cb78d42b4495dc51e07e67bbb0a238e9ec6650" - name = "k8s.io/apiextensions-apiserver" - packages = [ - "pkg/apis/apiextensions", - "pkg/apis/apiextensions/v1beta1", - "pkg/client/clientset/clientset", - "pkg/client/clientset/clientset/fake", - "pkg/client/clientset/clientset/scheme", - "pkg/client/clientset/clientset/typed/apiextensions/v1beta1", - "pkg/client/clientset/clientset/typed/apiextensions/v1beta1/fake", - ] - pruneopts = "UT" - revision = "3de98c57bc05a81cf463e0ad7a0af4cec8a5b510" - version = "kubernetes-1.11.0" - -[[projects]] - digest = "1:b7a7277a24d4ca275a5c8d45dcfd90847fbbfdcd6b0bf43f23777a628cd9515c" - name = "k8s.io/apimachinery" - packages = [ - "pkg/api/errors", - "pkg/api/meta", - "pkg/api/resource", - "pkg/apis/meta/internalversion", - "pkg/apis/meta/v1", - "pkg/apis/meta/v1/unstructured", - "pkg/apis/meta/v1beta1", - "pkg/conversion", - "pkg/conversion/queryparams", - "pkg/fields", - "pkg/labels", - "pkg/runtime", - "pkg/runtime/schema", - "pkg/runtime/serializer", - "pkg/runtime/serializer/json", - "pkg/runtime/serializer/protobuf", - "pkg/runtime/serializer/recognizer", - "pkg/runtime/serializer/streaming", - "pkg/runtime/serializer/versioning", - "pkg/selection", - "pkg/types", - "pkg/util/cache", - "pkg/util/clock", - "pkg/util/diff", - "pkg/util/errors", - "pkg/util/framer", - "pkg/util/intstr", - "pkg/util/json", - "pkg/util/mergepatch", - "pkg/util/net", - "pkg/util/runtime", - "pkg/util/sets", - "pkg/util/strategicpatch", - "pkg/util/validation", - "pkg/util/validation/field", - "pkg/util/wait", - "pkg/util/yaml", - "pkg/version", - "pkg/watch", - "third_party/forked/golang/json", - "third_party/forked/golang/reflect", - ] - pruneopts = "UT" - revision = "103fd098999dc9c0c88536f5c9ad2e5da39373ae" - version = "kubernetes-1.11.0" - -[[projects]] - digest = "1:6ec745815bcc9f656f039ffdb9dc2c85d16335fb7044615a5cddff09dded6a11" - name = "k8s.io/client-go" - packages = [ - "discovery", - "discovery/fake", - "kubernetes", - "kubernetes/fake", - "kubernetes/scheme", - "kubernetes/typed/admissionregistration/v1alpha1", - "kubernetes/typed/admissionregistration/v1alpha1/fake", - "kubernetes/typed/admissionregistration/v1beta1", - "kubernetes/typed/admissionregistration/v1beta1/fake", - "kubernetes/typed/apps/v1", - "kubernetes/typed/apps/v1/fake", - "kubernetes/typed/apps/v1beta1", - "kubernetes/typed/apps/v1beta1/fake", - "kubernetes/typed/apps/v1beta2", - "kubernetes/typed/apps/v1beta2/fake", - "kubernetes/typed/authentication/v1", - "kubernetes/typed/authentication/v1/fake", - "kubernetes/typed/authentication/v1beta1", - "kubernetes/typed/authentication/v1beta1/fake", - "kubernetes/typed/authorization/v1", - "kubernetes/typed/authorization/v1/fake", - "kubernetes/typed/authorization/v1beta1", - "kubernetes/typed/authorization/v1beta1/fake", - "kubernetes/typed/autoscaling/v1", - "kubernetes/typed/autoscaling/v1/fake", - "kubernetes/typed/autoscaling/v2beta1", - "kubernetes/typed/autoscaling/v2beta1/fake", - "kubernetes/typed/batch/v1", - "kubernetes/typed/batch/v1/fake", - "kubernetes/typed/batch/v1beta1", - "kubernetes/typed/batch/v1beta1/fake", - "kubernetes/typed/batch/v2alpha1", - "kubernetes/typed/batch/v2alpha1/fake", - "kubernetes/typed/certificates/v1beta1", - "kubernetes/typed/certificates/v1beta1/fake", - "kubernetes/typed/core/v1", - "kubernetes/typed/core/v1/fake", - "kubernetes/typed/events/v1beta1", - "kubernetes/typed/events/v1beta1/fake", - "kubernetes/typed/extensions/v1beta1", - "kubernetes/typed/extensions/v1beta1/fake", - "kubernetes/typed/networking/v1", - "kubernetes/typed/networking/v1/fake", - "kubernetes/typed/policy/v1beta1", - "kubernetes/typed/policy/v1beta1/fake", - "kubernetes/typed/rbac/v1", - "kubernetes/typed/rbac/v1/fake", - "kubernetes/typed/rbac/v1alpha1", - "kubernetes/typed/rbac/v1alpha1/fake", - "kubernetes/typed/rbac/v1beta1", - "kubernetes/typed/rbac/v1beta1/fake", - "kubernetes/typed/scheduling/v1alpha1", - "kubernetes/typed/scheduling/v1alpha1/fake", - "kubernetes/typed/scheduling/v1beta1", - "kubernetes/typed/scheduling/v1beta1/fake", - "kubernetes/typed/settings/v1alpha1", - "kubernetes/typed/settings/v1alpha1/fake", - "kubernetes/typed/storage/v1", - "kubernetes/typed/storage/v1/fake", - "kubernetes/typed/storage/v1alpha1", - "kubernetes/typed/storage/v1alpha1/fake", - "kubernetes/typed/storage/v1beta1", - "kubernetes/typed/storage/v1beta1/fake", - "pkg/apis/clientauthentication", - "pkg/apis/clientauthentication/v1alpha1", - "pkg/apis/clientauthentication/v1beta1", - "pkg/version", - "plugin/pkg/client/auth/exec", - "rest", - "rest/watch", - "testing", - "tools/auth", - "tools/cache", - "tools/clientcmd", - "tools/clientcmd/api", - "tools/clientcmd/api/latest", - "tools/clientcmd/api/v1", - "tools/metrics", - "tools/pager", - "tools/reference", - "transport", - "util/buffer", - "util/cert", - "util/connrotation", - "util/flowcontrol", - "util/homedir", - "util/integer", - "util/retry", - ] - pruneopts = "UT" - revision = "7d04d0e2a0a1a4d4a1cd6baa432a2301492e4e65" - version = "v8.0.0" - -[[projects]] - branch = "master" - digest = "1:03a96603922fc1f6895ae083e1e16d943b55ef0656b56965351bd87e7d90485f" - name = "k8s.io/kube-openapi" - packages = ["pkg/util/proto"] - pruneopts = "UT" - revision = "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/golang/glog", - "github.com/inwinstack/blended/apis/inwinstack/v1", - "github.com/inwinstack/blended/client/clientset/versioned", - "github.com/inwinstack/blended/client/clientset/versioned/fake", - "github.com/inwinstack/operator-kit", - "github.com/mikioh/ipaddr", - "github.com/spf13/pflag", - "github.com/stretchr/testify/assert", - "github.com/thoas/go-funk", - "k8s.io/api/core/v1", - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1", - "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset", - "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake", - "k8s.io/apimachinery/pkg/api/errors", - "k8s.io/apimachinery/pkg/apis/meta/v1", - "k8s.io/client-go/kubernetes", - "k8s.io/client-go/kubernetes/fake", - "k8s.io/client-go/rest", - "k8s.io/client-go/tools/cache", - "k8s.io/client-go/tools/clientcmd", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index a12c34a..0000000 --- a/Gopkg.toml +++ /dev/null @@ -1,32 +0,0 @@ - -[[constraint]] - name = "k8s.io/api" - version = "kubernetes-1.11.0" - -[[constraint]] - name = "k8s.io/apiextensions-apiserver" - version = "kubernetes-1.11.0" - -[[constraint]] - name = "k8s.io/apimachinery" - version = "kubernetes-1.11.0" - -[[constraint]] - name = "k8s.io/client-go" - version = "v8.0.0" - -[[constraint]] - name = "github.com/stretchr/testify" - version = "1.3.0" - -[[constraint]] - name = "github.com/inwinstack/operator-kit" - branch = "v1.11.0" - -[[constraint]] - name = "github.com/inwinstack/blended" - version = "v0.6.0" - -[prune] - go-tests = true - unused-packages = true diff --git a/LICENSE b/LICENSE index 3161911..329404c 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018 inwinSTACK.inc + Copyright 2018 inwinSTACK Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Makefile b/Makefile index 0cf86cb..52b481f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION_MAJOR ?= 0 -VERSION_MINOR ?= 6 -VERSION_BUILD ?= 1 +VERSION_MINOR ?= 7 +VERSION_BUILD ?= 0 VERSION ?= v$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_BUILD) GOOS ?= $(shell go env GOOS) @@ -20,10 +20,6 @@ out/controller: -ldflags="-s -w -X $(REPOPATH)/pkg/version.version=$(VERSION)" \ -a -o $@ cmd/main.go -.PHONY: dep -dep: - dep ensure - .PHONY: test test: ./hack/test-go.sh diff --git a/README.md b/README.md index 5d928a2..038269d 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ Clone repo into your go path under `$GOPATH/src`: ```sh $ git clone https://github.com/inwinstack/ipam.git $GOPATH/src/github.com/inwinstack/ipam $ cd $GOPATH/src/github.com/inwinstack/ipam -$ make dep $ make ``` diff --git a/cmd/main.go b/cmd/main.go index b45512b..6edebce 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,5 +1,5 @@ /* -Copyright © 2018 inwinSTACK.inc +Copyright © 2018 inwinSTACK Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,28 +17,54 @@ limitations under the License. package main import ( + "context" goflag "flag" "fmt" "os" + "os/signal" + "syscall" "github.com/golang/glog" + blended "github.com/inwinstack/blended/generated/clientset/versioned" + "github.com/inwinstack/ipam/pkg/config" "github.com/inwinstack/ipam/pkg/operator" "github.com/inwinstack/ipam/pkg/version" flag "github.com/spf13/pflag" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" ) var ( + cfg = &config.Config{} kubeconfig string ver bool ) func parserFlags() { flag.StringVarP(&kubeconfig, "kubeconfig", "", "", "Absolute path to the kubeconfig file.") + flag.IntVarP(&cfg.Threads, "threads", "", 2, "Number of worker threads used by the controller.") + flag.IntVarP(&cfg.SyncSec, "sync-seconds", "", 30, "Seconds for syncing and retrying objects.") flag.BoolVarP(&ver, "version", "", false, "Display the version") flag.CommandLine.AddGoFlagSet(goflag.CommandLine) flag.Parse() } +func restConfig(kubeconfig string) (*rest.Config, error) { + if kubeconfig != "" { + cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + return nil, err + } + return cfg, nil + } + + cfg, err := rest.InClusterConfig() + if err != nil { + return nil, err + } + return cfg, nil +} + func main() { defer glog.Flush() parserFlags() @@ -48,14 +74,27 @@ func main() { os.Exit(0) } - glog.Infof("Starting IPAM controller...") + k8scfg, err := restConfig(kubeconfig) + if err != nil { + glog.Fatalf("Error to build kubeconfig: %s", err.Error()) + } - op := operator.NewMainOperator(kubeconfig) - if err := op.Initialize(); err != nil { - glog.Fatalf("Error initing operator instance: %+v.\n", err) + blendedclient, err := blended.NewForConfig(k8scfg) + if err != nil { + glog.Fatalf("Error to build Blended client: %s", err.Error()) } - if err := op.Run(); err != nil { - glog.Fatalf("Error serving operator instance: %+v.\n", err) + ctx, cancel := context.WithCancel(context.Background()) + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) + + op := operator.New(cfg, blendedclient) + if err := op.Run(ctx); err != nil { + glog.Fatalf("Error to serve the operator instance: %s.", err) } + + <-signalChan + cancel() + op.Stop() + glog.Infof("Shutdown signal received, exiting...") } diff --git a/deploy/crd.yml b/deploy/crd.yml new file mode 100644 index 0000000..df77749 --- /dev/null +++ b/deploy/crd.yml @@ -0,0 +1,46 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: pools.inwinstack.com +spec: + group: inwinstack.com + version: v1 + names: + kind: Pool + plural: pools + scope: Cluster + additionalPrinterColumns: + - name: Allocatable + type: integer + JSONPath: .status.allocatable + - name: Capacity + type: integer + JSONPath: .status.capacity + - name: Status + type: string + JSONPath: .status.phase + - name: Age + type: date + JSONPath: .metadata.creationTimestamp +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: ips.inwinstack.com +spec: + group: inwinstack.com + version: v1 + names: + kind: IP + plural: ips + scope: Namespaced + additionalPrinterColumns: + - name: Address + type: string + JSONPath: .status.address + - name: Status + type: string + JSONPath: .status.phase + - name: Age + type: date + JSONPath: .metadata.creationTimestamp \ No newline at end of file diff --git a/deploy/deployment.yml b/deploy/deployment.yml index 5623ba5..ac08e66 100644 --- a/deploy/deployment.yml +++ b/deploy/deployment.yml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: ipam-controller + name: ipam namespace: kube-system spec: replicas: 1 @@ -21,8 +21,8 @@ spec: key: node-role.kubernetes.io/master serviceAccountName: ipam containers: - - name: ipam-controller - image: inwinstack/ipam:v0.6.1 + - name: ipam + image: inwinstack/ipam:v0.7.0 args: - --logtostderr=true - - --v=2 \ No newline at end of file + - --v=2 diff --git a/deploy/rbac.yml b/deploy/rbac.yml index 0238400..7fb6609 100644 --- a/deploy/rbac.yml +++ b/deploy/rbac.yml @@ -9,12 +9,6 @@ apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: ipam-role rules: -- apiGroups: - - "" - resources: - - namespaces - verbs: - - "*" - apiGroups: - apiextensions.k8s.io resources: @@ -29,7 +23,8 @@ rules: - apiGroups: - inwinstack.com resources: - - "*" + - "ips" + - "pools" verbs: - "*" --- diff --git a/examples/ip/test.yml b/examples/ip/test.yml index 93d2f70..f9fd0f4 100644 --- a/examples/ip/test.yml +++ b/examples/ip/test.yml @@ -1,6 +1,6 @@ apiVersion: inwinstack.com/v1 kind: IP metadata: - name: test-ip + name: test-ip-1 spec: poolName: test \ No newline at end of file diff --git a/examples/pool/test.yml b/examples/pool/test.yml index 0222a88..c0c5fbd 100644 --- a/examples/pool/test.yml +++ b/examples/pool/test.yml @@ -4,9 +4,11 @@ metadata: name: test spec: addresses: - - 172.22.132.0-172.22.132.10 + - 172.22.132.0-172.22.132.15 - 172.22.132.250-172.22.132.255 assignToNamespace: false + filterIPs: + - 172.22.132.10 avoidBuggyIPs: true avoidGatewayIPs: true ignoreNamespaceAnnotation: true diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..fbc03df --- /dev/null +++ b/go.mod @@ -0,0 +1,23 @@ +module github.com/inwinstack/ipam + +go 1.12 + +require ( + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/inwinstack/blended v0.7.0 + github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 + github.com/spf13/pflag v1.0.3 + github.com/stretchr/testify v1.3.0 + github.com/thoas/go-funk v0.4.0 + k8s.io/api v0.0.0-20190620084959-7cf5895f2711 + k8s.io/apiextensions-apiserver v0.0.0-20190620085554-14e95df34f1f + k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719 + k8s.io/client-go v8.0.0+incompatible +) + +replace ( + k8s.io/api => k8s.io/api v0.0.0-20190620084959-7cf5895f2711 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20190620085554-14e95df34f1f + k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719 + k8s.io/client-go => k8s.io/client-go v0.0.0-20190620085101-78d2af792bab +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1ddd0c7 --- /dev/null +++ b/go.sum @@ -0,0 +1,206 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-oidc v0.0.0-20180117170138-065b426bd416/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.0.0-20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +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/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +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/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= +github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415 h1:WSBJMqJbLxsn+bTCPyPYZfqHdJmc8MK4wrBjMft6BAM= +github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +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/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +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/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +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-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v0.0.0-20170330212424-2500245aa611/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inwinstack/blended v0.7.0 h1:fA5Kb7mxTabBb3KuNYc/7MsmQQ/Uw+A0ihSMFZvbD6E= +github.com/inwinstack/blended v0.7.0/go.mod h1:tyTgnHheUc/u3s+dWDzMcA9mkIfldHBvFajEKN8nbjk= +github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE= +github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws= +github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +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 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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/cobra v0.0.0-20180319062004-c439c4fa0937/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1/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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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/thoas/go-funk v0.4.0 h1:KBaa5NL7NMtsFlQaD8nQMbDt1wuM+OOaNQyYNYQFhVo= +github.com/thoas/go-funk v0.4.0/go.mod h1:mlR+dHGb+4YgXkf13rkQTuzrneeHANxOm6+ZnEV9HsA= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/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-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g= +golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20170731182057-09f6ed296fc6/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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/natefinch/lumberjack.v2 v2.0.0-20150622162204-20b71e5b60d7/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/square/go-jose.v2 v2.0.0-20180411045311-89060dee6a84/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +k8s.io/api v0.0.0-20190620084959-7cf5895f2711 h1:BblVYz/wE5WtBsD/Gvu54KyBUTJMflolzc5I2DTvh50= +k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= +k8s.io/apiextensions-apiserver v0.0.0-20190620085554-14e95df34f1f/go.mod h1:++XMkbLSSAutLgulnUnXW4kNbSkyQzlPL8PaW4hjJT4= +k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719 h1:uV4S5IB5g4Nvi+TBVNf3e9L4wrirlwYJ6w88jUQxTUw= +k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= +k8s.io/apiserver v0.0.0-20190620085212-47dc9a115b18/go.mod h1:Hc9PbFVOsMigd7B7OiY/6bIRkR8y31eIKsr1D+JtKg4= +k8s.io/client-go v0.0.0-20190620085101-78d2af792bab h1:E8Fecph0qbNsAbijJJQryKu4Oi9QTp5cVpjTE+nqg6g= +k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k= +k8s.io/code-generator v0.0.0-20190612205613-18da4a14b22b/go.mod h1:G8bQwmHm2eafm5bgtX67XDZQ8CWKSGu9DekI+yN4Y5I= +k8s.io/component-base v0.0.0-20190620085130-185d68e6e6ea/go.mod h1:VLedAFwENz2swOjm0zmUXpAP2mV55c49xgaOzPBI/QQ= +k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68= +k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4= +k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +sigs.k8s.io/structured-merge-diff v0.0.0-20190302045857-e85c7b244fd2/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/hack/test-go.sh b/hack/test-go.sh index d34135b..f622611 100755 --- a/hack/test-go.sh +++ b/hack/test-go.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright © 2018 inwinSTACK.inc +# Copyright © 2018 inwinSTACK Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pkg/util/util.go b/pkg/config/types.go similarity index 53% rename from pkg/util/util.go rename to pkg/config/types.go index 478f9dc..dffa861 100644 --- a/pkg/util/util.go +++ b/pkg/config/types.go @@ -1,5 +1,5 @@ /* -Copyright © 2018 inwinSTACK.inc +Copyright © 2018 inwinSTACK Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,27 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package config -import ( - "log" - "time" -) - -type RetriableError struct { - Err error -} - -func (r RetriableError) Error() string { return r.Err.Error() } - -func Retry(callback func() error, d time.Duration, attempts int) (err error) { - for i := 0; i < attempts; i++ { - err = callback() - if err == nil { - return nil - } - log.Printf("Error: %s, Retrying in %s. %d Retries remaining.", err, d, attempts-i) - time.Sleep(d) - } - return err +// Config contains the operator config +type Config struct { + Threads int + SyncSec int } diff --git a/pkg/util/net.go b/pkg/ipaddr/ipaddr.go similarity index 74% rename from pkg/util/net.go rename to pkg/ipaddr/ipaddr.go index d370dba..fd75a91 100644 --- a/pkg/util/net.go +++ b/pkg/ipaddr/ipaddr.go @@ -1,5 +1,5 @@ /* -Copyright © 2018 inwinSTACK.inc +Copyright © 2018 inwinSTACK Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,31 +14,33 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package ipaddr import ( "fmt" "net" "strings" + "github.com/thoas/go-funk" + "github.com/mikioh/ipaddr" ) -type NetworkParser struct { +type Parser struct { Addresses []string AvoidBuggy bool AvoidGateway bool } -func NewNetworkParser(addrs []string, buggy, gateway bool) *NetworkParser { - return &NetworkParser{ +func NewParser(addrs []string, buggy, gateway bool) *Parser { + return &Parser{ Addresses: addrs, AvoidBuggy: buggy, AvoidGateway: gateway, } } -func (p *NetworkParser) getIPNets(cidr string) ([]*net.IPNet, error) { +func (p *Parser) getIPNets(cidr string) ([]*net.IPNet, error) { if !strings.Contains(cidr, "-") { _, n, err := net.ParseCIDR(cidr) if err != nil { @@ -73,7 +75,7 @@ func (p *NetworkParser) getIPNets(cidr string) ([]*net.IPNet, error) { return ret, nil } -func (p *NetworkParser) isGatewayIP(v string) bool { +func (p *Parser) isGatewayIP(v string) bool { gatewayIPs := []string{"1", "254"} fs := strings.SplitN(v, ".", 4) for _, ip := range gatewayIPs { @@ -84,7 +86,7 @@ func (p *NetworkParser) isGatewayIP(v string) bool { return false } -func (p *NetworkParser) isBuggyIP(v string) bool { +func (p *Parser) isBuggyIP(v string) bool { buggyIPs := []string{"0", "255"} fs := strings.SplitN(v, ".", 4) for _, ip := range buggyIPs { @@ -104,7 +106,7 @@ func inc(ip net.IP) { } } -func (p *NetworkParser) getIPs(ipnet *net.IPNet) []string { +func (p *Parser) getIPs(ipnet *net.IPNet) []string { var ips []string for ip := ipnet.IP.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) { if p.AvoidBuggy && p.isBuggyIP(ip.String()) { @@ -119,12 +121,12 @@ func (p *NetworkParser) getIPs(ipnet *net.IPNet) []string { return ips } -func (p *NetworkParser) IPs() ([]string, error) { +func (p *Parser) IPs() ([]string, error) { var ips []string for _, address := range p.Addresses { nets, err := p.getIPNets(address) if err != nil { - return nil, fmt.Errorf("Invalid parse CIDR from %+v", address) + return nil, fmt.Errorf("Invalid parse CIDR from %+v address", address) } for _, net := range nets { @@ -133,3 +135,19 @@ func (p *NetworkParser) IPs() ([]string, error) { } return ips, nil } + +func (p *Parser) FilterIPs(filters ...[]string) ([]string, error) { + ips, err := p.IPs() + if err != nil { + return nil, err + } + + for _, f := range filters { + if len(f) > 0 { + for _, addr := range f { + ips = funk.FilterString(ips, func(v string) bool { return v != addr }) + } + } + } + return ips, nil +} diff --git a/pkg/ipaddr/ipaddr_test.go b/pkg/ipaddr/ipaddr_test.go new file mode 100644 index 0000000..55c4886 --- /dev/null +++ b/pkg/ipaddr/ipaddr_test.go @@ -0,0 +1,78 @@ +/* +Copyright © 2018 inwinSTACK Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package ipaddr + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIPs(t *testing.T) { + tests := []struct { + Parser *Parser + IPs []string + }{ + { + Parser: NewParser([]string{"172.22.132.0/30"}, true, true), + IPs: []string{"172.22.132.2", "172.22.132.3"}, + }, + { + Parser: NewParser([]string{"172.22.132.0/30"}, true, false), + IPs: []string{"172.22.132.1", "172.22.132.2", "172.22.132.3"}, + }, + { + Parser: NewParser([]string{"172.22.132.0/30"}, false, true), + IPs: []string{"172.22.132.0", "172.22.132.2", "172.22.132.3"}, + }, + { + Parser: NewParser([]string{"172.22.132.0/30"}, false, false), + IPs: []string{"172.22.132.0", "172.22.132.1", "172.22.132.2", "172.22.132.3"}, + }, + } + + for _, test := range tests { + ips, err := test.Parser.IPs() + assert.Nil(t, err) + assert.Equal(t, test.IPs, ips) + } +} + +func TestFilterIPs(t *testing.T) { + tests := []struct { + Parser *Parser + Filters []string + IPs []string + }{ + { + Parser: NewParser([]string{"172.22.132.0-172.22.132.5"}, true, true), + Filters: []string{"172.22.132.4", "172.22.132.5"}, + IPs: []string{"172.22.132.2", "172.22.132.3"}, + }, + { + Parser: NewParser([]string{"172.22.132.0/30"}, true, false), + Filters: []string{"172.22.132.3"}, + IPs: []string{"172.22.132.1", "172.22.132.2"}, + }, + } + + for _, test := range tests { + ips, err := test.Parser.FilterIPs(test.Filters) + assert.Nil(t, err) + assert.Equal(t, test.IPs, ips) + } +} diff --git a/pkg/operator/ip/controller.go b/pkg/operator/ip/controller.go index 10c1399..c7c5d6a 100644 --- a/pkg/operator/ip/controller.go +++ b/pkg/operator/ip/controller.go @@ -1,5 +1,5 @@ /* -Copyright © 2018 inwinSTACK.inc +Copyright © 2018 inwinSTACK Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,175 +17,258 @@ limitations under the License. package ip import ( + "context" "fmt" - "reflect" "time" + "github.com/thoas/go-funk" + "github.com/golang/glog" - inwinv1 "github.com/inwinstack/blended/apis/inwinstack/v1" - clientset "github.com/inwinstack/blended/client/clientset/versioned" - "github.com/inwinstack/ipam/pkg/util" - opkit "github.com/inwinstack/operator-kit" - slice "github.com/thoas/go-funk" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + blendedv1 "github.com/inwinstack/blended/apis/inwinstack/v1" + "github.com/inwinstack/blended/constants" + blended "github.com/inwinstack/blended/generated/clientset/versioned" + informerv1 "github.com/inwinstack/blended/generated/informers/externalversions/inwinstack/v1" + listerv1 "github.com/inwinstack/blended/generated/listers/inwinstack/v1" + "github.com/inwinstack/blended/k8sutil" + "github.com/inwinstack/ipam/pkg/ipaddr" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" ) -const ( - customResourceName = "ip" - customResourceNamePlural = "ips" -) - -var Resource = opkit.CustomResource{ - Name: customResourceName, - Plural: customResourceNamePlural, - Group: inwinv1.CustomResourceGroup, - Version: inwinv1.Version, - Scope: apiextensionsv1beta1.NamespaceScoped, - Kind: reflect.TypeOf(inwinv1.IP{}).Name(), -} - -type IPController struct { - ctx *opkit.Context - clientset clientset.Interface +// Controller represents the controller of ip +type Controller struct { + blendedset blended.Interface + lister listerv1.IPLister + synced cache.InformerSynced + queue workqueue.RateLimitingInterface } -func NewController(ctx *opkit.Context, clientset clientset.Interface) *IPController { - return &IPController{ctx: ctx, clientset: clientset} +// NewController creates an instance of the ip controller +func NewController(blendedset blended.Interface, informer informerv1.IPInformer) *Controller { + controller := &Controller{ + blendedset: blendedset, + lister: informer.Lister(), + synced: informer.Informer().HasSynced, + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "IPs"), + } + informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: controller.enqueue, + UpdateFunc: func(old, new interface{}) { + oo := old.(*blendedv1.IP) + no := new.(*blendedv1.IP) + k8sutil.MakeNeedToUpdate(&no.ObjectMeta, oo.Spec, no.Spec) + if k8sutil.IsNeedToUpdate(no.ObjectMeta) { + // Don't change the IP pool name + no.Spec.PoolName = oo.Spec.PoolName + } + controller.enqueue(no) + }, + }) + return controller } -func (c *IPController) StartWatch(namespace string, stopCh chan struct{}) error { - resourceHandlerFuncs := cache.ResourceEventHandlerFuncs{ - AddFunc: c.onAdd, - UpdateFunc: c.onUpdate, - DeleteFunc: c.onDelete, +// Run serves the ip controller +func (c *Controller) Run(ctx context.Context, threadiness int) error { + glog.Info("Starting the ip controller") + glog.Info("Waiting for the ip informer caches to sync") + if ok := cache.WaitForCacheSync(ctx.Done(), c.synced); !ok { + return fmt.Errorf("failed to wait for caches to sync") } - glog.Infof("Start watching IP resources.") - watcher := opkit.NewWatcher(Resource, namespace, resourceHandlerFuncs, c.clientset.InwinstackV1().RESTClient()) - go watcher.Watch(&inwinv1.IP{}, stopCh) + for i := 0; i < threadiness; i++ { + go wait.Until(c.runWorker, time.Second, ctx.Done()) + } return nil } -func (c *IPController) onAdd(obj interface{}) { - ip := obj.(*inwinv1.IP).DeepCopy() - glog.V(2).Infof("Received add on IP %s in %s namespace.", ip.Name, ip.Namespace) +// Stop stops the ip controller +func (c *Controller) Stop() { + glog.Info("Stopping the ip controller") + c.queue.ShutDown() +} - if ip.Status.Phase != inwinv1.IPActive { - if err := c.allocate(ip); err != nil { - glog.Errorf("Failed to allocate IP for %s in %s namespace: %+v.", ip.Name, ip.Namespace, err) - } +func (c *Controller) runWorker() { + defer utilruntime.HandleCrash() + for c.processNextWorkItem() { } } -func (c *IPController) onUpdate(oldObj, newObj interface{}) { - old := oldObj.(*inwinv1.IP).DeepCopy() - ip := newObj.(*inwinv1.IP).DeepCopy() - glog.V(2).Infof("Received update on IP %s in namespace %s.", ip.Name, ip.Namespace) +func (c *Controller) processNextWorkItem() bool { + obj, shutdown := c.queue.Get() + if shutdown { + return false + } - if old.Spec.PoolName != ip.Spec.PoolName { - if err := c.allocate(ip); err != nil { - glog.Errorf("Failed to allocate new IP for %s in %s namespace: %+v.", ip.Name, ip.Namespace, err) + err := func(obj interface{}) error { + defer c.queue.Done(obj) + key, ok := obj.(string) + if !ok { + c.queue.Forget(obj) + utilruntime.HandleError(fmt.Errorf("IP expected string in workqueue but got %#v", obj)) + return nil } - if err := c.deallocate(old); err != nil { - glog.Errorf("Failed to deallocate old IP for %s in %s namespace: %+v.", old.Name, old.Namespace, err) + if err := c.reconcile(key); err != nil { + c.queue.AddRateLimited(key) + return fmt.Errorf("IP error syncing '%s': %s, requeuing", key, err.Error()) } + + c.queue.Forget(obj) + glog.V(2).Infof("IP successfully synced '%s'", key) + return nil + }(obj) + + if err != nil { + utilruntime.HandleError(err) + return true } + return true } -func (c *IPController) onDelete(obj interface{}) { - ip := obj.(*inwinv1.IP).DeepCopy() - glog.V(2).Infof("Received delete on IP %s in %s namespace.", ip.Name, ip.Namespace) +func (c *Controller) enqueue(obj interface{}) { + key, err := cache.MetaNamespaceKeyFunc(obj) + if err != nil { + utilruntime.HandleError(err) + return + } + c.queue.Add(key) +} + +func (c *Controller) reconcile(key string) error { + namespace, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key)) + return err + } - if ip.Status.Phase == inwinv1.IPActive { - if err := c.deallocate(ip); err != nil { - glog.Errorf("Failed to deallocate IP for %s in %s namespace: %+v.", ip.Name, ip.Namespace, err) + ip, err := c.lister.IPs(namespace).Get(name) + if err != nil { + if errors.IsNotFound(err) { + utilruntime.HandleError(fmt.Errorf("ip '%s' in work queue no longer exists", key)) + return err } + return err } -} -func (c *IPController) makeFailedStatus(ip *inwinv1.IP, e error) error { - ip.Status.Phase = inwinv1.IPFailed - ip.Status.Reason = fmt.Sprintf("%+v.", e) - ip.Status.Address = "" - ip.Status.LastUpdateTime = metav1.NewTime(time.Now()) - if _, err := c.clientset.InwinstackV1().IPs(ip.Namespace).Update(ip); err != nil { + if !ip.ObjectMeta.DeletionTimestamp.IsZero() { + return c.deallocate(ip) + } + + if err := c.checkAndUdateFinalizer(ip); err != nil { return err } + + need := k8sutil.IsNeedToUpdate(ip.ObjectMeta) + if ip.Status.Phase != blendedv1.IPActive || need { + if err := c.allocate(ip); err != nil { + return err + } + } return nil } -func (c *IPController) updatePool(pool *inwinv1.Pool) error { - pool.Status.LastUpdateTime = metav1.NewTime(time.Now()) - if _, err := c.clientset.InwinstackV1().Pools().Update(pool); err != nil { - return err +func (c *Controller) checkAndUdateFinalizer(ip *blendedv1.IP) error { + ipCopy := ip.DeepCopy() + ok := funk.ContainsString(ipCopy.Finalizers, constants.CustomFinalizer) + if ipCopy.Status.Phase == blendedv1.IPActive && !ok { + k8sutil.AddFinalizer(&ipCopy.ObjectMeta, constants.CustomFinalizer) + if _, err := c.blendedset.InwinstackV1().IPs(ipCopy.Namespace).Update(ipCopy); err != nil { + return err + } } return nil } -func (c *IPController) allocate(ip *inwinv1.IP) error { - pool, err := c.clientset.InwinstackV1().Pools().Get(ip.Spec.PoolName, metav1.GetOptions{}) - if err != nil { - return c.makeFailedStatus(ip, err) +func (c *Controller) updatePool(pool *blendedv1.Pool) error { + pool.Status.LastUpdateTime = metav1.Now() + if _, err := c.blendedset.InwinstackV1().Pools().Update(pool); err != nil { + return err } + return nil +} - if pool.Status.Phase == inwinv1.PoolFailed { - err := fmt.Errorf("Unable to allocate IP from failed pool") - return c.makeFailedStatus(ip, err) +func (c *Controller) makeFailedStatus(ip *blendedv1.IP, e error) error { + ip.Status.Address = "" + ip.Status.Phase = blendedv1.IPFailed + ip.Status.Reason = fmt.Sprintf("%+v.", e) + ip.Status.LastUpdateTime = metav1.Now() + delete(ip.Annotations, constants.NeedUpdateKey) + if _, err := c.blendedset.InwinstackV1().IPs(ip.Namespace).Update(ip); err != nil { + return err } + return nil +} - if pool.Status.Allocatable == 0 { - err := fmt.Errorf("The pool \"%s\" has been exhausted", pool.Name) - return c.makeFailedStatus(ip, err) +func (c *Controller) allocate(ip *blendedv1.IP) error { + ipCopy := ip.DeepCopy() + pool, err := c.blendedset.InwinstackV1().Pools().Get(ipCopy.Spec.PoolName, metav1.GetOptions{}) + if err != nil { + return err } - np := util.NewNetworkParser(pool.Spec.Addresses, pool.Spec.AvoidBuggyIPs, pool.Spec.AvoidGatewayIPs) - ips, _ := np.IPs() + switch pool.Status.Phase { + case blendedv1.PoolActive: + if ipCopy.Status.Address == "" { + if pool.Status.Allocatable == 0 { + return c.makeFailedStatus(ipCopy, fmt.Errorf("The \"%s\" pool has been exhausted", pool.Name)) + } - filterIPs := pool.Status.AllocatedIPs - if pool.Spec.FilterIPs != nil { - filterIPs = append([]string{}, append(filterIPs, pool.Spec.FilterIPs...)...) - } + parser := ipaddr.NewParser(pool.Spec.Addresses, pool.Spec.AvoidBuggyIPs, pool.Spec.AvoidGatewayIPs) + ips, err := parser.FilterIPs(pool.Status.AllocatedIPs, pool.Spec.FilterIPs) + if err != nil { + return c.makeFailedStatus(ipCopy, err) + } - // Filter IPs - for _, rem := range filterIPs { - ips = slice.FilterString(ips, func(v string) bool { - return v != rem - }) - } + pool.Status.AllocatedIPs = append(pool.Status.AllocatedIPs, ips[0]) + pool.Status.Allocatable = pool.Status.Capacity - len(pool.Status.AllocatedIPs) + if err := c.updatePool(pool); err != nil { + // If the pool failed to update, this res will requeue + return err + } - pool.Status.AllocatedIPs = append(pool.Status.AllocatedIPs, ips[0]) - pool.Status.Allocatable = pool.Status.Capacity - len(pool.Status.AllocatedIPs) - if err := c.updatePool(pool); err != nil { - return c.makeFailedStatus(ip, err) + ipCopy.Status.Reason = "" + ipCopy.Status.Address = ips[0] + ipCopy.Status.Phase = blendedv1.IPActive + k8sutil.AddFinalizer(&ipCopy.ObjectMeta, constants.CustomFinalizer) + } + case blendedv1.PoolTerminating: + ipCopy.Status.Reason = fmt.Sprintf("The \"%s\" pool has been terminated.", pool.Name) + ipCopy.Status.Phase = blendedv1.IPFailed } - ip.Status.Address = ips[0] - ip.Status.Phase = inwinv1.IPActive - ip.Status.Reason = "" - ip.Status.LastUpdateTime = metav1.NewTime(time.Now()) - if _, err := c.clientset.InwinstackV1().IPs(ip.Namespace).Update(ip); err != nil { + delete(ipCopy.Annotations, constants.NeedUpdateKey) + ipCopy.Status.LastUpdateTime = metav1.Now() + if _, err := c.blendedset.InwinstackV1().IPs(ipCopy.Namespace).Update(ipCopy); err != nil { return err } return nil } -func (c *IPController) deallocate(ip *inwinv1.IP) error { - pool, err := c.clientset.InwinstackV1().Pools().Get(ip.Spec.PoolName, metav1.GetOptions{}) +func (c *Controller) deallocate(ip *blendedv1.IP) error { + ipCopy := ip.DeepCopy() + pool, err := c.blendedset.InwinstackV1().Pools().Get(ipCopy.Spec.PoolName, metav1.GetOptions{}) if err != nil { - return c.makeFailedStatus(ip, err) + return err } - if pool.Status.Phase == inwinv1.PoolActive { - pool.Status.AllocatedIPs = slice.FilterString(pool.Status.AllocatedIPs, func(v string) bool { - return v != ip.Status.Address - }) - pool.Status.Allocatable = pool.Status.Capacity - len(pool.Status.AllocatedIPs) - if err := c.updatePool(pool); err != nil { - return c.makeFailedStatus(ip, err) - } + pool.Status.AllocatedIPs = funk.FilterString(pool.Status.AllocatedIPs, func(v string) bool { + return v != ip.Status.Address + }) + pool.Status.Allocatable = pool.Status.Capacity - len(pool.Status.AllocatedIPs) + if err := c.updatePool(pool); err != nil { + return err + } + + ipCopy.Status.LastUpdateTime = metav1.Now() + ipCopy.Status.Phase = blendedv1.IPTerminating + delete(ip.Annotations, constants.NeedUpdateKey) + k8sutil.RemoveFinalizer(&ipCopy.ObjectMeta, constants.CustomFinalizer) + if _, err := c.blendedset.InwinstackV1().IPs(ipCopy.Namespace).Update(ipCopy); err != nil { + return err } return nil } diff --git a/pkg/operator/ip/controller_test.go b/pkg/operator/ip/controller_test.go index 63114e7..0e1ef10 100644 --- a/pkg/operator/ip/controller_test.go +++ b/pkg/operator/ip/controller_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2018 inwinSTACK.inc +Copyright © 2018 inwinSTACK Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,147 +17,92 @@ limitations under the License. package ip import ( + "context" "testing" "time" - inwinv1 "github.com/inwinstack/blended/apis/inwinstack/v1" - fake "github.com/inwinstack/blended/client/clientset/versioned/fake" - opkit "github.com/inwinstack/operator-kit" - - "k8s.io/api/core/v1" - extensionsfake "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" - corefake "k8s.io/client-go/kubernetes/fake" - + blendedv1 "github.com/inwinstack/blended/apis/inwinstack/v1" + blendedfake "github.com/inwinstack/blended/generated/clientset/versioned/fake" + blendedinformers "github.com/inwinstack/blended/generated/informers/externalversions" + "github.com/inwinstack/ipam/pkg/config" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -const namespace = "test" - -func TestIPController(t *testing.T) { - client := fake.NewSimpleClientset() - coreClient := corefake.NewSimpleClientset() - extensionsClient := extensionsfake.NewSimpleClientset() +const timeout = 3 * time.Second - test := &inwinv1.Pool{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: inwinv1.PoolSpec{ - Addresses: []string{"172.22.132.150-172.22.132.200"}, - AssignToNamespace: false, - IgnoreNamespaces: []string{"kube-system", "kube-public", "default"}, - }, - Status: inwinv1.PoolStatus{ - Phase: inwinv1.PoolActive, - AllocatedIPs: []string{}, - Capacity: 51, - Allocatable: 51, - LastUpdateTime: metav1.NewTime(time.Now()), - }, - } +func TestPoolController(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + cfg := &config.Config{Threads: 2} + blendedset := blendedfake.NewSimpleClientset() + informer := blendedinformers.NewSharedInformerFactory(blendedset, 0) - _, testerr := client.InwinstackV1().Pools().Create(test) - assert.Nil(t, testerr) + controller := NewController(blendedset, informer.Inwinstack().V1().IPs()) + go informer.Start(ctx.Done()) + assert.Nil(t, controller.Run(ctx, cfg.Threads)) - internet := &inwinv1.Pool{ + pool := &blendedv1.Pool{ ObjectMeta: metav1.ObjectMeta{ - Name: "internet", + Name: "test", }, - Spec: inwinv1.PoolSpec{ - Addresses: []string{"140.145.33.150-140.145.33.200"}, + Spec: blendedv1.PoolSpec{ + Addresses: []string{"172.22.132.0-172.22.132.5"}, AssignToNamespace: false, - IgnoreNamespaces: []string{"kube-system", "kube-public", "default"}, + AvoidBuggyIPs: true, + AvoidGatewayIPs: false, }, - Status: inwinv1.PoolStatus{ - Phase: inwinv1.PoolActive, + Status: blendedv1.PoolStatus{ + Phase: blendedv1.PoolActive, AllocatedIPs: []string{}, - Capacity: 51, - Allocatable: 51, + Capacity: 5, + Allocatable: 5, LastUpdateTime: metav1.NewTime(time.Now()), }, } + _, err := blendedset.InwinstackV1().Pools().Create(pool) + assert.Nil(t, err) - _, interneterr := client.InwinstackV1().Pools().Create(internet) - assert.Nil(t, interneterr) - - ns := &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: namespace, - Annotations: map[string]string{}, - }, - } - - _, nserr := coreClient.CoreV1().Namespaces().Create(ns) - assert.Nil(t, nserr) - - ctx := &opkit.Context{ - Clientset: coreClient, - APIExtensionClientset: extensionsClient, - Interval: 500 * time.Millisecond, - Timeout: 60 * time.Second, - } - - controller := NewController(ctx, client) - assert.NotNil(t, controller) - - // Test onAdd - ip := &inwinv1.IP{ + ip := &blendedv1.IP{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-ip", - Namespace: namespace, + Name: "test-ip-1", + Namespace: "default", }, - Spec: inwinv1.IPSpec{ - PoolName: test.Name, + Spec: blendedv1.IPSpec{ + PoolName: pool.Name, }, } - createIP, err := client.InwinstackV1().IPs(namespace).Create(ip) - assert.Nil(t, err) - - controller.onAdd(createIP) - - onAddIP, err := client.InwinstackV1().IPs(namespace).Get(ip.Name, metav1.GetOptions{}) - assert.Nil(t, err) - assert.Equal(t, inwinv1.IPActive, onAddIP.Status.Phase) - assert.Equal(t, "172.22.132.150", onAddIP.Status.Address) - - onAddPool, err := client.InwinstackV1().Pools().Get(test.Name, metav1.GetOptions{}) + _, err = blendedset.InwinstackV1().IPs(ip.Namespace).Create(ip) assert.Nil(t, err) - assert.Equal(t, []string{"172.22.132.150"}, onAddPool.Status.AllocatedIPs) - assert.Equal(t, 51, onAddPool.Status.Capacity) - assert.Equal(t, 50, onAddPool.Status.Allocatable) - - // Test onUpdate - controller.onUpdate(createIP, onAddIP) - - // Test onUpdate for change pool - onUpdateIP, err := client.InwinstackV1().IPs(namespace).Get(ip.Name, metav1.GetOptions{}) - assert.Nil(t, err) - - onUpdateIP.Spec.PoolName = internet.Name - controller.onUpdate(onAddIP, onUpdateIP) - onUpdateNewPoolIP, err := client.InwinstackV1().IPs(namespace).Get(ip.Name, metav1.GetOptions{}) - assert.Nil(t, err) - assert.Equal(t, inwinv1.IPActive, onUpdateNewPoolIP.Status.Phase) - assert.Equal(t, "140.145.33.150", onUpdateNewPoolIP.Status.Address) + failed := true + for start := time.Now(); time.Since(start) < timeout; { + gip, err := blendedset.InwinstackV1().IPs(ip.Namespace).Get(ip.Name, metav1.GetOptions{}) + assert.Nil(t, err) + + if gip.Status.Phase == blendedv1.IPActive { + assert.Equal(t, "172.22.132.1", gip.Status.Address) + gpool, err := blendedset.InwinstackV1().Pools().Get(pool.Name, metav1.GetOptions{}) + assert.Nil(t, err) + assert.Equal(t, []string{"172.22.132.1"}, gpool.Status.AllocatedIPs) + assert.Equal(t, 4, gpool.Status.Allocatable) + assert.Equal(t, 5, gpool.Status.Capacity) + failed = false + break + } + } + assert.Equal(t, false, failed, "The service object failed to allocate IP.") - onUpdateNewTestPool, err := client.InwinstackV1().Pools().Get(test.Name, metav1.GetOptions{}) + // Test to deallocate IP + gip, err := blendedset.InwinstackV1().IPs(ip.Namespace).Get(ip.Name, metav1.GetOptions{}) assert.Nil(t, err) - assert.Equal(t, []string{}, onUpdateNewTestPool.Status.AllocatedIPs) - assert.Equal(t, 51, onUpdateNewTestPool.Status.Allocatable) + assert.Nil(t, controller.deallocate(gip)) - onUpdateNewInternetPool, err := client.InwinstackV1().Pools().Get(internet.Name, metav1.GetOptions{}) + gpool, err := blendedset.InwinstackV1().Pools().Get(pool.Name, metav1.GetOptions{}) assert.Nil(t, err) - assert.Equal(t, []string{"140.145.33.150"}, onUpdateNewInternetPool.Status.AllocatedIPs) - assert.Equal(t, 50, onUpdateNewInternetPool.Status.Allocatable) + assert.Equal(t, []string{}, gpool.Status.AllocatedIPs) + assert.Equal(t, 5, gpool.Status.Allocatable) + assert.Equal(t, 5, gpool.Status.Capacity) - // Test onDelete - controller.onDelete(onUpdateNewPoolIP) - - onDeletePool, err := client.InwinstackV1().Pools().Get(internet.Name, metav1.GetOptions{}) - assert.Nil(t, err) - assert.Equal(t, []string{}, onDeletePool.Status.AllocatedIPs) - assert.Equal(t, 51, onDeletePool.Status.Capacity) - assert.Equal(t, 51, onDeletePool.Status.Allocatable) + cancel() + controller.Stop() } diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index bdae074..fe2d92f 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -1,5 +1,5 @@ /* -Copyright © 2018 inwinSTACK.inc +Copyright © 2018 inwinSTACK Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,130 +17,55 @@ limitations under the License. package operator import ( + "context" "fmt" - "os" - "os/signal" - "syscall" "time" - "github.com/golang/glog" - clientset "github.com/inwinstack/blended/client/clientset/versioned" - "github.com/inwinstack/ipam/pkg/k8sutil" + blended "github.com/inwinstack/blended/generated/clientset/versioned" + blendedinformers "github.com/inwinstack/blended/generated/informers/externalversions" + "github.com/inwinstack/ipam/pkg/config" "github.com/inwinstack/ipam/pkg/operator/ip" "github.com/inwinstack/ipam/pkg/operator/pool" - opkit "github.com/inwinstack/operator-kit" - apiextensionsclients "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" ) -type Operator struct { - kubeconfig string - - ctx *opkit.Context - pool *pool.PoolController - ip *ip.IPController - resources []opkit.CustomResource -} - -const ( - initRetryDelay = 10 * time.Second - interval = 500 * time.Millisecond - timeout = 60 * time.Second -) - -func NewMainOperator(kubeconfig string) *Operator { - return &Operator{ - resources: []opkit.CustomResource{pool.Resource, ip.Resource}, - kubeconfig: kubeconfig, - } -} - -func (o *Operator) Initialize() error { - glog.V(2).Info("Initialize the operator resources.") +const defaultSyncTime = time.Second * 30 - ctx, blendedClient, err := o.initContextAndClient() - if err != nil { - return err - } - o.pool = pool.NewController(ctx, blendedClient) - o.ip = ip.NewController(ctx, blendedClient) - o.ctx = ctx - return nil +// Operator represents an operator context +type Operator struct { + clientset blended.Interface + informer blendedinformers.SharedInformerFactory + cfg *config.Config + pool *pool.Controller + ip *ip.Controller } -func (o *Operator) initContextAndClient() (*opkit.Context, clientset.Interface, error) { - glog.V(2).Info("Initialize the operator context and client.") - - config, err := k8sutil.GetRestConfig(o.kubeconfig) - if err != nil { - return nil, nil, fmt.Errorf("Failed to get Kubernetes config. %+v", err) - } - - client, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, nil, fmt.Errorf("Failed to get Kubernetes client. %+v", err) - } - - extensionsClient, err := apiextensionsclients.NewForConfig(config) - if err != nil { - return nil, nil, fmt.Errorf("Failed to create Kubernetes API extension clientset. %+v", err) - } - - blendedClient, err := clientset.NewForConfig(config) - if err != nil { - return nil, nil, fmt.Errorf("Failed to create blended clientset. %+v", err) - } - - ctx := &opkit.Context{ - Clientset: client, - APIExtensionClientset: extensionsClient, - Interval: interval, - Timeout: timeout, +// New creates an instance of the operator +func New(cfg *config.Config, clientset blended.Interface) *Operator { + t := defaultSyncTime + if cfg.SyncSec > 30 { + t = time.Second * time.Duration(cfg.SyncSec) } - return ctx, blendedClient, nil + o := &Operator{cfg: cfg, clientset: clientset} + o.informer = blendedinformers.NewSharedInformerFactory(clientset, t) + o.pool = pool.NewController(clientset, o.informer.Inwinstack().V1().Pools()) + o.ip = ip.NewController(clientset, o.informer.Inwinstack().V1().IPs()) + return o } -func (o *Operator) initResources() error { - glog.V(2).Info("Initialize the CRD resources.") - - ctx := opkit.Context{ - Clientset: o.ctx.Clientset, - APIExtensionClientset: o.ctx.APIExtensionClientset, - Interval: interval, - Timeout: timeout, +// Run serves an isntance of the operator +func (o *Operator) Run(ctx context.Context) error { + go o.informer.Start(ctx.Done()) + if err := o.pool.Run(ctx, o.cfg.Threads); err != nil { + return fmt.Errorf("failed to run the pool controller: %s", err.Error()) } - - if err := opkit.CreateCustomResources(ctx, o.resources); err != nil { - return fmt.Errorf("Failed to create custom resource. %+v", err) + if err := o.ip.Run(ctx, o.cfg.Threads); err != nil { + return fmt.Errorf("failed to run the ip controller: %s", err.Error()) } return nil } -func (o *Operator) Run() error { - for { - err := o.initResources() - if err == nil { - break - } - glog.Errorf("Failed to init resources. %+v. retrying...", err) - <-time.After(initRetryDelay) - } - - signalChan := make(chan os.Signal, 1) - stopChan := make(chan struct{}) - signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) - - // start watching the custom resources - o.ip.StartWatch(v1.NamespaceAll, stopChan) - o.pool.StartWatch(v1.NamespaceAll, stopChan) - - for { - select { - case <-signalChan: - glog.Infof("Shutdown signal received, exiting...") - close(stopChan) - return nil - } - } +// Stop stops the main controller +func (o *Operator) Stop() { + o.pool.Stop() + o.ip.Stop() } diff --git a/pkg/operator/operator_test.go b/pkg/operator/operator_test.go index f96215f..491757a 100644 --- a/pkg/operator/operator_test.go +++ b/pkg/operator/operator_test.go @@ -1,12 +1,9 @@ /* -Copyright © 2018 inwinSTACK.inc - +Copyright © 2018 inwinSTACK Inc 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. @@ -17,21 +14,33 @@ limitations under the License. package operator import ( + "context" "fmt" + "reflect" "testing" - "time" - opkit "github.com/inwinstack/operator-kit" + blendedv1 "github.com/inwinstack/blended/apis/inwinstack/v1" + blendedfake "github.com/inwinstack/blended/generated/clientset/versioned/fake" + "github.com/inwinstack/ipam/pkg/config" + "github.com/stretchr/testify/assert" apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" extensionsfake "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" - corefake "k8s.io/client-go/kubernetes/fake" - - "github.com/stretchr/testify/assert" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func createCRD(context *opkit.Context, resource opkit.CustomResource) error { +type customResource struct { + Name string + Kind string + Group string + Plural string + Version string + Scope apiextensionsv1beta1.ResourceScope + ShortNames []string +} + +func createCRD(clientset apiextensionsclientset.Interface, resource customResource) error { crdName := fmt.Sprintf("%s.%s", resource.Plural, resource.Group) crd := &apiextensionsv1beta1.CustomResourceDefinition{ ObjectMeta: metav1.ObjectMeta{ @@ -49,8 +58,7 @@ func createCRD(context *opkit.Context, resource opkit.CustomResource) error { }, }, } - - _, err := context.APIExtensionClientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd) + _, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd) if err != nil { if !errors.IsAlreadyExists(err) { return fmt.Errorf("failed to create %s CRD. %+v", resource.Name, err) @@ -60,35 +68,41 @@ func createCRD(context *opkit.Context, resource opkit.CustomResource) error { } func TestOperator(t *testing.T) { - coreClient := corefake.NewSimpleClientset() + ctx, cancel := context.WithCancel(context.Background()) + cfg := &config.Config{Threads: 2} + blendedset := blendedfake.NewSimpleClientset() extensionsClient := extensionsfake.NewSimpleClientset() - operator := NewMainOperator("") - operator.ctx = &opkit.Context{ - Clientset: coreClient, - APIExtensionClientset: extensionsClient, - Interval: 500 * time.Millisecond, - Timeout: 60 * time.Second, + resources := []customResource{ + { + Name: "pool", + Plural: "pools", + Kind: reflect.TypeOf(blendedv1.Pool{}).Name(), + Group: blendedv1.CustomResourceGroup, + Version: blendedv1.Version, + Scope: apiextensionsv1beta1.ClusterScoped, + }, + { + Name: "ip", + Plural: "ips", + Kind: reflect.TypeOf(blendedv1.IP{}).Name(), + Group: blendedv1.CustomResourceGroup, + Version: blendedv1.Version, + Scope: apiextensionsv1beta1.NamespaceScoped, + }, } - - assert.NotNil(t, operator) - assert.Equal(t, coreClient, operator.ctx.Clientset) - assert.Equal(t, extensionsClient, operator.ctx.APIExtensionClientset) - - for _, res := range operator.resources { - assert.Nil(t, createCRD(operator.ctx, res)) + for _, res := range resources { + assert.Nil(t, createCRD(extensionsClient, res)) } crds, err := extensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().List(metav1.ListOptions{}) assert.Nil(t, err) - assert.Equal(t, len(operator.resources), len(crds.Items)) + assert.Equal(t, len(resources), len(crds.Items)) - for index, crd := range crds.Items { - assert.Equal(t, operator.resources[index].Group, crd.Spec.Group) - assert.Equal(t, operator.resources[index].Scope, crd.Spec.Scope) - assert.Equal(t, operator.resources[index].Name, crd.Spec.Names.Singular) - assert.Equal(t, operator.resources[index].Kind, crd.Spec.Names.Kind) - assert.Equal(t, operator.resources[index].Plural, crd.Spec.Names.Plural) - assert.Equal(t, operator.resources[index].ShortNames, crd.Spec.Names.ShortNames) - } + op := New(cfg, blendedset) + assert.NotNil(t, op) + assert.Nil(t, op.Run(ctx)) + + cancel() + op.Stop() } diff --git a/pkg/operator/pool/controller.go b/pkg/operator/pool/controller.go index 24789ef..5bc68cd 100644 --- a/pkg/operator/pool/controller.go +++ b/pkg/operator/pool/controller.go @@ -1,5 +1,5 @@ /* -Copyright © 2018 inwinSTACK.inc +Copyright © 2018 inwinSTACK Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,119 +17,215 @@ limitations under the License. package pool import ( + "context" "fmt" - "reflect" "time" - "github.com/golang/glog" - inwinv1 "github.com/inwinstack/blended/apis/inwinstack/v1" - clientset "github.com/inwinstack/blended/client/clientset/versioned" - "github.com/inwinstack/ipam/pkg/util" - opkit "github.com/inwinstack/operator-kit" - slice "github.com/thoas/go-funk" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + "github.com/thoas/go-funk" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/cache" -) -const ( - customResourceName = "pool" - customResourceNamePlural = "pools" + "github.com/golang/glog" + blendedv1 "github.com/inwinstack/blended/apis/inwinstack/v1" + "github.com/inwinstack/blended/constants" + blended "github.com/inwinstack/blended/generated/clientset/versioned" + informerv1 "github.com/inwinstack/blended/generated/informers/externalversions/inwinstack/v1" + listerv1 "github.com/inwinstack/blended/generated/listers/inwinstack/v1" + "github.com/inwinstack/blended/k8sutil" + "github.com/inwinstack/ipam/pkg/ipaddr" + "k8s.io/apimachinery/pkg/api/errors" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" ) -var Resource = opkit.CustomResource{ - Name: customResourceName, - Plural: customResourceNamePlural, - Group: inwinv1.CustomResourceGroup, - Version: inwinv1.Version, - Scope: apiextensionsv1beta1.ClusterScoped, - Kind: reflect.TypeOf(inwinv1.Pool{}).Name(), +// Controller represents the controller of pool +type Controller struct { + blendedset blended.Interface + lister listerv1.PoolLister + synced cache.InformerSynced + queue workqueue.RateLimitingInterface } -type PoolController struct { - ctx *opkit.Context - clientset clientset.Interface -} - -func NewController(ctx *opkit.Context, clientset clientset.Interface) *PoolController { - return &PoolController{ctx: ctx, clientset: clientset} +// NewController creates an instance of the pool controller +func NewController(blendedset blended.Interface, informer informerv1.PoolInformer) *Controller { + controller := &Controller{ + blendedset: blendedset, + lister: informer.Lister(), + synced: informer.Informer().HasSynced, + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Pools"), + } + informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: controller.enqueue, + UpdateFunc: func(old, new interface{}) { + oo := old.(*blendedv1.Pool) + no := new.(*blendedv1.Pool) + k8sutil.MakeNeedToUpdate(&no.ObjectMeta, oo.Spec, no.Spec) + controller.enqueue(no) + }, + }) + return controller } -func (c *PoolController) StartWatch(namespace string, stopCh chan struct{}) error { - resourceHandlerFuncs := cache.ResourceEventHandlerFuncs{ - AddFunc: c.onAdd, - UpdateFunc: c.onUpdate, +// Run serves the pool controller +func (c *Controller) Run(ctx context.Context, threadiness int) error { + glog.Info("Starting the pool controller") + glog.Info("Waiting for the pool informer caches to sync") + if ok := cache.WaitForCacheSync(ctx.Done(), c.synced); !ok { + return fmt.Errorf("failed to wait for caches to sync") } - glog.Infof("Start watching pool resources.") - watcher := opkit.NewWatcher(Resource, namespace, resourceHandlerFuncs, c.clientset.InwinstackV1().RESTClient()) - go watcher.Watch(&inwinv1.Pool{}, stopCh) + for i := 0; i < threadiness; i++ { + go wait.Until(c.runWorker, time.Second, ctx.Done()) + } return nil } -func (c *PoolController) onAdd(obj interface{}) { - pool := obj.(*inwinv1.Pool).DeepCopy() - glog.V(2).Infof("Received add on Pool %s.", pool.Name) +// Stop stops the pool controller +func (c *Controller) Stop() { + glog.Info("Stopping the pool controller") + c.queue.ShutDown() +} - if err := c.makeStatus(pool); err != nil { - glog.Errorf("Failed to init status on Pool %s: %+v.", pool.Name, err) +func (c *Controller) runWorker() { + defer utilruntime.HandleCrash() + for c.processNextWorkItem() { } } -func (c *PoolController) onUpdate(oldObj, newObj interface{}) { - old := oldObj.(*inwinv1.Pool).DeepCopy() - pool := newObj.(*inwinv1.Pool).DeepCopy() - glog.V(2).Infof("Received update on Pool %s.", pool.Name) +func (c *Controller) processNextWorkItem() bool { + obj, shutdown := c.queue.Get() + if shutdown { + return false + } - if !reflect.DeepEqual(old.Spec, pool.Spec) { - if err := c.updateStatus(pool); err != nil { - glog.Errorf("Failed to update status on Pool %s: %+v.", pool.Name, err) + err := func(obj interface{}) error { + defer c.queue.Done(obj) + key, ok := obj.(string) + if !ok { + c.queue.Forget(obj) + utilruntime.HandleError(fmt.Errorf("Pool expected string in workqueue but got %#v", obj)) + return nil } + + if err := c.reconcile(key); err != nil { + c.queue.AddRateLimited(key) + return fmt.Errorf("Pool error syncing '%s': %s, requeuing", key, err.Error()) + } + + c.queue.Forget(obj) + glog.V(2).Infof("Pool successfully synced '%s'", key) + return nil + }(obj) + + if err != nil { + utilruntime.HandleError(err) + return true + } + return true +} + +func (c *Controller) enqueue(obj interface{}) { + key, err := cache.MetaNamespaceKeyFunc(obj) + if err != nil { + utilruntime.HandleError(err) + return } + c.queue.Add(key) } -func (c *PoolController) makeStatus(pool *inwinv1.Pool) error { - if pool.Status.Capacity == 0 && pool.Status.Phase != inwinv1.PoolActive { - if err := c.setStatus(true, pool); err != nil { +func (c *Controller) reconcile(key string) error { + _, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key)) + return err + } + + pool, err := c.lister.Get(name) + if err != nil { + if errors.IsNotFound(err) { + utilruntime.HandleError(fmt.Errorf("pool '%s' in work queue no longer exists", key)) return err } + return err + } + + if !pool.ObjectMeta.DeletionTimestamp.IsZero() { + return c.cleanup(pool) + } + + if err := c.checkAndUdateFinalizer(pool); err != nil { + return err + } + + need := k8sutil.IsNeedToUpdate(pool.ObjectMeta) + if pool.Status.Phase != blendedv1.PoolActive || need { + if err := c.makeStatus(pool); err != nil { + return c.makeFailedStatus(pool, err) + } } return nil } -func (c *PoolController) updateStatus(pool *inwinv1.Pool) error { - return c.setStatus(false, pool) +func (c *Controller) checkAndUdateFinalizer(pool *blendedv1.Pool) error { + poolCopy := pool.DeepCopy() + ok := funk.ContainsString(poolCopy.Finalizers, constants.CustomFinalizer) + if poolCopy.Status.Phase == blendedv1.PoolActive && !ok { + k8sutil.AddFinalizer(&poolCopy.ObjectMeta, constants.CustomFinalizer) + if _, err := c.blendedset.InwinstackV1().Pools().Update(poolCopy); err != nil { + return err + } + } + return nil } -func (c *PoolController) setStatus(init bool, pool *inwinv1.Pool) error { - pool.Status.Phase = inwinv1.PoolActive - np := util.NewNetworkParser(pool.Spec.Addresses, pool.Spec.AvoidBuggyIPs, pool.Spec.AvoidGatewayIPs) - ips, err := np.IPs() +func (c *Controller) makeStatus(pool *blendedv1.Pool) error { + poolCopy := pool.DeepCopy() + if poolCopy.Status.AllocatedIPs == nil { + poolCopy.Status.AllocatedIPs = []string{} + } + + parser := ipaddr.NewParser(poolCopy.Spec.Addresses, poolCopy.Spec.AvoidBuggyIPs, poolCopy.Spec.AvoidGatewayIPs) + ips, err := parser.FilterIPs(pool.Spec.FilterIPs) if err != nil { - pool.Status.Phase = inwinv1.PoolFailed - pool.Status.Reason = fmt.Sprintf("%+v.", err) + return err } - if pool.Spec.FilterIPs != nil { - for _, rem := range pool.Spec.FilterIPs { - ips = slice.FilterString(ips, func(v string) bool { - return v != rem - }) - } + poolCopy.Status.Reason = "" + poolCopy.Status.Capacity = len(ips) + poolCopy.Status.Allocatable = len(ips) - len(poolCopy.Status.AllocatedIPs) + poolCopy.Status.LastUpdateTime = metav1.NewTime(time.Now()) + poolCopy.Status.Phase = blendedv1.PoolActive + delete(poolCopy.Annotations, constants.NeedUpdateKey) + k8sutil.AddFinalizer(&poolCopy.ObjectMeta, constants.CustomFinalizer) + if _, err := c.blendedset.InwinstackV1().Pools().Update(poolCopy); err != nil { + return err } + return nil +} - if init { - pool.Status.AllocatedIPs = []string{} +func (c *Controller) makeFailedStatus(pool *blendedv1.Pool, e error) error { + poolCopy := pool.DeepCopy() + poolCopy.Status.Reason = e.Error() + poolCopy.Status.Phase = blendedv1.PoolFailed + poolCopy.Status.LastUpdateTime = metav1.NewTime(time.Now()) + delete(poolCopy.Annotations, constants.NeedUpdateKey) + if _, err := c.blendedset.InwinstackV1().Pools().Update(poolCopy); err != nil { + return err } + glog.Errorf("Pool got an error: %+v.", e) + return nil +} - if pool.Status.Phase == inwinv1.PoolActive { - pool.Status.Reason = "" +func (c *Controller) cleanup(pool *blendedv1.Pool) error { + poolCopy := pool.DeepCopy() + poolCopy.Status.Phase = blendedv1.PoolTerminating + if len(poolCopy.Status.AllocatedIPs) == 0 { + k8sutil.RemoveFinalizer(&poolCopy.ObjectMeta, constants.CustomFinalizer) } - pool.Status.Capacity = len(ips) - pool.Status.Allocatable = len(ips) - pool.Status.LastUpdateTime = metav1.NewTime(time.Now()) - if _, err := c.clientset.InwinstackV1().Pools().Update(pool); err != nil { + if _, err := c.blendedset.InwinstackV1().Pools().Update(poolCopy); err != nil { return err } return nil diff --git a/pkg/operator/pool/controller_test.go b/pkg/operator/pool/controller_test.go index 34edf65..4dafe72 100644 --- a/pkg/operator/pool/controller_test.go +++ b/pkg/operator/pool/controller_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2018 inwinSTACK.inc +Copyright © 2018 inwinSTACK Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,41 +17,35 @@ limitations under the License. package pool import ( + "context" "testing" "time" - inwinv1 "github.com/inwinstack/blended/apis/inwinstack/v1" - fake "github.com/inwinstack/blended/client/clientset/versioned/fake" - opkit "github.com/inwinstack/operator-kit" - - extensionsfake "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" - corefake "k8s.io/client-go/kubernetes/fake" - + blendedv1 "github.com/inwinstack/blended/apis/inwinstack/v1" + blendedfake "github.com/inwinstack/blended/generated/clientset/versioned/fake" + blendedinformers "github.com/inwinstack/blended/generated/informers/externalversions" + "github.com/inwinstack/ipam/pkg/config" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const timeout = 3 * time.Second + func TestPoolController(t *testing.T) { - client := fake.NewSimpleClientset() - coreClient := corefake.NewSimpleClientset() - extensionsClient := extensionsfake.NewSimpleClientset() - - ctx := &opkit.Context{ - Clientset: coreClient, - APIExtensionClientset: extensionsClient, - Interval: 500 * time.Millisecond, - Timeout: 60 * time.Second, - } + ctx, cancel := context.WithCancel(context.Background()) + cfg := &config.Config{Threads: 2} + blendedset := blendedfake.NewSimpleClientset() + informer := blendedinformers.NewSharedInformerFactory(blendedset, 0) - controller := NewController(ctx, client) - assert.NotNil(t, controller) + controller := NewController(blendedset, informer.Inwinstack().V1().Pools()) + go informer.Start(ctx.Done()) + assert.Nil(t, controller.Run(ctx, cfg.Threads)) - // Test onAdd - pool := &inwinv1.Pool{ + pool := &blendedv1.Pool{ ObjectMeta: metav1.ObjectMeta{ Name: "test-pool", }, - Spec: inwinv1.PoolSpec{ + Spec: blendedv1.PoolSpec{ Addresses: []string{"172.22.132.0-172.22.132.5"}, AssignToNamespace: false, AvoidBuggyIPs: true, @@ -60,35 +54,73 @@ func TestPoolController(t *testing.T) { }, } - createPool, err := client.InwinstackV1().Pools().Create(pool) + // Create the pool + _, err := blendedset.InwinstackV1().Pools().Create(pool) assert.Nil(t, err) - controller.onAdd(createPool) + failed := true + for start := time.Now(); time.Since(start) < timeout; { + p, err := blendedset.InwinstackV1().Pools().Get(pool.Name, metav1.GetOptions{}) + assert.Nil(t, err) + + if p.Status.Phase == blendedv1.PoolActive { + assert.Equal(t, []string{}, p.Status.AllocatedIPs) + assert.Equal(t, 5, p.Status.Capacity) + assert.Equal(t, 5, p.Status.Allocatable) + failed = false + break + } + } + assert.Equal(t, false, failed, "The pool object failed to make status.") - onAddPool, err := client.InwinstackV1().Pools().Get(pool.Name, metav1.GetOptions{}) + // Success to update the pool + gpool, err := blendedset.InwinstackV1().Pools().Get(pool.Name, metav1.GetOptions{}) assert.Nil(t, err) - assert.Equal(t, inwinv1.PoolActive, onAddPool.Status.Phase) - assert.Equal(t, []string{}, onAddPool.Status.AllocatedIPs) - assert.Equal(t, 5, onAddPool.Status.Capacity) - assert.Equal(t, 5, onAddPool.Status.Allocatable) - - // Test onUpdate - onAddPool.Spec.Addresses = append(onAddPool.Spec.Addresses, "172.22.132.250-172.22.132.255") - controller.onUpdate(createPool, onAddPool) - onUpdatePool, err := client.InwinstackV1().Pools().Get(onAddPool.Name, metav1.GetOptions{}) + gpool.Spec.Addresses = append(gpool.Spec.Addresses, "172.22.132.250-172.22.132.255") + _, err = blendedset.InwinstackV1().Pools().Update(gpool) assert.Nil(t, err) - assert.Equal(t, 10, onUpdatePool.Status.Capacity) - assert.Equal(t, 10, onUpdatePool.Status.Allocatable) - // Test onUpdate failed - onUpdatePool.Spec.Addresses = []string{"172.22.132.250-172.22.132.267"} - controller.onUpdate(onAddPool, onUpdatePool) + failed = true + for start := time.Now(); time.Since(start) < timeout; { + p, err := blendedset.InwinstackV1().Pools().Get(pool.Name, metav1.GetOptions{}) + assert.Nil(t, err) + + if p.Status.Capacity == 10 { + assert.Equal(t, 10, p.Status.Allocatable) + failed = false + break + } + } + assert.Equal(t, false, failed, "The service object failed to sync status.") + + // Failed to update the pool + gpool, err = blendedset.InwinstackV1().Pools().Get(pool.Name, metav1.GetOptions{}) + assert.Nil(t, err) + gpool.Spec.Addresses = []string{"172.22.132.250-172.22.132.267"} - onUpdateFailedPool, err := client.InwinstackV1().Pools().Get(onAddPool.Name, metav1.GetOptions{}) + _, err = blendedset.InwinstackV1().Pools().Update(gpool) assert.Nil(t, err) - assert.Equal(t, inwinv1.PoolFailed, onUpdateFailedPool.Status.Phase) - assert.Equal(t, 0, onUpdateFailedPool.Status.Capacity) - assert.Equal(t, 0, onUpdateFailedPool.Status.Allocatable) - assert.NotNil(t, onUpdateFailedPool.Status.Reason) + + failed = true + for start := time.Now(); time.Since(start) < timeout; { + p, err := blendedset.InwinstackV1().Pools().Get(pool.Name, metav1.GetOptions{}) + assert.Nil(t, err) + + if p.Status.Phase == blendedv1.PoolFailed { + assert.NotNil(t, p.Status.Reason) + failed = false + break + } + } + assert.Equal(t, false, failed, "The service object failed to get error status.") + + // Delete the pool + assert.Nil(t, blendedset.InwinstackV1().Pools().Delete(pool.Name, nil)) + + _, err = blendedset.InwinstackV1().Pools().Get(pool.Name, metav1.GetOptions{}) + assert.NotNil(t, err) + + cancel() + controller.Stop() } diff --git a/pkg/util/net_test.go b/pkg/util/net_test.go deleted file mode 100644 index ca713e7..0000000 --- a/pkg/util/net_test.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright © 2018 inwinSTACK.inc - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package util - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNetworkParser(t *testing.T) { - tests := []struct { - NetworkParser *NetworkParser - IPs []string - }{ - { - NetworkParser: NewNetworkParser([]string{"172.22.132.0/30"}, true, true), - IPs: []string{"172.22.132.2", "172.22.132.3"}, - }, - { - NetworkParser: NewNetworkParser([]string{"172.22.132.0/30"}, true, false), - IPs: []string{"172.22.132.1", "172.22.132.2", "172.22.132.3"}, - }, - { - NetworkParser: NewNetworkParser([]string{"172.22.132.0/30"}, false, true), - IPs: []string{"172.22.132.0", "172.22.132.2", "172.22.132.3"}, - }, - { - NetworkParser: NewNetworkParser([]string{"172.22.132.0/30"}, false, false), - IPs: []string{"172.22.132.0", "172.22.132.1", "172.22.132.2", "172.22.132.3"}, - }, - } - - for _, test := range tests { - ips, err := test.NetworkParser.IPs() - assert.Nil(t, err) - assert.Equal(t, test.IPs, ips) - } -} diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go deleted file mode 100644 index 360b764..0000000 --- a/pkg/util/util_test.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright © 2018 inwinSTACK.inc - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package util - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/assert" -) - -func errorGenerator(n int, retryable bool) func() error { - errorCount := 0 - return func() (err error) { - if errorCount < n { - errorCount++ - e := errors.New("testing error") - if retryable { - return &RetriableError{Err: e} - } - return e - } - return nil - } -} - -func TestRetry(t *testing.T) { - f := errorGenerator(4, true) - if err := Retry(f, 1, 5); err != nil { - assert.Fail(t, "Error should not have been raised by retry.") - } - - f = errorGenerator(5, true) - if err := Retry(f, 1, 4); err == nil { - assert.Fail(t, "Error should have been raised by retry.") - } -} diff --git a/pkg/version/version.go b/pkg/version/version.go index 219d167..1aa0492 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -1,5 +1,5 @@ /* -Copyright © 2018 inwinSTACK.inc +Copyright © 2018 inwinSTACK Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/k8sutil/k8sutil.go b/pkg/version/version_test.go similarity index 56% rename from pkg/k8sutil/k8sutil.go rename to pkg/version/version_test.go index 69a7983..a89633b 100644 --- a/pkg/k8sutil/k8sutil.go +++ b/pkg/version/version_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2018 inwinSTACK.inc +Copyright © 2018 inwinSTACK Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,26 +13,17 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ - -package k8sutil +package version import ( - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" + "testing" + + "github.com/stretchr/testify/assert" ) -func GetRestConfig(kubeconfig string) (*rest.Config, error) { - if kubeconfig != "" { - cfg, err := clientcmd.BuildConfigFromFlags("master", kubeconfig) - if err != nil { - return nil, err - } - return cfg, nil - } +func TestGetVersion(t *testing.T) { + assert.Equal(t, "v0.0.0-unset", GetVersion()) - cfg, err := rest.InClusterConfig() - if err != nil { - return nil, err - } - return cfg, nil + version = "v0.1.0" + assert.Equal(t, "v0.1.0", GetVersion()) }