diff --git a/postgres-kube-builder/.gitignore b/postgres-kube-builder/.gitignore new file mode 100644 index 0000000..d97ffc5 --- /dev/null +++ b/postgres-kube-builder/.gitignore @@ -0,0 +1,24 @@ + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +bin + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Kubernetes Generated files - skip generated files, except for vendored files + +!vendor/**/zz_generated.* + +# editor and IDE paraphernalia +.idea +*.swp +*.swo +*~ diff --git a/postgres-kube-builder/Dockerfile b/postgres-kube-builder/Dockerfile new file mode 100644 index 0000000..3c10090 --- /dev/null +++ b/postgres-kube-builder/Dockerfile @@ -0,0 +1,17 @@ +# Build the manager binary +FROM golang:1.10.3 as builder + +# Copy in the go src +WORKDIR /go/src/labs/postgres-kube-builder +COPY pkg/ pkg/ +COPY cmd/ cmd/ +COPY vendor/ vendor/ + +# Build +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager labs/postgres-kube-builder/cmd/manager + +# Copy the controller-manager into a thin image +FROM ubuntu:latest +WORKDIR /root/ +COPY --from=builder /go/src/labs/postgres-kube-builder/manager . +ENTRYPOINT ["./manager"] diff --git a/postgres-kube-builder/Gopkg.lock b/postgres-kube-builder/Gopkg.lock new file mode 100644 index 0000000..1f3086d --- /dev/null +++ b/postgres-kube-builder/Gopkg.lock @@ -0,0 +1,938 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:e4549155be72f065cf860ada7148bbeb0857360e81da2d5e28b799bd8720f1bc" + name = "cloud.google.com/go" + packages = ["compute/metadata"] + pruneopts = "T" + revision = "0ebda48a7f143b1cce9eb37a8c1106ac762a3430" + version = "v0.34.0" + +[[projects]] + branch = "master" + digest = "1:ad4589ec239820ee99eb01c1ad47ebc5f8e02c4f5103a9b210adff9696d89f36" + name = "github.com/beorn7/perks" + packages = ["quantile"] + pruneopts = "T" + revision = "3a771d992973f24aa725d07868b467d1ddfceafb" + +[[projects]] + digest = "1:9f42202ac457c462ad8bb9642806d275af9ab4850cf0b1960b9c6f083d4a309a" + name = "github.com/davecgh/go-spew" + packages = ["spew"] + pruneopts = "T" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" + +[[projects]] + digest = "1:0ffd93121f3971aea43f6a26b3eaaa64c8af20fb0ff0731087d8dab7164af5a8" + name = "github.com/emicklei/go-restful" + packages = [ + ".", + "log", + ] + pruneopts = "T" + revision = "3eb9738c1697594ea6e71a7156a9bb32ed216cf0" + version = "v2.8.0" + +[[projects]] + digest = "1:2cd7915ab26ede7d95b8749e6b1f933f1c6d5398030684e6505940a10f31cfda" + name = "github.com/ghodss/yaml" + packages = ["."] + pruneopts = "T" + revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" + version = "v1.0.0" + +[[projects]] + branch = "master" + digest = "1:65587005c6fa4293c0b8a2e457e689df7fda48cc5e1f5449ea2c1e7784551558" + name = "github.com/go-logr/logr" + packages = ["."] + pruneopts = "T" + revision = "9fb12b3b21c5415d16ac18dc5cd42c1cfdd40c4e" + +[[projects]] + digest = "1:ce43ad4015e7cdad3f0e8f2c8339439dd4470859a828d2a6988b0f713699e94a" + name = "github.com/go-logr/zapr" + packages = ["."] + pruneopts = "T" + revision = "7536572e8d55209135cd5e7ccf7fce43dca217ab" + version = "v0.1.0" + +[[projects]] + digest = "1:3e34b0a53d651b06655475eb488745c912596e08557a33b83d336aab41cb6fe9" + name = "github.com/gobuffalo/envy" + packages = ["."] + pruneopts = "T" + revision = "801d7253ade1f895f74596b9a96147ed2d3b087e" + version = "v1.6.11" + +[[projects]] + digest = "1:f5ccd717b5f093cbabc51ee2e7a5979b92f17d217f9031d6d64f337101c408e4" + name = "github.com/gogo/protobuf" + packages = [ + "proto", + "sortkeys", + ] + pruneopts = "T" + revision = "4cbf7e384e768b4e01799441fdf2a706a5635ae7" + version = "v1.2.0" + +[[projects]] + branch = "master" + digest = "1:1ba1d79f2810270045c328ae5d674321db34e3aae468eb4233883b473c5c0467" + name = "github.com/golang/glog" + packages = ["."] + pruneopts = "T" + revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" + +[[projects]] + branch = "master" + digest = "1:8f3489cb7352125027252a6517757cbd1706523119f1e14e20741ae8d2f70428" + name = "github.com/golang/groupcache" + packages = ["lru"] + pruneopts = "T" + revision = "c65c006176ff7ff98bb916961c7abbc6b0afc0aa" + +[[projects]] + digest = "1:a2ecb56e5053d942aafc86738915fb94c9131bac848c543b8b6764365fd69080" + name = "github.com/golang/protobuf" + packages = [ + "proto", + "ptypes", + "ptypes/any", + "ptypes/duration", + "ptypes/timestamp", + ] + pruneopts = "T" + revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" + version = "v1.2.0" + +[[projects]] + branch = "master" + digest = "1:0bfbe13936953a98ae3cfe8ed6670d396ad81edf069a806d2f6515d7bb6950df" + name = "github.com/google/btree" + packages = ["."] + pruneopts = "T" + revision = "4030bb1f1f0c35b30ca7009e9ebd06849dd45306" + +[[projects]] + branch = "master" + digest = "1:3ee90c0d94da31b442dde97c99635aaafec68d0b8a3c12ee2075c6bdabeec6bb" + name = "github.com/google/gofuzz" + packages = ["."] + pruneopts = "T" + revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1" + +[[projects]] + digest = "1:236d7e1bdb50d8f68559af37dbcf9d142d56b431c9b2176d41e2a009b664cda8" + name = "github.com/google/uuid" + packages = ["."] + pruneopts = "T" + revision = "9b3b1e0f5f99ae461456d768e7d301a7acdaa2d8" + version = "v1.1.0" + +[[projects]] + digest = "1:35735e2255fa34521c2a1355fb2a3a2300bc9949f487be1c1ce8ee8efcfa2d04" + name = "github.com/googleapis/gnostic" + packages = [ + "OpenAPIv2", + "compiler", + "extensions", + ] + pruneopts = "T" + revision = "7c663266750e7d82587642f65e60bc4083f1f84e" + version = "v0.2.0" + +[[projects]] + branch = "master" + digest = "1:8c0ceab65d43f49dce22aac0e8f670c170fc74dcf2dfba66d3a89516f7ae2c15" + name = "github.com/gregjones/httpcache" + packages = [ + ".", + "diskcache", + ] + pruneopts = "T" + revision = "c63ab54fda8f77302f8d414e19933f2b6026a089" + +[[projects]] + digest = "1:8ec8d88c248041a6df5f6574b87bc00e7e0b493881dad2e7ef47b11dc69093b5" + name = "github.com/hashicorp/golang-lru" + packages = [ + ".", + "simplelru", + ] + pruneopts = "T" + revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768" + version = "v0.5.0" + +[[projects]] + digest = "1:8f20c8dd713564fa97299fbcb77d729c6de9c33f3222812a76e6ecfaef80fd61" + name = "github.com/hpcloud/tail" + packages = [ + ".", + "ratelimiter", + "util", + "watch", + "winfile", + ] + pruneopts = "T" + revision = "a30252cb686a21eb2d0b98132633053ec2f7f1e5" + version = "v1.0.0" + +[[projects]] + digest = "1:3477d9dd8c135faab978bac762eaeafb31f28d6da97ef500d5c271966f74140a" + name = "github.com/imdario/mergo" + packages = ["."] + pruneopts = "T" + revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4" + version = "v0.3.6" + +[[projects]] + digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" + name = "github.com/inconshreveable/mousetrap" + packages = ["."] + pruneopts = "T" + revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" + version = "v1.0" + +[[projects]] + digest = "1:ee5840274624ad20cac08227aeca582fbdaf3727ef9dd37ae935706240679e09" + name = "github.com/joho/godotenv" + packages = ["."] + pruneopts = "T" + revision = "23d116af351c84513e1946b527c88823e476be13" + version = "v1.3.0" + +[[projects]] + digest = "1:5d713dbcad44f3358fec51fd5573d4f733c02cac5a40dcb177787ad5ffe9272f" + name = "github.com/json-iterator/go" + packages = ["."] + pruneopts = "T" + revision = "1624edc4454b8682399def8740d46db5e4362ba4" + version = "v1.1.5" + +[[projects]] + digest = "1:3804a3a02964db8e6db3e5e7960ac1c1a9b12835642dd4f4ac4e56c749ec73eb" + name = "github.com/markbates/inflect" + packages = ["."] + pruneopts = "T" + revision = "24b83195037b3bc61fcda2d28b7b0518bce293b6" + version = "v1.0.4" + +[[projects]] + branch = "master" + digest = "1:fc2b04b0069d6b10bdef96d278fe20c345794009685ed3c8c7f1a6dc023eefec" + name = "github.com/mattbaird/jsonpatch" + packages = ["."] + pruneopts = "T" + revision = "81af80346b1a01caae0cbc27fd3c1ba5b11e189f" + +[[projects]] + digest = "1:a8e3d14801bed585908d130ebfc3b925ba642208e6f30d879437ddfc7bb9b413" + name = "github.com/matttproud/golang_protobuf_extensions" + packages = ["pbutil"] + pruneopts = "T" + revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" + version = "v1.0.1" + +[[projects]] + digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563" + name = "github.com/modern-go/concurrent" + packages = ["."] + pruneopts = "T" + revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" + version = "1.0.3" + +[[projects]] + digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" + name = "github.com/modern-go/reflect2" + packages = ["."] + pruneopts = "T" + revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" + version = "1.0.1" + +[[projects]] + digest = "1:1de2ef6996903caab4bcf41f7885aa276d8e20076ba52ca80b2c6c32efb9c5f5" + name = "github.com/onsi/ginkgo" + packages = [ + ".", + "config", + "internal/codelocation", + "internal/containernode", + "internal/failer", + "internal/leafnodes", + "internal/remote", + "internal/spec", + "internal/spec_iterator", + "internal/specrunner", + "internal/suite", + "internal/testingtproxy", + "internal/writer", + "reporters", + "reporters/stenographer", + "reporters/stenographer/support/go-colorable", + "reporters/stenographer/support/go-isatty", + "types", + ] + pruneopts = "T" + revision = "2e1be8f7d90e9d3e3e58b0ce470f2f14d075406f" + version = "v1.7.0" + +[[projects]] + digest = "1:4f40f5e1925c58d5f9b6a953d1b02f475a6be704e9b5f1a21df6c62c434bde13" + name = "github.com/onsi/gomega" + packages = [ + ".", + "format", + "gbytes", + "gexec", + "internal/assertion", + "internal/asyncassertion", + "internal/oraclematcher", + "internal/testingtsupport", + "matchers", + "matchers/support/goraph/bipartitegraph", + "matchers/support/goraph/edge", + "matchers/support/goraph/node", + "matchers/support/goraph/util", + "types", + ] + pruneopts = "T" + revision = "65fb64232476ad9046e57c26cd0bff3d3a8dc6cd" + version = "v1.4.3" + +[[projects]] + digest = "1:e5d0bd87abc2781d14e274807a470acd180f0499f8bf5bb18606e9ec22ad9de9" + name = "github.com/pborman/uuid" + packages = ["."] + pruneopts = "T" + revision = "adf5a7427709b9deb95d29d3fa8a2bf9cfd388f1" + version = "v1.2" + +[[projects]] + branch = "master" + digest = "1:0c29d499ffc3b9f33e7136444575527d0c3a9463a89b3cbeda0523b737f910b3" + name = "github.com/petar/GoLLRB" + packages = ["llrb"] + pruneopts = "T" + revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" + +[[projects]] + digest = "1:598241bd36d3a5f6d9102a306bd9bf78f3bc253672460d92ac70566157eae648" + name = "github.com/peterbourgon/diskv" + packages = ["."] + pruneopts = "T" + revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" + version = "v2.0.1" + +[[projects]] + digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" + name = "github.com/pkg/errors" + packages = ["."] + pruneopts = "T" + revision = "645ef00459ed84a119197bfb8d8205042c6df63d" + version = "v0.8.0" + +[[projects]] + digest = "1:3b5729e3fc486abc6fc16ce026331c3d196e788c3b973081ecf5d28ae3e1050d" + name = "github.com/prometheus/client_golang" + packages = [ + "prometheus", + "prometheus/internal", + "prometheus/promhttp", + ] + pruneopts = "T" + revision = "505eaef017263e299324067d40ca2c48f6a2cf50" + version = "v0.9.2" + +[[projects]] + branch = "master" + digest = "1:185cf55b1f44a1bf243558901c3f06efa5c64ba62cfdcbb1bf7bbe8c3fb68561" + name = "github.com/prometheus/client_model" + packages = ["go"] + pruneopts = "T" + revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f" + +[[projects]] + branch = "master" + digest = "1:af934fb00202ba000f356e210fe42a61527fe9045d29bc5eaa57d0d1b5076963" + name = "github.com/prometheus/common" + packages = [ + "expfmt", + "internal/bitbucket.org/ww/goautoneg", + "model", + ] + pruneopts = "T" + revision = "4724e9255275ce38f7179b2478abeae4e28c904f" + +[[projects]] + branch = "master" + digest = "1:23dfc492513f6cd72a51d20c57a3bebb9b9ac4c81d27a474b1b49e33b79c4894" + name = "github.com/prometheus/procfs" + packages = [ + ".", + "internal/util", + "nfs", + "xfs", + ] + pruneopts = "T" + revision = "1dc9a6cbc91aacc3e8b2d63db4d2e957a5394ac4" + +[[projects]] + digest = "1:fac620096bacf6e41b3beb93ff03af9fe545d77421b3e7320f114b2da2e10824" + name = "github.com/rogpeppe/go-internal" + packages = [ + "modfile", + "module", + "semver", + ] + pruneopts = "T" + revision = "d87f08a7d80821c797ffc8eb8f4e01675f378736" + version = "v1.0.0" + +[[projects]] + digest = "1:b7bf9fd95d38ebe6726a63b7d0320611f7c920c64e2c8313eba0cec51926bf55" + name = "github.com/spf13/afero" + packages = [ + ".", + "mem", + ] + pruneopts = "T" + revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd" + version = "v1.1.2" + +[[projects]] + digest = "1:8be8b3743fc9795ec21bbd3e0fc28ff6234018e1a269b0a7064184be95ac13e0" + name = "github.com/spf13/cobra" + packages = ["."] + pruneopts = "T" + revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385" + version = "v0.0.3" + +[[projects]] + digest = "1:0f775ea7a72e30d5574267692aaa9ff265aafd15214a7ae7db26bc77f2ca04dc" + name = "github.com/spf13/pflag" + packages = ["."] + pruneopts = "T" + revision = "298182f68c66c05229eb03ac171abe6e309ee79a" + version = "v1.0.3" + +[[projects]] + digest = "1:365b8ecb35a5faf5aa0ee8d798548fc9cd4200cb95d77a5b0b285ac881bae499" + name = "go.uber.org/atomic" + packages = ["."] + pruneopts = "T" + revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289" + version = "v1.3.2" + +[[projects]] + digest = "1:8da5d356e645cc806b953f35966e9185e9f3817c6cee54f9b33ca602544ee434" + name = "go.uber.org/multierr" + packages = ["."] + pruneopts = "T" + revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a" + version = "v1.1.0" + +[[projects]] + digest = "1:c57d4b95825e74f8eb3b0c851d04aac3626a602de613f8702c0de41de26856b6" + name = "go.uber.org/zap" + packages = [ + ".", + "buffer", + "internal/bufferpool", + "internal/color", + "internal/exit", + "zapcore", + ] + pruneopts = "T" + revision = "ff33455a0e382e8a81d14dd7c922020b6b5e7982" + version = "v1.9.1" + +[[projects]] + branch = "master" + digest = "1:d470cb69884835b1800e93ceceb85afcf981ea647e61d99398a76af7a95bad6a" + name = "golang.org/x/crypto" + packages = ["ssh/terminal"] + pruneopts = "T" + revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447" + +[[projects]] + branch = "master" + digest = "1:134fd2b5998199ca103bdcd2920be488fffbbf307fec71d158b773694a8a2080" + name = "golang.org/x/net" + packages = [ + "context", + "context/ctxhttp", + "html", + "html/atom", + "html/charset", + "http/httpguts", + "http2", + "http2/hpack", + "idna", + ] + pruneopts = "T" + revision = "610586996380ceef02dd726cc09df7e00a3f8e56" + +[[projects]] + branch = "master" + digest = "1:320e5ba9ea8000060bec710764b8b26c251ee28f6012422b669cb8cb100c9815" + name = "golang.org/x/oauth2" + packages = [ + ".", + "google", + "internal", + "jws", + "jwt", + ] + pruneopts = "T" + revision = "d668ce993890a79bda886613ee587a69dd5da7a6" + +[[projects]] + branch = "master" + digest = "1:e5fbd96c8de0c2e83829cbb0a48972def049e7d31424778d083065fd10dc2ca6" + name = "golang.org/x/sys" + packages = [ + "unix", + "windows", + ] + pruneopts = "T" + revision = "b05ddf57801d2239d6ab0ee35f9d981e0420f4ac" + +[[projects]] + digest = "1:6164911cb5e94e8d8d5131d646613ff82c14f5a8ce869de2f6d80d9889df8c5a" + name = "golang.org/x/text" + packages = [ + "collate", + "collate/build", + "encoding", + "encoding/charmap", + "encoding/htmlindex", + "encoding/internal", + "encoding/internal/identifier", + "encoding/japanese", + "encoding/korean", + "encoding/simplifiedchinese", + "encoding/traditionalchinese", + "encoding/unicode", + "internal/colltab", + "internal/gen", + "internal/tag", + "internal/triegen", + "internal/ucd", + "internal/utf8internal", + "language", + "runes", + "secure/bidirule", + "transform", + "unicode/bidi", + "unicode/cldr", + "unicode/norm", + "unicode/rangetable", + ] + pruneopts = "T" + revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" + version = "v0.3.0" + +[[projects]] + branch = "master" + digest = "1:077216d94c076b8cd7bd057cb6f7c6d224970cc991bdfe49c0c7a24e8e39ee33" + name = "golang.org/x/time" + packages = ["rate"] + pruneopts = "T" + revision = "85acf8d2951cb2a3bde7632f9ff273ef0379bcbd" + +[[projects]] + branch = "master" + digest = "1:90eddec4c9c92906064635dd4bcbe8646ac31736480ed7b9b40b1a65bd27627c" + name = "golang.org/x/tools" + packages = [ + "go/ast/astutil", + "imports", + "internal/fastwalk", + "internal/gopathwalk", + ] + pruneopts = "T" + revision = "49db546f375eee2dbfef045773161e6a4ea59c2f" + +[[projects]] + digest = "1:fb4ee7f835110d238c58366cfa442da7dfa62cec87d42da2d8049000f4330758" + name = "google.golang.org/appengine" + packages = [ + ".", + "internal", + "internal/app_identity", + "internal/base", + "internal/datastore", + "internal/log", + "internal/modules", + "internal/remote_api", + "internal/urlfetch", + "urlfetch", + ] + pruneopts = "T" + revision = "4a4468ece617fc8205e99368fa2200e9d1fad421" + version = "v1.3.0" + +[[projects]] + digest = "1:7fc160b460a6fc506b37fcca68332464c3f2cd57b6e3f111f26c5bbfd2d5518e" + name = "gopkg.in/fsnotify.v1" + packages = ["."] + pruneopts = "T" + revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" + source = "https://github.com/fsnotify/fsnotify.git" + version = "v1.4.7" + +[[projects]] + digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a" + name = "gopkg.in/inf.v0" + packages = ["."] + pruneopts = "T" + revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf" + version = "v0.9.1" + +[[projects]] + branch = "v1" + digest = "1:0caa92e17bc0b65a98c63e5bc76a9e844cd5e56493f8fdbb28fad101a16254d9" + name = "gopkg.in/tomb.v1" + packages = ["."] + pruneopts = "T" + revision = "dd632973f1e7218eb1089048e0798ec9ae7dceb8" + +[[projects]] + digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" + name = "gopkg.in/yaml.v2" + packages = ["."] + pruneopts = "T" + revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" + version = "v2.2.2" + +[[projects]] + digest = "1:8a1af4b4d5b51e3f8d6ec2a44467e8505e25e29814c2557d1d9717834065c674" + name = "k8s.io/api" + packages = [ + "admission/v1beta1", + "admissionregistration/v1alpha1", + "admissionregistration/v1beta1", + "apps/v1", + "apps/v1beta1", + "apps/v1beta2", + "authentication/v1", + "authentication/v1beta1", + "authorization/v1", + "authorization/v1beta1", + "autoscaling/v1", + "autoscaling/v2beta1", + "autoscaling/v2beta2", + "batch/v1", + "batch/v1beta1", + "batch/v2alpha1", + "certificates/v1beta1", + "coordination/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 = "T" + revision = "b503174bad5991eb66f18247f52e41c3258f6348" + version = "kubernetes-1.12.3" + +[[projects]] + digest = "1:3d0ba42a7eaed76c293905ced94201752762c84481f76505ac2079e6f332ae2b" + name = "k8s.io/apiextensions-apiserver" + packages = [ + "pkg/apis/apiextensions", + "pkg/apis/apiextensions/v1beta1", + ] + pruneopts = "T" + revision = "0cd23ebeb6882bd1cdc2cb15fc7b2d72e8a86a5b" + version = "kubernetes-1.12.3" + +[[projects]] + digest = "1:bcf693d144a1d98dabdb4ab3bf18fbde8b2f7ee86d2fd8a3f74fb6a035627550" + 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/naming", + "pkg/util/net", + "pkg/util/runtime", + "pkg/util/sets", + "pkg/util/strategicpatch", + "pkg/util/uuid", + "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 = "T" + revision = "eddba98df674a16931d2d4ba75edc3a389bf633a" + version = "kubernetes-1.12.3" + +[[projects]] + digest = "1:5aa94390c366adc57a82b19ad4f99482ab8fbeca0a2ff7b09db12d7baf590e46" + name = "k8s.io/client-go" + packages = [ + "discovery", + "dynamic", + "kubernetes", + "kubernetes/scheme", + "kubernetes/typed/admissionregistration/v1alpha1", + "kubernetes/typed/admissionregistration/v1beta1", + "kubernetes/typed/apps/v1", + "kubernetes/typed/apps/v1beta1", + "kubernetes/typed/apps/v1beta2", + "kubernetes/typed/authentication/v1", + "kubernetes/typed/authentication/v1beta1", + "kubernetes/typed/authorization/v1", + "kubernetes/typed/authorization/v1beta1", + "kubernetes/typed/autoscaling/v1", + "kubernetes/typed/autoscaling/v2beta1", + "kubernetes/typed/autoscaling/v2beta2", + "kubernetes/typed/batch/v1", + "kubernetes/typed/batch/v1beta1", + "kubernetes/typed/batch/v2alpha1", + "kubernetes/typed/certificates/v1beta1", + "kubernetes/typed/coordination/v1beta1", + "kubernetes/typed/core/v1", + "kubernetes/typed/events/v1beta1", + "kubernetes/typed/extensions/v1beta1", + "kubernetes/typed/networking/v1", + "kubernetes/typed/policy/v1beta1", + "kubernetes/typed/rbac/v1", + "kubernetes/typed/rbac/v1alpha1", + "kubernetes/typed/rbac/v1beta1", + "kubernetes/typed/scheduling/v1alpha1", + "kubernetes/typed/scheduling/v1beta1", + "kubernetes/typed/settings/v1alpha1", + "kubernetes/typed/storage/v1", + "kubernetes/typed/storage/v1alpha1", + "kubernetes/typed/storage/v1beta1", + "pkg/apis/clientauthentication", + "pkg/apis/clientauthentication/v1alpha1", + "pkg/apis/clientauthentication/v1beta1", + "pkg/version", + "plugin/pkg/client/auth/exec", + "plugin/pkg/client/auth/gcp", + "rest", + "rest/watch", + "restmapper", + "third_party/forked/golang/template", + "tools/auth", + "tools/cache", + "tools/clientcmd", + "tools/clientcmd/api", + "tools/clientcmd/api/latest", + "tools/clientcmd/api/v1", + "tools/leaderelection", + "tools/leaderelection/resourcelock", + "tools/metrics", + "tools/pager", + "tools/record", + "tools/reference", + "transport", + "util/buffer", + "util/cert", + "util/connrotation", + "util/flowcontrol", + "util/homedir", + "util/integer", + "util/jsonpath", + "util/retry", + "util/workqueue", + ] + pruneopts = "T" + revision = "d082d5923d3cc0bfbb066ee5fbdea3d0ca79acf8" + version = "kubernetes-1.12.3" + +[[projects]] + branch = "master" + digest = "1:b11533d0d337c776e3ee4d59f86ca302a66a777b40a289d97cb59a638179f31e" + name = "k8s.io/code-generator" + packages = [ + "cmd/client-gen", + "cmd/client-gen/args", + "cmd/client-gen/generators", + "cmd/client-gen/generators/fake", + "cmd/client-gen/generators/scheme", + "cmd/client-gen/generators/util", + "cmd/client-gen/path", + "cmd/client-gen/types", + "cmd/deepcopy-gen", + "cmd/deepcopy-gen/args", + "pkg/namer", + "pkg/util", + ] + pruneopts = "T" + revision = "3a2206dd6a78497deceb3ae058417fdeb2036c7e" + +[[projects]] + branch = "master" + digest = "1:c5636f57f83eb7ef2a1c7a2340b7e5750f1c105a1d0cbcfcab429d175c216773" + name = "k8s.io/gengo" + packages = [ + "args", + "examples/deepcopy-gen/generators", + "examples/set-gen/sets", + "generator", + "namer", + "parser", + "types", + ] + pruneopts = "T" + revision = "fd15ee9cc2f77baa4f31e59e6acbf21146455073" + +[[projects]] + digest = "1:7a3ef99d492d30157b8e933624a8f0292b4cee5934c23269f7640c8030eb83cd" + name = "k8s.io/klog" + packages = ["."] + pruneopts = "T" + revision = "a5bc97fbc634d635061f3146511332c7e313a55a" + version = "v0.1.0" + +[[projects]] + branch = "master" + digest = "1:90f16a49f856e6d94089444e487c535f4cd41f59a1e90c51deb9dcf965f3c50b" + name = "k8s.io/kube-openapi" + packages = ["pkg/util/proto"] + pruneopts = "T" + revision = "0317810137be915b9cf888946c6e115c1bfac693" + +[[projects]] + digest = "1:63405fd0a9cf28d074f8ef07f133f818ee633ae874935a1ed52f73c36564dce9" + name = "sigs.k8s.io/controller-runtime" + packages = [ + "pkg/cache", + "pkg/cache/internal", + "pkg/client", + "pkg/client/apiutil", + "pkg/client/config", + "pkg/controller", + "pkg/event", + "pkg/handler", + "pkg/internal/controller", + "pkg/internal/controller/metrics", + "pkg/internal/recorder", + "pkg/leaderelection", + "pkg/manager", + "pkg/metrics", + "pkg/patch", + "pkg/predicate", + "pkg/reconcile", + "pkg/recorder", + "pkg/runtime/inject", + "pkg/runtime/log", + "pkg/runtime/signals", + "pkg/source", + "pkg/source/internal", + "pkg/webhook", + "pkg/webhook/admission", + "pkg/webhook/admission/types", + "pkg/webhook/internal/cert", + "pkg/webhook/internal/cert/generator", + "pkg/webhook/internal/cert/writer", + "pkg/webhook/internal/cert/writer/atomic", + "pkg/webhook/types", + ] + pruneopts = "T" + revision = "c63ebda0bf4be5f0a8abd4003e4ea546032545ba" + version = "v0.1.8" + +[[projects]] + digest = "1:2518c407c98521579bcff2ca99dca0797cae36120bc47c32c7c929cf6c13a76a" + name = "sigs.k8s.io/controller-tools" + packages = [ + "cmd/controller-gen", + "pkg/crd/generator", + "pkg/crd/util", + "pkg/generate/rbac", + "pkg/generate/webhook", + "pkg/generate/webhook/internal", + "pkg/internal/codegen", + "pkg/internal/codegen/parse", + "pkg/internal/general", + "pkg/util", + ] + pruneopts = "T" + revision = "b072ef59824b16023b0e12c94d0040d99059a961" + version = "v0.1.7" + +[[projects]] + digest = "1:fd7fb0f1f6e3259ec03c9730c675f7c97bcbd9d2a1037364734b197eaa76eee7" + name = "sigs.k8s.io/testing_frameworks" + packages = [ + "integration", + "integration/internal", + ] + pruneopts = "T" + revision = "5818a3a284a11812aaed11d5ca0bcadec2c50e83" + version = "v0.1.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/emicklei/go-restful", + "github.com/onsi/ginkgo", + "github.com/onsi/gomega", + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1", + "k8s.io/apimachinery/pkg/runtime", + "k8s.io/client-go/plugin/pkg/client/auth/gcp", + "k8s.io/code-generator/cmd/client-gen", + "k8s.io/code-generator/cmd/deepcopy-gen", + "sigs.k8s.io/controller-runtime/pkg/client/config", + "sigs.k8s.io/controller-runtime/pkg/controller", + "sigs.k8s.io/controller-runtime/pkg/handler", + "sigs.k8s.io/controller-runtime/pkg/manager", + "sigs.k8s.io/controller-runtime/pkg/runtime/log", + "sigs.k8s.io/controller-runtime/pkg/runtime/signals", + "sigs.k8s.io/controller-runtime/pkg/source", + "sigs.k8s.io/controller-tools/cmd/controller-gen", + "sigs.k8s.io/testing_frameworks/integration", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/postgres-kube-builder/Gopkg.toml b/postgres-kube-builder/Gopkg.toml new file mode 100644 index 0000000..7cca82d --- /dev/null +++ b/postgres-kube-builder/Gopkg.toml @@ -0,0 +1,37 @@ +required = [ + "github.com/emicklei/go-restful", + "github.com/onsi/ginkgo", # for test framework + "github.com/onsi/gomega", # for test matchers + "k8s.io/client-go/plugin/pkg/client/auth/gcp", # for development against gcp + "k8s.io/code-generator/cmd/client-gen", # for go generate + "k8s.io/code-generator/cmd/deepcopy-gen", # for go generate + "sigs.k8s.io/controller-tools/cmd/controller-gen", # for crd/rbac generation + "sigs.k8s.io/controller-runtime/pkg/client/config", + "sigs.k8s.io/controller-runtime/pkg/controller", + "sigs.k8s.io/controller-runtime/pkg/handler", + "sigs.k8s.io/controller-runtime/pkg/manager", + "sigs.k8s.io/controller-runtime/pkg/runtime/signals", + "sigs.k8s.io/controller-runtime/pkg/source", + "sigs.k8s.io/testing_frameworks/integration", # for integration testing + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1", + ] + +[prune] + go-tests = true + + +# STANZAS BELOW ARE GENERATED AND MAY BE WRITTEN - DO NOT MODIFY BELOW THIS LINE. + +[[constraint]] + name="sigs.k8s.io/controller-runtime" + version="v0.1.1" + +[[constraint]] + name="sigs.k8s.io/controller-tools" + version="v0.1.1" + +# For dependency below: Refer to issue https://github.com/golang/dep/issues/1799 +[[override]] +name = "gopkg.in/fsnotify.v1" +source = "https://github.com/fsnotify/fsnotify.git" +version="v1.4.7" diff --git a/postgres-kube-builder/Makefile b/postgres-kube-builder/Makefile new file mode 100644 index 0000000..cbc9dca --- /dev/null +++ b/postgres-kube-builder/Makefile @@ -0,0 +1,52 @@ + +# Image URL to use all building/pushing image targets +IMG ?= controller:latest + +all: test manager + +# Run tests +test: generate fmt vet manifests + go test ./pkg/... ./cmd/... -coverprofile cover.out + +# Build manager binary +manager: generate fmt vet + go build -o bin/manager labs/postgres-kube-builder/cmd/manager + +# Run against the configured Kubernetes cluster in ~/.kube/config +run: generate fmt vet + go run ./cmd/manager/main.go + +# Install CRDs into a cluster +install: manifests + kubectl apply -f config/crds + +# Deploy controller in the configured Kubernetes cluster in ~/.kube/config +deploy: manifests + kubectl apply -f config/crds + kustomize build config/default | kubectl apply -f - + +# Generate manifests e.g. CRD, RBAC etc. +manifests: + go run vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go all + +# Run go fmt against code +fmt: + go fmt ./pkg/... ./cmd/... + +# Run go vet against code +vet: + go vet ./pkg/... ./cmd/... + +# Generate code +generate: + go generate ./pkg/... ./cmd/... + +# Build the docker image +docker-build: test + docker build . -t ${IMG} + @echo "updating kustomize image patch file for manager resource" + sed -i'' -e 's@image: .*@image: '"${IMG}"'@' ./config/default/manager_image_patch.yaml + +# Push the docker image +docker-push: + docker push ${IMG} diff --git a/postgres-kube-builder/PROJECT b/postgres-kube-builder/PROJECT new file mode 100644 index 0000000..b54fa94 --- /dev/null +++ b/postgres-kube-builder/PROJECT @@ -0,0 +1,3 @@ +version: "1" +domain: k8s.io +repo: labs/postgres-kube-builder diff --git a/postgres-kube-builder/cmd/manager/main.go b/postgres-kube-builder/cmd/manager/main.go new file mode 100644 index 0000000..00e9b45 --- /dev/null +++ b/postgres-kube-builder/cmd/manager/main.go @@ -0,0 +1,65 @@ +package main + +import ( + "os" + + "labs/postgres-kube-builder/pkg/apis" + "labs/postgres-kube-builder/pkg/controller" + "labs/postgres-kube-builder/pkg/webhook" + + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/controller-runtime/pkg/manager" + logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" + "sigs.k8s.io/controller-runtime/pkg/runtime/signals" +) + +func main() { + logf.SetLogger(logf.ZapLogger(false)) + log := logf.Log.WithName("entrypoint") + + // Get a config to talk to the apiserver + log.Info("setting up client for manager") + cfg, err := config.GetConfig() + if err != nil { + log.Error(err, "unable to set up client config") + os.Exit(1) + } + + // Create a new Cmd to provide shared dependencies and start components + log.Info("setting up manager") + mgr, err := manager.New(cfg, manager.Options{}) + if err != nil { + log.Error(err, "unable to set up overall controller manager") + os.Exit(1) + } + + log.Info("Registering Components.") + + // Setup Scheme for all resources + log.Info("setting up scheme") + if err := apis.AddToScheme(mgr.GetScheme()); err != nil { + log.Error(err, "unable add APIs to scheme") + os.Exit(1) + } + + // Setup all Controllers + log.Info("Setting up controller") + if err := controller.AddToManager(mgr); err != nil { + log.Error(err, "unable to register controllers to the manager") + os.Exit(1) + } + + log.Info("setting up webhooks") + if err := webhook.AddToManager(mgr); err != nil { + log.Error(err, "unable to register webhooks to the manager") + os.Exit(1) + } + + // Start the Cmd + log.Info("Starting the Cmd.") + if err := mgr.Start(signals.SetupSignalHandler()); err != nil { + log.Error(err, "unable to run the manager") + os.Exit(1) + } +} diff --git a/postgres-kube-builder/config/crds/kubeplus_v1_postgres.yaml b/postgres-kube-builder/config/crds/kubeplus_v1_postgres.yaml new file mode 100644 index 0000000..5cbe065 --- /dev/null +++ b/postgres-kube-builder/config/crds/kubeplus_v1_postgres.yaml @@ -0,0 +1,112 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + labels: + controller-tools.k8s.io: "1.0" + name: postgres.kubeplus.k8s.io +spec: + group: kubeplus.k8s.io + names: + kind: Postgres + plural: postgres + scope: Namespaced + validation: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + databases: + items: + type: string + type: array + deploymentName: + type: string + image: + type: string + initcommands: + items: + type: string + type: array + replicas: + format: int32 + type: integer + users: + items: + properties: + password: + type: string + username: + type: string + required: + - username + - password + type: object + type: array + required: + - deploymentName + - image + - replicas + - users + - databases + - initcommands + type: object + status: + properties: + actionHistory: + items: + type: string + type: array + availableReplicas: + format: int32 + type: integer + databases: + items: + type: string + type: array + serviceIP: + type: string + servicePort: + type: string + status: + type: string + users: + items: + properties: + password: + type: string + username: + type: string + required: + - username + - password + type: object + type: array + verifyCommand: + type: string + required: + - availableReplicas + - actionHistory + - users + - databases + - verifyCommand + - serviceIP + - servicePort + - status + type: object + required: + - metadata + - spec + version: v1 +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/postgres-kube-builder/config/default/kustomization.yaml b/postgres-kube-builder/config/default/kustomization.yaml new file mode 100644 index 0000000..6972dcb --- /dev/null +++ b/postgres-kube-builder/config/default/kustomization.yaml @@ -0,0 +1,33 @@ +# Adds namespace to all resources. +namespace: postgres-kube-builder-system + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +# Note that it should also match with the prefix (text before '-') of the namespace +# field above. +namePrefix: postgres-kube-builder- + +# Labels to add to all resources and selectors. +#commonLabels: +# someName: someValue + +# Each entry in this list must resolve to an existing +# resource definition in YAML. These are the resource +# files that kustomize reads, modifies and emits as a +# YAML string, with resources separated by document +# markers ("---"). +resources: +- ../rbac/rbac_role.yaml +- ../rbac/rbac_role_binding.yaml +- ../manager/manager.yaml + +patches: +- manager_image_patch.yaml + +vars: +- name: WEBHOOK_SECRET_NAME + objref: + kind: Secret + name: webhook-server-secret + apiVersion: v1 diff --git a/postgres-kube-builder/config/default/manager_image_patch.yaml b/postgres-kube-builder/config/default/manager_image_patch.yaml new file mode 100644 index 0000000..fcbf39d --- /dev/null +++ b/postgres-kube-builder/config/default/manager_image_patch.yaml @@ -0,0 +1,12 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + # Change the value of image field below to your controller image URL + - image: IMAGE_URL + name: manager diff --git a/postgres-kube-builder/config/manager/manager.yaml b/postgres-kube-builder/config/manager/manager.yaml new file mode 100644 index 0000000..5ca44d3 --- /dev/null +++ b/postgres-kube-builder/config/manager/manager.yaml @@ -0,0 +1,82 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + controller-tools.k8s.io: "1.0" + name: system +--- +apiVersion: v1 +kind: Service +metadata: + name: controller-manager-service + namespace: system + labels: + control-plane: controller-manager + controller-tools.k8s.io: "1.0" +spec: + selector: + control-plane: controller-manager + controller-tools.k8s.io: "1.0" + ports: + - port: 443 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: controller-manager + namespace: system + labels: + control-plane: controller-manager + controller-tools.k8s.io: "1.0" +spec: + selector: + matchLabels: + control-plane: controller-manager + controller-tools.k8s.io: "1.0" + serviceName: controller-manager-service + template: + metadata: + labels: + control-plane: controller-manager + controller-tools.k8s.io: "1.0" + spec: + containers: + - command: + - /root/manager + image: controller:latest + imagePullPolicy: Always + name: manager + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SECRET_NAME + value: $(WEBHOOK_SECRET_NAME) + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + ports: + - containerPort: 9876 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/cert + name: cert + readOnly: true + terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-secret +--- +apiVersion: v1 +kind: Secret +metadata: + name: webhook-server-secret + namespace: system diff --git a/postgres-kube-builder/config/rbac/rbac_role.yaml b/postgres-kube-builder/config/rbac/rbac_role.yaml new file mode 100644 index 0000000..63c86bc --- /dev/null +++ b/postgres-kube-builder/config/rbac/rbac_role.yaml @@ -0,0 +1,67 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: manager-role +rules: +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - kubeplus.k8s.io + resources: + - postgres + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch + - create + - update + - patch + - delete diff --git a/postgres-kube-builder/config/rbac/rbac_role_binding.yaml b/postgres-kube-builder/config/rbac/rbac_role_binding.yaml new file mode 100644 index 0000000..c1033e2 --- /dev/null +++ b/postgres-kube-builder/config/rbac/rbac_role_binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + creationTimestamp: null + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/postgres-kube-builder/config/rbac_back/rbac_role.yaml b/postgres-kube-builder/config/rbac_back/rbac_role.yaml new file mode 100644 index 0000000..63c86bc --- /dev/null +++ b/postgres-kube-builder/config/rbac_back/rbac_role.yaml @@ -0,0 +1,67 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: manager-role +rules: +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - kubeplus.k8s.io + resources: + - postgres + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch + - create + - update + - patch + - delete diff --git a/postgres-kube-builder/config/rbac_back/rbac_role_binding.yaml b/postgres-kube-builder/config/rbac_back/rbac_role_binding.yaml new file mode 100644 index 0000000..c1033e2 --- /dev/null +++ b/postgres-kube-builder/config/rbac_back/rbac_role_binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + creationTimestamp: null + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/postgres-kube-builder/config/samples/kubeplus_v1_postgres.yaml b/postgres-kube-builder/config/samples/kubeplus_v1_postgres.yaml new file mode 100644 index 0000000..c33f332 --- /dev/null +++ b/postgres-kube-builder/config/samples/kubeplus_v1_postgres.yaml @@ -0,0 +1,9 @@ +apiVersion: kubeplus.k8s.io/v1 +kind: Postgres +metadata: + labels: + controller-tools.k8s.io: "1.0" + name: postgres-sample +spec: + # Add fields here + foo: bar diff --git a/postgres-kube-builder/hack/boilerplate.go.txt b/postgres-kube-builder/hack/boilerplate.go.txt new file mode 100644 index 0000000..b92001f --- /dev/null +++ b/postgres-kube-builder/hack/boilerplate.go.txt @@ -0,0 +1,14 @@ +/* + +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. +*/ \ No newline at end of file diff --git a/postgres-kube-builder/initializeclient.yaml b/postgres-kube-builder/initializeclient.yaml new file mode 100644 index 0000000..e85d57b --- /dev/null +++ b/postgres-kube-builder/initializeclient.yaml @@ -0,0 +1,19 @@ +apiVersion: kubeplus.k8s.io/v1 +kind: Postgres +metadata: + name: client25 +spec: + deploymentName: client25 + image: postgres:9.3 + replicas: 1 + users: [{"username": "devdatta", "password": "pass123"}, + {"username": "pallavi", "password": "pass234"}] + databases: ["moodle"] + initcommands: [ "create table moodle_data1 (items varchar(250));", + "insert into moodle_data1 (items) values ('Moodle data1');", + "insert into moodle_data1 (items) values ('Moodle data1');", + "insert into moodle_data1 (items) values ('Moodle data2');", + "GRANT ALL PRIVILEGES ON TABLE moodle_data1 TO devdatta;", + "GRANT ALL PRIVILEGES ON TABLE moodle_data1 TO pallavi;" + ] + diff --git a/postgres-kube-builder/pkg/apis/addtoscheme_kubeplus_v1.go b/postgres-kube-builder/pkg/apis/addtoscheme_kubeplus_v1.go new file mode 100644 index 0000000..7a89d6d --- /dev/null +++ b/postgres-kube-builder/pkg/apis/addtoscheme_kubeplus_v1.go @@ -0,0 +1,25 @@ +/* + +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 apis + +import ( + "labs/postgres-kube-builder/pkg/apis/kubeplus/v1" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, v1.SchemeBuilder.AddToScheme) +} diff --git a/postgres-kube-builder/pkg/apis/apis.go b/postgres-kube-builder/pkg/apis/apis.go new file mode 100644 index 0000000..15ccbf9 --- /dev/null +++ b/postgres-kube-builder/pkg/apis/apis.go @@ -0,0 +1,32 @@ +/* + +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. +*/ + +// Generate deepcopy for apis +//go:generate go run ../../vendor/k8s.io/code-generator/cmd/deepcopy-gen/main.go -O zz_generated.deepcopy -i ./... -h ../../hack/boilerplate.go.txt + +// Package apis contains Kubernetes API groups. +package apis + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +// AddToSchemes may be used to add all resources defined in the project to a Scheme +var AddToSchemes runtime.SchemeBuilder + +// AddToScheme adds all Resources to the Scheme +func AddToScheme(s *runtime.Scheme) error { + return AddToSchemes.AddToScheme(s) +} diff --git a/postgres-kube-builder/pkg/apis/kubeplus/group.go b/postgres-kube-builder/pkg/apis/kubeplus/group.go new file mode 100644 index 0000000..c9b260b --- /dev/null +++ b/postgres-kube-builder/pkg/apis/kubeplus/group.go @@ -0,0 +1,17 @@ +/* + +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 kubeplus contains kubeplus API versions +package kubeplus diff --git a/postgres-kube-builder/pkg/apis/kubeplus/v1/doc.go b/postgres-kube-builder/pkg/apis/kubeplus/v1/doc.go new file mode 100644 index 0000000..c6e233c --- /dev/null +++ b/postgres-kube-builder/pkg/apis/kubeplus/v1/doc.go @@ -0,0 +1,22 @@ +/* + +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 v1 contains API Schema definitions for the kubeplus v1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=labs/postgres-kube-builder/pkg/apis/kubeplus +// +k8s:defaulter-gen=TypeMeta +// +groupName=kubeplus.k8s.io +package v1 diff --git a/postgres-kube-builder/pkg/apis/kubeplus/v1/postgres_types.go b/postgres-kube-builder/pkg/apis/kubeplus/v1/postgres_types.go new file mode 100644 index 0000000..72b123c --- /dev/null +++ b/postgres-kube-builder/pkg/apis/kubeplus/v1/postgres_types.go @@ -0,0 +1,75 @@ +/* + +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 v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// PostgresSpec defines the desired state of Postgres +type PostgresSpec struct { + DeploymentName string `json:"deploymentName"` + Image string `json:"image"` + Replicas *int32 `json:"replicas"` + Users []UserSpec `json:"users"` + Databases []string `json:"databases"` + Commands []string `json:"initcommands"` +} + +// PostgresStatus defines the observed state of Postgres +type PostgresStatus struct { + AvailableReplicas int32 `json:"availableReplicas"` + ActionHistory []string `json:"actionHistory"` + Users []UserSpec `json:"users"` + Databases []string `json:"databases"` + VerifyCmd string `json:"verifyCommand"` + ServiceIP string `json:"serviceIP"` + ServicePort string `json:"servicePort"` + Status string `json:"status"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Postgres is the Schema for the postgres API +// +k8s:openapi-gen=true +type Postgres struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata"` + Spec PostgresSpec `json:"spec"` + Status PostgresStatus `json:"status,omitempty"` +} + +type UserSpec struct { + User string `json:"username"` + Password string `json:"password"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PostgresList contains a list of Postgres +type PostgresList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + Items []Postgres `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Postgres{}, &PostgresList{}) +} diff --git a/postgres-kube-builder/pkg/apis/kubeplus/v1/postgres_types_test.go b/postgres-kube-builder/pkg/apis/kubeplus/v1/postgres_types_test.go new file mode 100644 index 0000000..229801a --- /dev/null +++ b/postgres-kube-builder/pkg/apis/kubeplus/v1/postgres_types_test.go @@ -0,0 +1,57 @@ +/* + +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 v1 + +import ( + "testing" + + "github.com/onsi/gomega" + "golang.org/x/net/context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +func TestStoragePostgres(t *testing.T) { + key := types.NamespacedName{ + Name: "foo", + Namespace: "default", + } + created := &Postgres{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }} + g := gomega.NewGomegaWithT(t) + + // Test Create + fetched := &Postgres{} + g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred()) + + g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(fetched).To(gomega.Equal(created)) + + // Test Updating the Labels + updated := fetched.DeepCopy() + updated.Labels = map[string]string{"hello": "world"} + g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred()) + + g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(fetched).To(gomega.Equal(updated)) + + // Test Delete + g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred()) +} diff --git a/postgres-kube-builder/pkg/apis/kubeplus/v1/register.go b/postgres-kube-builder/pkg/apis/kubeplus/v1/register.go new file mode 100644 index 0000000..a48cd74 --- /dev/null +++ b/postgres-kube-builder/pkg/apis/kubeplus/v1/register.go @@ -0,0 +1,45 @@ +/* + +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. +*/ + +// NOTE: Boilerplate only. Ignore this file. + +// Package v1 contains API Schema definitions for the kubeplus v1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=labs/postgres-kube-builder/pkg/apis/kubeplus +// +k8s:defaulter-gen=TypeMeta +// +groupName=kubeplus.k8s.io +package v1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/runtime/scheme" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "kubeplus.k8s.io", Version: "v1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} + + // AddToScheme is required by pkg/client/... + AddToScheme = SchemeBuilder.AddToScheme +) + +// Resource is required by pkg/client/listers/... +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/postgres-kube-builder/pkg/apis/kubeplus/v1/v1_suite_test.go b/postgres-kube-builder/pkg/apis/kubeplus/v1/v1_suite_test.go new file mode 100644 index 0000000..827d06e --- /dev/null +++ b/postgres-kube-builder/pkg/apis/kubeplus/v1/v1_suite_test.go @@ -0,0 +1,54 @@ +/* + +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 v1 + +import ( + "log" + "os" + "path/filepath" + "testing" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" +) + +var cfg *rest.Config +var c client.Client + +func TestMain(m *testing.M) { + t := &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")}, + } + + err := SchemeBuilder.AddToScheme(scheme.Scheme) + if err != nil { + log.Fatal(err) + } + + if cfg, err = t.Start(); err != nil { + log.Fatal(err) + } + + if c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}); err != nil { + log.Fatal(err) + } + + code := m.Run() + t.Stop() + os.Exit(code) +} diff --git a/postgres-kube-builder/pkg/apis/kubeplus/v1/zz_generated.deepcopy.go b/postgres-kube-builder/pkg/apis/kubeplus/v1/zz_generated.deepcopy.go new file mode 100644 index 0000000..f811164 --- /dev/null +++ b/postgres-kube-builder/pkg/apis/kubeplus/v1/zz_generated.deepcopy.go @@ -0,0 +1,167 @@ +// +build !ignore_autogenerated + +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by main. DO NOT EDIT. + +package v1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Postgres) DeepCopyInto(out *Postgres) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Postgres. +func (in *Postgres) DeepCopy() *Postgres { + if in == nil { + return nil + } + out := new(Postgres) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Postgres) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresList) DeepCopyInto(out *PostgresList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Postgres, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresList. +func (in *PostgresList) DeepCopy() *PostgresList { + if in == nil { + return nil + } + out := new(PostgresList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PostgresList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make([]UserSpec, len(*in)) + copy(*out, *in) + } + if in.Databases != nil { + in, out := &in.Databases, &out.Databases + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Commands != nil { + in, out := &in.Commands, &out.Commands + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresSpec. +func (in *PostgresSpec) DeepCopy() *PostgresSpec { + if in == nil { + return nil + } + out := new(PostgresSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresStatus) DeepCopyInto(out *PostgresStatus) { + *out = *in + if in.ActionHistory != nil { + in, out := &in.ActionHistory, &out.ActionHistory + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make([]UserSpec, len(*in)) + copy(*out, *in) + } + if in.Databases != nil { + in, out := &in.Databases, &out.Databases + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresStatus. +func (in *PostgresStatus) DeepCopy() *PostgresStatus { + if in == nil { + return nil + } + out := new(PostgresStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UserSpec) DeepCopyInto(out *UserSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserSpec. +func (in *UserSpec) DeepCopy() *UserSpec { + if in == nil { + return nil + } + out := new(UserSpec) + in.DeepCopyInto(out) + return out +} diff --git a/postgres-kube-builder/pkg/controller/add_postgres.go b/postgres-kube-builder/pkg/controller/add_postgres.go new file mode 100644 index 0000000..c7b770d --- /dev/null +++ b/postgres-kube-builder/pkg/controller/add_postgres.go @@ -0,0 +1,25 @@ +/* + +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 controller + +import ( + "labs/postgres-kube-builder/pkg/controller/postgres" +) + +func init() { + // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. + AddToManagerFuncs = append(AddToManagerFuncs, postgres.Add) +} diff --git a/postgres-kube-builder/pkg/controller/controller.go b/postgres-kube-builder/pkg/controller/controller.go new file mode 100644 index 0000000..b2acc50 --- /dev/null +++ b/postgres-kube-builder/pkg/controller/controller.go @@ -0,0 +1,33 @@ +/* + +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 controller + +import ( + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +// AddToManagerFuncs is a list of functions to add all Controllers to the Manager +var AddToManagerFuncs []func(manager.Manager) error + +// AddToManager adds all Controllers to the Manager +func AddToManager(m manager.Manager) error { + for _, f := range AddToManagerFuncs { + if err := f(m); err != nil { + return err + } + } + return nil +} diff --git a/postgres-kube-builder/pkg/controller/postgres/database.go b/postgres-kube-builder/pkg/controller/postgres/database.go new file mode 100644 index 0000000..258c02a --- /dev/null +++ b/postgres-kube-builder/pkg/controller/postgres/database.go @@ -0,0 +1,44 @@ +package postgres + +import ( + "fmt" + "strings" +) + +func getDatabaseCommands(desiredList []string, currentList []string) ([]string, []string) { + var createDatabaseCommands []string + var deleteDatabaseCommands []string + + if len(currentList) == 0 { + createDatabaseCommands = getCreateDatabaseCommands(desiredList) + } else { + addList := getDiffList(desiredList, currentList) + createDatabaseCommands = getCreateDatabaseCommands(addList) + + dropList := getDiffList(currentList, desiredList) + deleteDatabaseCommands = getDropDatabaseCommands(dropList) + } + return createDatabaseCommands, deleteDatabaseCommands +} + +func getCreateDatabaseCommands(dbList []string) []string { + var cmdList []string + for _, db := range dbList { + createDBCmd := strings.Fields("create database " + db + ";") + var cmdString = strings.Join(createDBCmd, " ") + fmt.Printf("CreateDBCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} + +func getDropDatabaseCommands(dbList []string) []string { + var cmdList []string + for _, db := range dbList { + dropDBCmd := strings.Fields("drop database " + db + ";") + var cmdString = strings.Join(dropDBCmd, " ") + fmt.Printf("DropDBCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} diff --git a/postgres-kube-builder/pkg/controller/postgres/postgres_controller.go b/postgres-kube-builder/pkg/controller/postgres/postgres_controller.go new file mode 100644 index 0000000..6a65e1e --- /dev/null +++ b/postgres-kube-builder/pkg/controller/postgres/postgres_controller.go @@ -0,0 +1,615 @@ +/* + +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 postgres + +import ( + "context" + "fmt" + "io/ioutil" + kubeplusv1 "labs/postgres-kube-builder/pkg/apis/kubeplus/v1" + "os" + "strconv" + "strings" + "time" + + appsv1 "k8s.io/api/apps/v1" + apiv1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + apiutil "k8s.io/apimachinery/pkg/util/intstr" + "labs/postgres-kube-builder/pkg/apis/kubeplus/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + + "database/sql" + _ "github.com/lib/pq" +) + +const controllerAgentName = "postgres-controller" + +const ( + // SuccessSynced is used as part of the Event 'reason' when a Foo is synced + SuccessSynced = "Synced" + // ErrResourceExists is used as part of the Event 'reason' when a Foo fails + // to sync due to a Deployment of the same name already existing. + ErrResourceExists = "ErrResourceExists" + + // MessageResourceExists is the message used for Events when a resource + // fails to sync due to a Deployment already existing + MessageResourceExists = "Resource %q already exists and is not managed by Foo" + // MessageResourceSynced is the message used for an Event fired when a Foo + // is synced successfully + MessageResourceSynced = "Foo synced successfully" +) + +const ( + PGPASSWORD = "mysecretpassword" + MINIKUBE_IP = "192.168.99.100" +) + +var ( + id_list = []string{} +) + +/** +* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller +* business logic. Delete these comments after modifying this file.* + */ + +// Add creates a new Postgres Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller +// and Start it when the Manager is Started. +// USER ACTION REQUIRED: update cmd/manager/main.go to call this kubeplus.Add(mgr) to install this Controller +func Add(mgr manager.Manager) error { + return add(mgr, newReconciler(mgr)) +} + +// newReconciler returns a new reconcile.Reconciler +func newReconciler(mgr manager.Manager) reconcile.Reconciler { + return &ReconcilePostgres{Client: mgr.GetClient(), scheme: mgr.GetScheme()} +} + +// add adds a new Controller to mgr with r as the reconcile.Reconciler +func add(mgr manager.Manager, r reconcile.Reconciler) error { + // Create a new controller + c, err := controller.New("postgres-controller", mgr, controller.Options{Reconciler: r}) + if err != nil { + return err + } + + // Watch for changes to Postgres + err = c.Watch(&source.Kind{Type: &kubeplusv1.Postgres{}}, &handler.EnqueueRequestForObject{}) + if err != nil { + return err + } + + // TODO(user): Modify this to be the types you create + // Uncomment watch a Deployment created by Postgres - change this for objects you create + err = c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForOwner{ + IsController: true, + OwnerType: &kubeplusv1.Postgres{}, + }) + if err != nil { + return err + } + + return nil +} + +var _ reconcile.Reconciler = &ReconcilePostgres{} + +// ReconcilePostgres reconciles a Postgres object +type ReconcilePostgres struct { + client.Client + scheme *runtime.Scheme +} + +// Reconcile reads that state of the cluster for a Postgres object and makes changes based on the state read +// and what is in the Postgres.Spec +// TODO(user): Modify this Reconcile function to implement your Controller logic. The scaffolding writes +// a Deployment as an example +// Automatically generate RBAC rules to allow the Controller to read and write Deployments +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=kubeplus.k8s.io,resources=postgres,verbs=get;list;watch;create;update;patch;delete +func (r *ReconcilePostgres) Reconcile(request reconcile.Request) (reconcile.Result, error) { + // Fetch the Postgres instance + instance := &kubeplusv1.Postgres{} + err := r.Get(context.TODO(), request.NamespacedName, instance) + if err != nil { + if errors.IsNotFound(err) { + // Object not found, return. Created objects are automatically garbage collected. + // For additional cleanup logic use finalizers. + return reconcile.Result{}, nil + } + // Error reading the object - requeue the request. + return reconcile.Result{}, err + } + + // TODO(user): Change this to be the object type created by your controller + // Define the desired Deployment object + deploymentName := instance.Spec.DeploymentName + + var verifyCmd string + var actionHistory []string + var serviceIP string + var servicePort string + var setupCommands []string + var databases []string + var users []kubeplusv1.UserSpec + + serviceIP, servicePort, setupCommands, databases, users, verifyCmd, err = createDeployment(instance, r) + + if err != nil { + if apierrors.IsAlreadyExists(err) { + + fmt.Printf("CRD %s created\n", deploymentName) + fmt.Printf("Check using: kubectl describe postgres %s \n", deploymentName) + + pgresObj := instance + err2 := r.Get(context.TODO(), request.NamespacedName, pgresObj) + + if err2 != nil { + panic(err2) + } + + actionHistory := pgresObj.Status.ActionHistory + serviceIP := pgresObj.Status.ServiceIP + servicePort := pgresObj.Status.ServicePort + verifyCmd := pgresObj.Status.VerifyCmd + fmt.Printf("Action History:[%s]\n", actionHistory) + fmt.Printf("Service IP:[%s]\n", serviceIP) + fmt.Printf("Service Port:[%s]\n", servicePort) + fmt.Printf("Verify cmd: %v\n", verifyCmd) + + //setupCommands = canonicalize(foo.Spec.Commands) + + var commandsToRun []string + + desiredDatabases := instance.Spec.Databases + currentDatabases := pgresObj.Status.Databases + fmt.Printf("Current Databases:%v\n", currentDatabases) + fmt.Printf("Desired Databases:%v\n", desiredDatabases) + createDBCommands, dropDBCommands := getDatabaseCommands(desiredDatabases, + currentDatabases) + appendList(&commandsToRun, createDBCommands) + appendList(&commandsToRun, dropDBCommands) + + desiredUsers := instance.Spec.Users + currentUsers := pgresObj.Status.Users + fmt.Printf("Current Users:%v\n", currentUsers) + fmt.Printf("Desired Users:%v\n", desiredUsers) + createUserCmds, dropUserCmds, alterUserCmds := getUserCommands(desiredUsers, + currentUsers) + appendList(&commandsToRun, createUserCmds) + appendList(&commandsToRun, dropUserCmds) + appendList(&commandsToRun, alterUserCmds) + + //commandsToRun = getCommandsToRun(actionHistory, setupCommands) + fmt.Printf("commandsToRun: %v\n", commandsToRun) + + if len(commandsToRun) > 0 { + err2 := updateFooStatus(instance, &actionHistory, ¤tUsers, &desiredDatabases, + verifyCmd, serviceIP, servicePort, "UPDATING", r) + if err2 != nil { + return reconcile.Result{}, err2 + } + updateCRD(pgresObj, commandsToRun) + } + + pgresObj2 := &kubeplusv1.Postgres{} + err3 := r.Get(context.TODO(), request.NamespacedName, pgresObj2) + + if err3 != nil { + panic(err3) + } + + actionHistory = pgresObj2.Status.ActionHistory + fmt.Printf("1111 Action History:%s\n", actionHistory) + for _, cmds := range commandsToRun { + actionHistory = append(actionHistory, cmds) + } + + err3 = updateFooStatus(pgresObj2, &actionHistory, &desiredUsers, &desiredDatabases, + verifyCmd, serviceIP, servicePort, "READY", r) + + if err3 != nil { + panic(err) + } + + } else { + panic(err) + } + } else { + for _, cmds := range setupCommands { + if !strings.Contains(cmds, "\\c") { + actionHistory = append(actionHistory, cmds) + } + } + fmt.Printf("Setup Commands: %v\n", setupCommands) + fmt.Printf("Verify using: %v\n", verifyCmd) + + err1 := updateFooStatus(instance, &actionHistory, &users, &databases, verifyCmd, serviceIP, servicePort, "READY", r) + + if err1 != nil { + return reconcile.Result{}, err1 + } + } + + return reconcile.Result{}, nil +} + +func updateCRD(foo *kubeplusv1.Postgres, setupCommands []string) { + serviceIP := foo.Status.ServiceIP + servicePort := foo.Status.ServicePort + + fmt.Printf("Service IP:[%s]\n", serviceIP) + fmt.Printf("Service Port:[%s]\n", servicePort) + fmt.Printf("Command:[%s]\n", setupCommands) + + if len(setupCommands) > 0 { + //file := createTempDBFile(setupCommands) + fmt.Println("Now setting up the database") + //setupDatabase(serviceIP, servicePort, file) + var dummyList []string + setupDatabase(serviceIP, servicePort, setupCommands, dummyList) + } +} + +func updateFooStatus(foo *kubeplusv1.Postgres, + actionHistory *[]string, users *[]kubeplusv1.UserSpec, databases *[]string, + verifyCmd string, serviceIP string, servicePort string, + status string, r *ReconcilePostgres) error { + + fooCopy := foo.DeepCopy() + fooCopy.Status.AvailableReplicas = 1 + + fooCopy.Status.VerifyCmd = verifyCmd + fooCopy.Status.ActionHistory = *actionHistory + fooCopy.Status.Users = *users + fooCopy.Status.Databases = *databases + fooCopy.Status.ServiceIP = serviceIP + fooCopy.Status.ServicePort = servicePort + fooCopy.Status.Status = status + err := r.Update(context.TODO(), fooCopy) + return err +} + +func createDeployment(foo *kubeplusv1.Postgres, r *ReconcilePostgres) (string, string, []string, []string, []kubeplusv1.UserSpec, string, error) { + + deploymentName := foo.Spec.DeploymentName + image := foo.Spec.Image + users := foo.Spec.Users + databases := foo.Spec.Databases + setupCommands := canonicalize(foo.Spec.Commands) + + var userAndDBCommands []string + var allCommands []string + + var currentDatabases []string + var currentUsers []kubeplusv1.UserSpec + createDBCmds, dropDBCmds := getDatabaseCommands(databases, currentDatabases) + createUserCmds, dropUserCmds, alterUserCmds := getUserCommands(users, currentUsers) + + fmt.Printf(" Deployment:%v, Image:%v\n", deploymentName, image) + fmt.Printf(" Users:%v\n", users) + fmt.Printf(" Databases:%v\n", databases) + fmt.Printf(" SetupCmds:%v\n", setupCommands) + fmt.Printf(" CreateDBCmds:%v\n", createDBCmds) + fmt.Printf(" DropDBCmds:%v\n", dropDBCmds) + fmt.Printf(" CreateUserCmds:%v\n", createUserCmds) + fmt.Printf(" DropUserCmds:%v\n", dropUserCmds) + fmt.Printf(" AlterUserCmds:%v\n", alterUserCmds) + + appendList(&userAndDBCommands, createDBCmds) + appendList(&userAndDBCommands, dropDBCmds) + appendList(&userAndDBCommands, createUserCmds) + appendList(&userAndDBCommands, dropUserCmds) + appendList(&userAndDBCommands, alterUserCmds) + fmt.Printf(" UserAndDBCmds:%v\n", userAndDBCommands) + fmt.Printf(" SetupCmds:%v\n", setupCommands) + + appendList(&allCommands, userAndDBCommands) + appendList(&allCommands, setupCommands) + + deployment := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: deploymentName, + Namespace: "default", + }, + Spec: appsv1.DeploymentSpec{ + Replicas: int32Ptr(1), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": deploymentName, + }, + }, + Template: apiv1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": deploymentName, + }, + }, + + Spec: apiv1.PodSpec{ + Containers: []apiv1.Container{ + { + Name: deploymentName, + Image: image, + Ports: []apiv1.ContainerPort{ + { + ContainerPort: 5432, + }, + }, + ReadinessProbe: &apiv1.Probe{ + Handler: apiv1.Handler{ + TCPSocket: &apiv1.TCPSocketAction{ + Port: apiutil.FromInt(5432), + }, + }, + InitialDelaySeconds: 5, + TimeoutSeconds: 60, + PeriodSeconds: 2, + }, + Env: []apiv1.EnvVar{ + { + Name: "POSTGRES_PASSWORD", + Value: PGPASSWORD, + }, + }, + }, + }, + }, + }, + }, + } + + fmt.Println("Creating deployment...") + err := r.Create(context.TODO(), deployment) + fmt.Println(err) + if err != nil { + return "", "", nil, nil, nil, "", err + } + + fmt.Printf("Created deployment %q.\n", deployment.GetObjectMeta().GetName()) + fmt.Printf("------------------------------\n") + + fmt.Printf("Creating service.....\n") + + service := &apiv1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: deploymentName, + Namespace: "default", + Labels: map[string]string{ + "app": deploymentName, + }, + }, + Spec: apiv1.ServiceSpec{ + Ports: []apiv1.ServicePort{ + { + Name: "my-port", + Port: 5432, + TargetPort: apiutil.FromInt(5432), + Protocol: apiv1.ProtocolTCP, + }, + }, + Selector: map[string]string{ + "app": deploymentName, + }, + Type: apiv1.ServiceTypeNodePort, + }, + } + + found := &apiv1.Service{} + err2 := r.Get(context.TODO(), types.NamespacedName{Name: deploymentName, Namespace: "default"}, found) + if err2 != nil { + fmt.Println(err2) + } + + err1 := r.Create(context.TODO(), service) + + if err1 != nil { + return "", "", nil, nil, nil, "", err + } + + fmt.Printf("Created service %q.\n", service.GetObjectMeta().GetName()) + fmt.Printf("------------------------------\n") + + serviceIP := MINIKUBE_IP + + nodePort1 := service.Spec.Ports[0].NodePort + nodePort := fmt.Sprint(nodePort1) + servicePort := nodePort + + fmt.Print("THIS IS THE SERVICE PORT HERE: ", servicePort) + + time.Sleep(time.Second * 5) + + for { + readyPods := 0 + pods := getPods() + + for _, d := range pods.Items { + podConditions := d.Status.Conditions + for _, podCond := range podConditions { + if podCond.Type == corev1.PodReady { + if podCond.Status == corev1.ConditionTrue { + readyPods += 1 + } + } + } + } + + if readyPods >= len(pods.Items) { + break + } else { + fmt.Println("Waiting for Pod to get ready.") + time.Sleep(time.Second * 4) + } + } + + time.Sleep(time.Second * 2) + + if len(userAndDBCommands) > 0 { + fmt.Printf("About to create temp db file for user and db commands") + //file := createTempDBFile(userAndDBCommands) + fmt.Println("Now setting up the database") + //setupDatabase_prev(serviceIP, servicePort, file) + var dummyList []string + setupDatabase(serviceIP, servicePort, userAndDBCommands, dummyList) + } + + if len(setupCommands) > 0 { + fmt.Printf("About to create temp db file for setup commands") + //file := createTempDBFile(setupCommands) + fmt.Println("Now setting up the database") + //setupDatabase(serviceIP, servicePort, file) + setupDatabase(serviceIP, servicePort, setupCommands, databases) + } + + verifyCmd := strings.Fields("psql -h " + serviceIP + " -p " + nodePort + " -U " + " -d ") + var verifyCmdString = strings.Join(verifyCmd, " ") + fmt.Printf("VerifyCmd: %v\n", verifyCmd) + return serviceIP, servicePort, allCommands, databases, users, verifyCmdString, err + +} + +func setupDatabase(serviceIP string, servicePort string, setupCommands []string, databases []string) { + + fmt.Println("Setting up database") + fmt.Println("Commands:") + fmt.Printf("%v", setupCommands) + + var host = serviceIP + port := -1 + port, _ = strconv.Atoi(servicePort) + var user = "postgres" + var password = PGPASSWORD + + var psqlInfo string + if len(databases) > 0 { + dbname := databases[0] + fmt.Printf("%s\n", dbname) + psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+ + "password=%s dbname=%s sslmode=disable", + host, port, user, password, dbname) + } else { + psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+ + "password=%s sslmode=disable", + host, port, user, password) + } + + db, err := sql.Open("postgres", psqlInfo) + if err != nil { + panic(err) + } + defer db.Close() + + err = db.Ping() + if err != nil { + panic(err) + } + + fmt.Println("Successfully connected!") + + for _, command := range setupCommands { + _, err = db.Exec(command) + if err != nil { + panic(err) + } + } + fmt.Println("Done setting up the database") +} + +func createTempDBFile(setupCommands []string) *os.File { + file, err := ioutil.TempFile("/tmp", "create-db1") + if err != nil { + panic(err) + } + + fmt.Printf("Database setup file:%s\n", file.Name()) + + for _, command := range setupCommands { + //fmt.Printf("Command: %v\n", command) + // TODO: Interpolation of variables + file.WriteString(command) + file.WriteString("\n") + } + file.Sync() + file.Close() + return file +} + +// podList returns a v1.PodList object +func getPods() *apiv1.PodList { + return &apiv1.PodList{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + } +} + +func int32Ptr(i int32) *int32 { return &i } + +// newbusyBoxPod demonstrates how to create a busybox pod +func newbusyBoxPod(cr *kubeplusv1.Postgres) *corev1.Pod { + labels := map[string]string{ + "app": "busy-box", + } + return &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "busy-box", + Namespace: cr.Namespace, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(cr, schema.GroupVersionKind{ + Group: v1.SchemeGroupVersion.Group, + Version: v1.SchemeGroupVersion.Version, + Kind: "Postgres", + }), + }, + Labels: labels, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "busybox", + Image: "busybox", + Command: []string{"sleep", "3600"}, + }, + }, + }, + } +} diff --git a/postgres-kube-builder/pkg/controller/postgres/postgres_controller_suite_test.go b/postgres-kube-builder/pkg/controller/postgres/postgres_controller_suite_test.go new file mode 100644 index 0000000..81f1701 --- /dev/null +++ b/postgres-kube-builder/pkg/controller/postgres/postgres_controller_suite_test.go @@ -0,0 +1,75 @@ +/* + +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 postgres + +import ( + "log" + "os" + "path/filepath" + "sync" + "testing" + + "labs/postgres-kube-builder/pkg/apis" + + "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +var cfg *rest.Config + +func TestMain(m *testing.M) { + t := &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")}, + } + apis.AddToScheme(scheme.Scheme) + + var err error + if cfg, err = t.Start(); err != nil { + log.Fatal(err) + } + + code := m.Run() + t.Stop() + os.Exit(code) +} + +// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and +// writes the request to requests after Reconcile is finished. +func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) { + requests := make(chan reconcile.Request) + fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) { + result, err := inner.Reconcile(req) + requests <- req + return result, err + }) + return fn, requests +} + +// StartTestManager adds recFn +func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) { + stop := make(chan struct{}) + wg := &sync.WaitGroup{} + go func() { + wg.Add(1) + g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred()) + wg.Done() + }() + return stop, wg +} diff --git a/postgres-kube-builder/pkg/controller/postgres/postgres_controller_test.go b/postgres-kube-builder/pkg/controller/postgres/postgres_controller_test.go new file mode 100644 index 0000000..70f8ea2 --- /dev/null +++ b/postgres-kube-builder/pkg/controller/postgres/postgres_controller_test.go @@ -0,0 +1,87 @@ +/* + +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 postgres + +import ( + "testing" + "time" + + kubeplusv1 "labs/postgres-kube-builder/pkg/apis/kubeplus/v1" + + "github.com/onsi/gomega" + "golang.org/x/net/context" + appsv1 "k8s.io/api/apps/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +var c client.Client + +var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: "foo", Namespace: "default"}} +var depKey = types.NamespacedName{Name: "foo-deployment", Namespace: "default"} + +const timeout = time.Second * 5 + +func TestReconcile(t *testing.T) { + g := gomega.NewGomegaWithT(t) + instance := &kubeplusv1.Postgres{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}} + + // Setup the Manager and Controller. Wrap the Controller Reconcile function so it writes each request to a + // channel when it is finished. + mgr, err := manager.New(cfg, manager.Options{}) + g.Expect(err).NotTo(gomega.HaveOccurred()) + c = mgr.GetClient() + + recFn, requests := SetupTestReconcile(newReconciler(mgr)) + g.Expect(add(mgr, recFn)).NotTo(gomega.HaveOccurred()) + + stopMgr, mgrStopped := StartTestManager(mgr, g) + + defer func() { + close(stopMgr) + mgrStopped.Wait() + }() + + // Create the Postgres object and expect the Reconcile and Deployment to be created + err = c.Create(context.TODO(), instance) + // The instance object may not be a valid object because it might be missing some required fields. + // Please modify the instance object by adding required fields and then remove the following if statement. + if apierrors.IsInvalid(err) { + t.Logf("failed to create object, got an invalid object error: %v", err) + return + } + g.Expect(err).NotTo(gomega.HaveOccurred()) + defer c.Delete(context.TODO(), instance) + g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest))) + + deploy := &appsv1.Deployment{} + g.Eventually(func() error { return c.Get(context.TODO(), depKey, deploy) }, timeout). + Should(gomega.Succeed()) + + // Delete the Deployment and expect Reconcile to be called for Deployment deletion + g.Expect(c.Delete(context.TODO(), deploy)).NotTo(gomega.HaveOccurred()) + g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest))) + g.Eventually(func() error { return c.Get(context.TODO(), depKey, deploy) }, timeout). + Should(gomega.Succeed()) + + // Manually delete Deployment since GC isn't enabled in the test control plane + g.Expect(c.Delete(context.TODO(), deploy)).To(gomega.Succeed()) + +} diff --git a/postgres-kube-builder/pkg/controller/postgres/user.go b/postgres-kube-builder/pkg/controller/postgres/user.go new file mode 100644 index 0000000..95c81fb --- /dev/null +++ b/postgres-kube-builder/pkg/controller/postgres/user.go @@ -0,0 +1,98 @@ +package postgres + +import ( + "fmt" + kubeplusv1 "labs/postgres-kube-builder/pkg/apis/kubeplus/v1" + "strings" +) + +func getCreateUserCommands(desiredList []kubeplusv1.UserSpec) []string { + var cmdList []string + for _, user := range desiredList { + username := user.User + password := user.Password + createUserCmd := strings.Fields("create user " + username + " with password '" + password + "';") + var cmdString = strings.Join(createUserCmd, " ") + fmt.Printf("CreateUserCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} + +func getDropUserCommands(desiredList []kubeplusv1.UserSpec) []string { + var cmdList []string + for _, user := range desiredList { + username := user.User + dropUserCmd := strings.Fields("drop user " + username + ";") + var cmdString = strings.Join(dropUserCmd, " ") + fmt.Printf("DropUserCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} + +func getAlterUserCommands(desiredList []kubeplusv1.UserSpec) []string { + var cmdList []string + for _, user := range desiredList { + username := user.User + password := user.Password + dropUserCmd := strings.Fields("alter user " + username + " with password '" + password + "';") + var cmdString = strings.Join(dropUserCmd, " ") + fmt.Printf("AlterUserCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} + +func getUserDiffList(desired []kubeplusv1.UserSpec, current []kubeplusv1.UserSpec) []kubeplusv1.UserSpec { + var diffList []kubeplusv1.UserSpec + for _, v := range desired { + var found bool = false + for _, v1 := range current { + if v.User == v1.User { + found = true + } + } + if !found { + diffList = append(diffList, v) + } + } + //fmt.Printf("-- DiffList: %v--\n", diffList) + return diffList +} + +func getUserCommonList(desired []kubeplusv1.UserSpec, current []kubeplusv1.UserSpec) []kubeplusv1.UserSpec { + var modifyList []kubeplusv1.UserSpec + for _, v := range desired { + for _, v1 := range current { + if v.User == v1.User { + if v.Password != v1.Password { + modifyList = append(modifyList, v) + } + } + } + } + //fmt.Printf("-- ModifyList: %v--\n", modifyList) + return modifyList +} + +func getUserCommands(desiredList []kubeplusv1.UserSpec, currentList []kubeplusv1.UserSpec) ([]string, []string, []string) { + + var createUserCommands []string + var dropUserCommands []string + var alterUserCommands []string + + if len(currentList) == 0 { + createUserCommands = getCreateUserCommands(desiredList) + } else { + addList := getUserDiffList(desiredList, currentList) + createUserCommands = getCreateUserCommands(addList) + + dropList := getUserDiffList(currentList, desiredList) + dropUserCommands = getDropUserCommands(dropList) + + alterList := getUserCommonList(desiredList, currentList) + alterUserCommands = getAlterUserCommands(alterList) + } + return createUserCommands, dropUserCommands, alterUserCommands +} diff --git a/postgres-kube-builder/pkg/controller/postgres/utils.go b/postgres-kube-builder/pkg/controller/postgres/utils.go new file mode 100644 index 0000000..2be3fb4 --- /dev/null +++ b/postgres-kube-builder/pkg/controller/postgres/utils.go @@ -0,0 +1,55 @@ +package postgres + +import ( + "fmt" + "strings" +) + +func getCommandsToRun(actionHistory []string, setupCommands []string) []string { + var commandsToRun []string + for _, v := range setupCommands { + var found bool = false + for _, v1 := range actionHistory { + if v == v1 { + found = true + } + } + if !found { + commandsToRun = append(commandsToRun, v) + } + } + fmt.Printf("-- commandsToRun: %v--\n", commandsToRun) + return commandsToRun +} + +func getDiffList(desired []string, current []string) []string { + var diffList []string + for _, v := range desired { + var found bool = false + for _, v1 := range current { + if v == v1 { + found = true + } + } + if !found { + diffList = append(diffList, v) + } + } + //fmt.Printf("-- DiffList: %v--\n", diffList) + return diffList +} + +func canonicalize(setupCommands1 []string) []string { + var setupCommands []string + //Convert setupCommands to Lower case + for _, cmd := range setupCommands1 { + setupCommands = append(setupCommands, strings.ToLower(cmd)) + } + return setupCommands +} + +func appendList(parentList *[]string, childList []string) { + for _, val := range childList { + *parentList = append(*parentList, val) + } +} diff --git a/postgres-kube-builder/pkg/webhook/webhook.go b/postgres-kube-builder/pkg/webhook/webhook.go new file mode 100644 index 0000000..4cb6587 --- /dev/null +++ b/postgres-kube-builder/pkg/webhook/webhook.go @@ -0,0 +1,36 @@ +/* + +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 webhook + +import ( + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +// AddToManagerFuncs is a list of functions to add all Controllers to the Manager +var AddToManagerFuncs []func(manager.Manager) error + +// AddToManager adds all Controllers to the Manager +// +kubebuilder:rbac:groups=admissionregistration.k8s.io,resources=mutatingwebhookconfigurations;validatingwebhookconfigurations,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete +func AddToManager(m manager.Manager) error { + for _, f := range AddToManagerFuncs { + if err := f(m); err != nil { + return err + } + } + return nil +} diff --git a/postgres-operator-sdk-new/.gitignore b/postgres-operator-sdk-new/.gitignore new file mode 100644 index 0000000..7c50470 --- /dev/null +++ b/postgres-operator-sdk-new/.gitignore @@ -0,0 +1,77 @@ +# Temporary Build Files +build/_output +build/_test +# Created by https://www.gitignore.io/api/go,vim,emacs,visualstudiocode +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* +# Org-mode +.org-id-locations +*_archive +# flymake-mode +*_flymake.* +# eshell files +/eshell/history +/eshell/lastdir +# elpa packages +/elpa/ +# reftex files +*.rel +# AUCTeX auto folder +/auto/ +# cask packages +.cask/ +dist/ +# Flycheck +flycheck_*.el +# server auth directory +/server/ +# projectiles files +.projectile +projectile-bookmarks.eld +# directory configuration +.dir-locals.el +# saveplace +places +# url cache +url/cache/ +# cedet +ede-projects.el +# smex +smex-items +# company-statistics +company-statistics-cache.el +# anaconda-mode +anaconda-mode/ +### Go ### +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +# Test binary, build with 'go test -c' +*.test +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +### Vim ### +# swap +.sw[a-p] +.*.sw[a-p] +# session +Session.vim +# temporary +.netrwhist +# auto-generated tag files +tags +### VisualStudioCode ### +.vscode/* +.history +# End of https://www.gitignore.io/api/go,vim,emacs,visualstudiocode diff --git a/postgres-operator-sdk-new/Gopkg.lock b/postgres-operator-sdk-new/Gopkg.lock new file mode 100644 index 0000000..e90fd30 --- /dev/null +++ b/postgres-operator-sdk-new/Gopkg.lock @@ -0,0 +1,734 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:8713dd3229c46881bb56b24fa3b581a0faab01d12e2d973a830965c24061e449" + name = "cloud.google.com/go" + packages = ["compute/metadata"] + pruneopts = "NT" + revision = "1fd54cf41e6e0e178ffe3c52b0e2260281f603e3" + version = "v0.32.0" + +[[projects]] + digest = "1:d8ebbd207f3d3266d4423ce4860c9f3794956306ded6c7ba312ecc69cdfbf04c" + name = "github.com/PuerkitoBio/purell" + packages = ["."] + pruneopts = "NT" + revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4" + version = "v1.1.0" + +[[projects]] + branch = "master" + digest = "1:8098cd40cd09879efbf12e33bcd51ead4a66006ac802cd563a66c4f3373b9727" + name = "github.com/PuerkitoBio/urlesc" + packages = ["."] + pruneopts = "NT" + revision = "de5bf2ad457846296e2031421a34e2568e304e35" + +[[projects]] + digest = "1:4b8b5811da6970495e04d1f4e98bb89518cc3cfc3b3f456bdb876ed7b6c74049" + name = "github.com/davecgh/go-spew" + packages = ["spew"] + pruneopts = "NT" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" + +[[projects]] + digest = "1:e6f888d4be8ec0f05c50e2aba83da4948b58045dee54d03be81fa74ea673302c" + name = "github.com/emicklei/go-restful" + packages = [ + ".", + "log", + ] + pruneopts = "NT" + revision = "3eb9738c1697594ea6e71a7156a9bb32ed216cf0" + version = "v2.8.0" + +[[projects]] + digest = "1:81466b4218bf6adddac2572a30ac733a9255919bc2f470b4827a317bd4ee1756" + name = "github.com/ghodss/yaml" + packages = ["."] + pruneopts = "NT" + revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" + version = "v1.0.0" + +[[projects]] + branch = "master" + digest = "1:d421af4c4fe51d399667d573982d663fe1fa67020a88d3ae43466ebfe8e2b5c9" + name = "github.com/go-logr/logr" + packages = ["."] + pruneopts = "NT" + revision = "9fb12b3b21c5415d16ac18dc5cd42c1cfdd40c4e" + +[[projects]] + digest = "1:340497a512995aa69c0add901d79a2096b3449d35a44a6f1f1115091a9f8c687" + name = "github.com/go-logr/zapr" + packages = ["."] + pruneopts = "NT" + revision = "7536572e8d55209135cd5e7ccf7fce43dca217ab" + version = "v0.1.0" + +[[projects]] + digest = "1:260f7ebefc63024c8dfe2c9f1a2935a89fa4213637a1f522f592f80c001cc441" + name = "github.com/go-openapi/jsonpointer" + packages = ["."] + pruneopts = "NT" + revision = "ef5f0afec364d3b9396b7b77b43dbe26bf1f8004" + version = "v0.17.2" + +[[projects]] + digest = "1:98abd61947ff5c7c6fcfec5473d02a4821ed3a2dd99a4fbfdb7925b0dd745546" + name = "github.com/go-openapi/jsonreference" + packages = ["."] + pruneopts = "NT" + revision = "8483a886a90412cd6858df4ea3483dce9c8e35a3" + version = "v0.17.2" + +[[projects]] + digest = "1:dfab391de021809e0041f0ab5648da6b74dd16a685472a1b8c3dc06b3dca1ee2" + name = "github.com/go-openapi/spec" + packages = ["."] + pruneopts = "NT" + revision = "5bae59e25b21498baea7f9d46e9c147ec106a42e" + version = "v0.17.2" + +[[projects]] + digest = "1:983f95b2fae6fe8fdd361738325ed6090f4f3bd15ce4db745e899fb5b0fdfc46" + name = "github.com/go-openapi/swag" + packages = ["."] + pruneopts = "NT" + revision = "5899d5c5e619fda5fa86e14795a835f473ca284c" + version = "v0.17.2" + +[[projects]] + digest = "1:2a9d5e367df8c95e780975ca1dd4010bef8e39a3777066d3880ce274b39d4b5a" + name = "github.com/gogo/protobuf" + packages = [ + "proto", + "sortkeys", + ] + pruneopts = "NT" + revision = "636bf0302bc95575d69441b25a2603156ffdddf1" + version = "v1.1.1" + +[[projects]] + branch = "master" + digest = "1:e2b86e41f3d669fc36b50d31d32d22c8ac656c75aa5ea89717ce7177e134ff2a" + name = "github.com/golang/glog" + packages = ["."] + pruneopts = "NT" + revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" + +[[projects]] + branch = "master" + digest = "1:aaedc94233e56ed57cdb04e3abfacc85c90c14082b62e3cdbe8ea72fc06ee035" + name = "github.com/golang/groupcache" + packages = ["lru"] + pruneopts = "NT" + revision = "c65c006176ff7ff98bb916961c7abbc6b0afc0aa" + +[[projects]] + digest = "1:d7cb4458ea8782e6efacd8f4940796ec559c90833509c436f40c4085b98156dd" + name = "github.com/golang/protobuf" + packages = [ + "proto", + "ptypes", + "ptypes/any", + "ptypes/duration", + "ptypes/timestamp", + ] + pruneopts = "NT" + revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" + version = "v1.2.0" + +[[projects]] + branch = "master" + digest = "1:05f95ffdfcf651bdb0f05b40b69e7f5663047f8da75c72d58728acb59b5cc107" + name = "github.com/google/btree" + packages = ["."] + pruneopts = "NT" + revision = "4030bb1f1f0c35b30ca7009e9ebd06849dd45306" + +[[projects]] + branch = "master" + digest = "1:52c5834e2bebac9030c97cc0798ac11c3aa8a39f098aeb419f142533da6cd3cc" + name = "github.com/google/gofuzz" + packages = ["."] + pruneopts = "NT" + revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1" + +[[projects]] + digest = "1:a1578f7323eca2b88021fdc9a79a99833d40b12c32a5ea4f284e2fad19ea2657" + name = "github.com/google/uuid" + packages = ["."] + pruneopts = "NT" + revision = "d460ce9f8df2e77fb1ba55ca87fafed96c607494" + version = "v1.0.0" + +[[projects]] + digest = "1:289332c13b80edfefc88397cce5266c16845dcf204fa2f6ac7e464ee4c7f6e96" + name = "github.com/googleapis/gnostic" + packages = [ + "OpenAPIv2", + "compiler", + "extensions", + ] + pruneopts = "NT" + revision = "7c663266750e7d82587642f65e60bc4083f1f84e" + version = "v0.2.0" + +[[projects]] + branch = "master" + digest = "1:2a2caa63899dae26ed3e4510b806549fd416d94db24ad68279daa62881b26488" + name = "github.com/gregjones/httpcache" + packages = [ + ".", + "diskcache", + ] + pruneopts = "NT" + revision = "9cad4c3443a7200dd6400aef47183728de563a38" + +[[projects]] + digest = "1:b42cde0e1f3c816dd57f57f7bbcf05ca40263ad96f168714c130c611fc0856a6" + name = "github.com/hashicorp/golang-lru" + packages = [ + ".", + "simplelru", + ] + pruneopts = "NT" + revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768" + version = "v0.5.0" + +[[projects]] + digest = "1:9a52adf44086cead3b384e5d0dbf7a1c1cce65e67552ee3383a8561c42a18cd3" + name = "github.com/imdario/mergo" + packages = ["."] + pruneopts = "NT" + revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4" + version = "v0.3.6" + +[[projects]] + digest = "1:1d39c063244ad17c4b18e8da1551163b6ffb52bd1640a49a8ec5c3b7bf4dbd5d" + name = "github.com/json-iterator/go" + packages = ["."] + pruneopts = "NT" + revision = "1624edc4454b8682399def8740d46db5e4362ba4" + version = "v1.1.5" + +[[projects]] + branch = "master" + digest = "1:7d9fcac7f1228470c4ea0ee31cdfb662a758c44df691e39b3e76c11d3e12ba8f" + name = "github.com/mailru/easyjson" + packages = [ + "buffer", + "jlexer", + "jwriter", + ] + pruneopts = "NT" + revision = "60711f1a8329503b04e1c88535f419d0bb440bff" + +[[projects]] + branch = "master" + digest = "1:0e9bfc47ab9941ecc3344e580baca5deb4091177e84dd9773b48b38ec26b93d5" + name = "github.com/mattbaird/jsonpatch" + packages = ["."] + pruneopts = "NT" + revision = "81af80346b1a01caae0cbc27fd3c1ba5b11e189f" + +[[projects]] + digest = "1:2f42fa12d6911c7b7659738758631bec870b7e9b4c6be5444f963cdcfccc191f" + name = "github.com/modern-go/concurrent" + packages = ["."] + pruneopts = "NT" + revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" + version = "1.0.3" + +[[projects]] + digest = "1:c6aca19413b13dc59c220ad7430329e2ec454cc310bc6d8de2c7e2b93c18a0f6" + name = "github.com/modern-go/reflect2" + packages = ["."] + pruneopts = "NT" + revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" + version = "1.0.1" + +[[projects]] + branch = "master" + digest = "1:65ab80e2a3dd41c63f75d105c89b5901f759e4e7dcc1b195a297b6d3522f655c" + name = "github.com/operator-framework/operator-sdk" + packages = [ + "pkg/k8sutil", + "version", + ] + pruneopts = "NT" + revision = "15b332d87d861d378ad0712624325a9f66d9f3d0" + +[[projects]] + digest = "1:93b1d84c5fa6d1ea52f4114c37714cddd84d5b78f151b62bb101128dd51399bf" + name = "github.com/pborman/uuid" + packages = ["."] + pruneopts = "NT" + revision = "adf5a7427709b9deb95d29d3fa8a2bf9cfd388f1" + version = "v1.2" + +[[projects]] + branch = "master" + digest = "1:bf2ac97824a7221eb16b096aecc1c390d4c8a4e49524386aaa2e2dd215cbfb31" + name = "github.com/petar/GoLLRB" + packages = ["llrb"] + pruneopts = "NT" + revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" + +[[projects]] + digest = "1:e4e9e026b8e4c5630205cd0208efb491b40ad40552e57f7a646bb8a46896077b" + name = "github.com/peterbourgon/diskv" + packages = ["."] + pruneopts = "NT" + revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" + version = "v2.0.1" + +[[projects]] + digest = "1:9d8420bbf131d1618bde6530af37c3799340d3762cc47210c1d9532a4c3a2779" + name = "github.com/spf13/pflag" + packages = ["."] + pruneopts = "NT" + revision = "298182f68c66c05229eb03ac171abe6e309ee79a" + version = "v1.0.3" + +[[projects]] + digest = "1:22f696cee54865fb8e9ff91df7b633f6b8f22037a8015253c6b6a71ca82219c7" + name = "go.uber.org/atomic" + packages = ["."] + pruneopts = "NT" + revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289" + version = "v1.3.2" + +[[projects]] + digest = "1:58ca93bdf81bac106ded02226b5395a0595d5346cdc4caa8d9c1f3a5f8f9976e" + name = "go.uber.org/multierr" + packages = ["."] + pruneopts = "NT" + revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a" + version = "v1.1.0" + +[[projects]] + digest = "1:572fa4496563920f3e3107a2294cf2621d6cc4ffd03403fb6397b1bab9fa082a" + name = "go.uber.org/zap" + packages = [ + ".", + "buffer", + "internal/bufferpool", + "internal/color", + "internal/exit", + "zapcore", + ] + pruneopts = "NT" + revision = "ff33455a0e382e8a81d14dd7c922020b6b5e7982" + version = "v1.9.1" + +[[projects]] + branch = "master" + digest = "1:e755223b4f1967d175739744538f2d3c722f67f945457e446cc27d9c97cf0304" + name = "golang.org/x/crypto" + packages = ["ssh/terminal"] + pruneopts = "NT" + revision = "e4dc69e5b2fd71dcaf8bd5d054eb936deb78d1fa" + +[[projects]] + branch = "master" + digest = "1:2b320dd877ed4444048d3d34781a956296dc757824a619767e34bb0dd7fdaaf6" + name = "golang.org/x/net" + packages = [ + "context", + "context/ctxhttp", + "http/httpguts", + "http2", + "http2/hpack", + "idna", + ] + pruneopts = "NT" + revision = "03003ca0c849e57b6ea29a4bab8d3cb6e4d568fe" + +[[projects]] + branch = "master" + digest = "1:948ab99b3a990ee11c6beb2d9e65217961897cbc0842acb57aad3d9f63c28f7e" + name = "golang.org/x/oauth2" + packages = [ + ".", + "google", + "internal", + "jws", + "jwt", + ] + pruneopts = "NT" + revision = "f42d05182288abf10faef86d16c0d07b8d40ea2d" + +[[projects]] + branch = "master" + digest = "1:5842bdbec06c1ea8891c369eff3033e9d73c201844b0c34f9ca556b0b8f5063a" + name = "golang.org/x/sys" + packages = [ + "unix", + "windows", + ] + pruneopts = "NT" + revision = "66b7b1311ac80bbafcd2daeef9a5e6e2cd1e2399" + +[[projects]] + digest = "1:8c74f97396ed63cc2ef04ebb5fc37bb032871b8fd890a25991ed40974b00cd2a" + 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", + "width", + ] + pruneopts = "NT" + revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" + version = "v0.3.0" + +[[projects]] + branch = "master" + digest = "1:9fdc2b55e8e0fafe4b41884091e51e77344f7dc511c5acedcfd98200003bff90" + name = "golang.org/x/time" + packages = ["rate"] + pruneopts = "NT" + revision = "85acf8d2951cb2a3bde7632f9ff273ef0379bcbd" + +[[projects]] + branch = "master" + digest = "1:8b1436e3482719ed148967faf7526dae7025061572afbf5c2f7fd226a9f937df" + name = "golang.org/x/tools" + packages = [ + "go/ast/astutil", + "imports", + "internal/fastwalk", + "internal/gopathwalk", + ] + pruneopts = "NT" + revision = "a00bb74625bdd57f48e427e23c0b26f505882e50" + +[[projects]] + digest = "1:2a4972ee51c3b9dfafbb3451fa0552e7a198d9d12c721bfc492050fe2f72e0f6" + name = "google.golang.org/appengine" + packages = [ + ".", + "internal", + "internal/app_identity", + "internal/base", + "internal/datastore", + "internal/log", + "internal/modules", + "internal/remote_api", + "internal/urlfetch", + "urlfetch", + ] + pruneopts = "NT" + revision = "4a4468ece617fc8205e99368fa2200e9d1fad421" + version = "v1.3.0" + +[[projects]] + digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a" + name = "gopkg.in/inf.v0" + packages = ["."] + pruneopts = "NT" + revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf" + version = "v0.9.1" + +[[projects]] + digest = "1:7c95b35057a0ff2e19f707173cc1a947fa43a6eb5c4d300d196ece0334046082" + name = "gopkg.in/yaml.v2" + packages = ["."] + pruneopts = "NT" + revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" + version = "v2.2.1" + +[[projects]] + digest = "1:f11e5753e619f411a51a49d60d39b2bc4da6766f5f0af2e2291daa6a3d9385d5" + name = "k8s.io/api" + packages = [ + "admission/v1beta1", + "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 = "NT" + revision = "2d6f90ab1293a1fb871cf149423ebb72aa7423aa" + +[[projects]] + digest = "1:b07bf863262aae765494d60f0d524483f211b29f9bb27d445a79c13af8676bf2" + 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/uuid", + "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 = "NT" + revision = "103fd098999dc9c0c88536f5c9ad2e5da39373ae" + +[[projects]] + digest = "1:1689a49a3ebc6e379849181f1e0899fccf143cab47586078721818bdcdb712bc" + name = "k8s.io/client-go" + packages = [ + "discovery", + "dynamic", + "kubernetes", + "kubernetes/scheme", + "kubernetes/typed/admissionregistration/v1alpha1", + "kubernetes/typed/admissionregistration/v1beta1", + "kubernetes/typed/apps/v1", + "kubernetes/typed/apps/v1beta1", + "kubernetes/typed/apps/v1beta2", + "kubernetes/typed/authentication/v1", + "kubernetes/typed/authentication/v1beta1", + "kubernetes/typed/authorization/v1", + "kubernetes/typed/authorization/v1beta1", + "kubernetes/typed/autoscaling/v1", + "kubernetes/typed/autoscaling/v2beta1", + "kubernetes/typed/batch/v1", + "kubernetes/typed/batch/v1beta1", + "kubernetes/typed/batch/v2alpha1", + "kubernetes/typed/certificates/v1beta1", + "kubernetes/typed/core/v1", + "kubernetes/typed/events/v1beta1", + "kubernetes/typed/extensions/v1beta1", + "kubernetes/typed/networking/v1", + "kubernetes/typed/policy/v1beta1", + "kubernetes/typed/rbac/v1", + "kubernetes/typed/rbac/v1alpha1", + "kubernetes/typed/rbac/v1beta1", + "kubernetes/typed/scheduling/v1alpha1", + "kubernetes/typed/scheduling/v1beta1", + "kubernetes/typed/settings/v1alpha1", + "kubernetes/typed/storage/v1", + "kubernetes/typed/storage/v1alpha1", + "kubernetes/typed/storage/v1beta1", + "pkg/apis/clientauthentication", + "pkg/apis/clientauthentication/v1alpha1", + "pkg/apis/clientauthentication/v1beta1", + "pkg/version", + "plugin/pkg/client/auth/exec", + "plugin/pkg/client/auth/gcp", + "rest", + "rest/watch", + "restmapper", + "third_party/forked/golang/template", + "tools/auth", + "tools/cache", + "tools/clientcmd", + "tools/clientcmd/api", + "tools/clientcmd/api/latest", + "tools/clientcmd/api/v1", + "tools/leaderelection", + "tools/leaderelection/resourcelock", + "tools/metrics", + "tools/pager", + "tools/record", + "tools/reference", + "transport", + "util/buffer", + "util/cert", + "util/connrotation", + "util/flowcontrol", + "util/homedir", + "util/integer", + "util/jsonpath", + "util/retry", + ] + pruneopts = "NT" + revision = "1f13a808da65775f22cbf47862c4e5898d8f4ca1" + +[[projects]] + digest = "1:8ab487a323486c8bbbaa3b689850487fdccc6cbea8690620e083b2d230a4447e" + name = "k8s.io/code-generator" + packages = [ + "cmd/client-gen", + "cmd/client-gen/args", + "cmd/client-gen/generators", + "cmd/client-gen/generators/fake", + "cmd/client-gen/generators/scheme", + "cmd/client-gen/generators/util", + "cmd/client-gen/path", + "cmd/client-gen/types", + "cmd/conversion-gen", + "cmd/conversion-gen/args", + "cmd/conversion-gen/generators", + "cmd/deepcopy-gen", + "cmd/deepcopy-gen/args", + "cmd/defaulter-gen", + "cmd/defaulter-gen/args", + "cmd/informer-gen", + "cmd/informer-gen/args", + "cmd/informer-gen/generators", + "cmd/lister-gen", + "cmd/lister-gen/args", + "cmd/lister-gen/generators", + "cmd/openapi-gen", + "cmd/openapi-gen/args", + "pkg/util", + ] + pruneopts = "T" + revision = "6702109cc68eb6fe6350b83e14407c8d7309fd1a" + +[[projects]] + branch = "master" + digest = "1:a978780d28f9e77c144c64d0bed9adb9df9e8840e44d9cde722c087177ed5025" + name = "k8s.io/gengo" + packages = [ + "args", + "examples/deepcopy-gen/generators", + "examples/defaulter-gen/generators", + "examples/set-gen/sets", + "generator", + "namer", + "parser", + "types", + ] + pruneopts = "NT" + revision = "7765d7f4899dc5c529e16c90791a600b0914c2a0" + +[[projects]] + branch = "master" + digest = "1:ffee5685348009e421410680345cbbd6c79c6592b8c7ed105aadb4d0a759d5f3" + name = "k8s.io/klog" + packages = ["."] + pruneopts = "NT" + revision = "8139d8cb77af419532b33dfa7dd09fbc5f1d344f" + +[[projects]] + branch = "master" + digest = "1:ee35efed55bb1633cbcce158c6c357c1f8ffc71131feddbc4eeb86fa22ea04a2" + name = "k8s.io/kube-openapi" + packages = [ + "cmd/openapi-gen/args", + "pkg/common", + "pkg/generators", + "pkg/generators/rules", + "pkg/util/proto", + "pkg/util/sets", + ] + pruneopts = "NT" + revision = "c59034cc13d587f5ef4e85ca0ade0c1866ae8e1d" + +[[projects]] + digest = "1:d1b7a6ed45c957e6308759f31fdbff8063741ecb08b7c3b6d67f0c9f4357b2ae" + name = "sigs.k8s.io/controller-runtime" + packages = [ + "pkg/cache", + "pkg/cache/internal", + "pkg/client", + "pkg/client/apiutil", + "pkg/client/config", + "pkg/internal/recorder", + "pkg/leaderelection", + "pkg/manager", + "pkg/patch", + "pkg/recorder", + "pkg/runtime/inject", + "pkg/runtime/log", + "pkg/runtime/signals", + "pkg/webhook/admission", + "pkg/webhook/admission/types", + "pkg/webhook/types", + ] + pruneopts = "NT" + revision = "5fd1e9e9fac5261e9ad9d47c375afc014fc31d21" + version = "v0.1.7" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/operator-framework/operator-sdk/pkg/k8sutil", + "github.com/operator-framework/operator-sdk/version", + "k8s.io/apimachinery/pkg/runtime", + "k8s.io/client-go/plugin/pkg/client/auth/gcp", + "k8s.io/code-generator/cmd/client-gen", + "k8s.io/code-generator/cmd/conversion-gen", + "k8s.io/code-generator/cmd/deepcopy-gen", + "k8s.io/code-generator/cmd/defaulter-gen", + "k8s.io/code-generator/cmd/informer-gen", + "k8s.io/code-generator/cmd/lister-gen", + "k8s.io/code-generator/cmd/openapi-gen", + "k8s.io/gengo/args", + "sigs.k8s.io/controller-runtime/pkg/client/config", + "sigs.k8s.io/controller-runtime/pkg/manager", + "sigs.k8s.io/controller-runtime/pkg/runtime/signals", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/postgres-operator-sdk-new/Gopkg.toml b/postgres-operator-sdk-new/Gopkg.toml new file mode 100644 index 0000000..d69029f --- /dev/null +++ b/postgres-operator-sdk-new/Gopkg.toml @@ -0,0 +1,54 @@ +# Force dep to vendor the code generators, which aren't imported just used at dev time. +required = [ + "k8s.io/code-generator/cmd/defaulter-gen", + "k8s.io/code-generator/cmd/deepcopy-gen", + "k8s.io/code-generator/cmd/conversion-gen", + "k8s.io/code-generator/cmd/client-gen", + "k8s.io/code-generator/cmd/lister-gen", + "k8s.io/code-generator/cmd/informer-gen", + "k8s.io/code-generator/cmd/openapi-gen", + "k8s.io/gengo/args", +] + +[[override]] + name = "k8s.io/code-generator" + # revision for tag "kubernetes-1.11.2" + revision = "6702109cc68eb6fe6350b83e14407c8d7309fd1a" + +[[override]] + name = "k8s.io/api" + # revision for tag "kubernetes-1.11.2" + revision = "2d6f90ab1293a1fb871cf149423ebb72aa7423aa" + +[[override]] + name = "k8s.io/apiextensions-apiserver" + # revision for tag "kubernetes-1.11.2" + revision = "408db4a50408e2149acbd657bceb2480c13cb0a4" + +[[override]] + name = "k8s.io/apimachinery" + # revision for tag "kubernetes-1.11.2" + revision = "103fd098999dc9c0c88536f5c9ad2e5da39373ae" + +[[override]] + name = "k8s.io/client-go" + # revision for tag "kubernetes-1.11.2" + revision = "1f13a808da65775f22cbf47862c4e5898d8f4ca1" + +[[override]] + name = "sigs.k8s.io/controller-runtime" + version = "v0.1.4" + +[[constraint]] + name = "github.com/operator-framework/operator-sdk" + # The version rule is used for a specific release and the master branch for in between releases. + branch = "master" #osdk_branch_annotation + # version = "=v0.1.0" #osdk_version_annotation + +[prune] + go-tests = true + non-go = true + + [[prune.project]] + name = "k8s.io/code-generator" + non-go = false diff --git a/postgres-operator-sdk-new/build/Dockerfile b/postgres-operator-sdk-new/build/Dockerfile new file mode 100644 index 0000000..5aa8b4a --- /dev/null +++ b/postgres-operator-sdk-new/build/Dockerfile @@ -0,0 +1,5 @@ +FROM alpine:3.6 + +USER nobody + +ADD build/_output/bin/postgres-operator-sdk-new /usr/local/bin/postgres-operator-sdk-new diff --git a/postgres-operator-sdk-new/cmd/manager/main.go b/postgres-operator-sdk-new/cmd/manager/main.go new file mode 100644 index 0000000..52ce2de --- /dev/null +++ b/postgres-operator-sdk-new/cmd/manager/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "flag" + "labs/postgres-operator-sdk-new/pkg/apis" + "labs/postgres-operator-sdk-new/pkg/controller" + "log" + "runtime" + + "github.com/operator-framework/operator-sdk/pkg/k8sutil" + sdkVersion "github.com/operator-framework/operator-sdk/version" + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/runtime/signals" +) + +func printVersion() { + log.Printf("Go Version: %s", runtime.Version()) + log.Printf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH) + log.Printf("operator-sdk Version: %v", sdkVersion.Version) +} + +func main() { + printVersion() + flag.Parse() + + namespace, err := k8sutil.GetWatchNamespace() + if err != nil { + log.Fatalf("failed to get watch namespace: %v", err) + } + + // Get a config to talk to the apiserver + cfg, err := config.GetConfig() + if err != nil { + log.Fatal(err) + } + + // Create a new Cmd to provide shared dependencies and start components + mgr, err := manager.New(cfg, manager.Options{Namespace: namespace}) + if err != nil { + log.Fatal(err) + } + + log.Print("Registering Components.") + + // Setup Scheme for all resources + if err := apis.AddToScheme(mgr.GetScheme()); err != nil { + log.Fatal(err) + } + + // Setup all Controllers + if err := controller.AddToManager(mgr); err != nil { + log.Fatal(err) + } + + log.Print("Starting the Cmd.") + + // Start the Cmd + log.Fatal(mgr.Start(signals.SetupSignalHandler())) +} diff --git a/postgres-operator-sdk-new/deploy/crds/postgresoperatorsdknew_v1_postgres_cr.yaml b/postgres-operator-sdk-new/deploy/crds/postgresoperatorsdknew_v1_postgres_cr.yaml new file mode 100644 index 0000000..1c1114f --- /dev/null +++ b/postgres-operator-sdk-new/deploy/crds/postgresoperatorsdknew_v1_postgres_cr.yaml @@ -0,0 +1,7 @@ +apiVersion: postgresoperatorsdknew.kubeplus/v1 +kind: Postgres +metadata: + name: example-postgres +spec: + # Add fields here + size: 3 diff --git a/postgres-operator-sdk-new/deploy/crds/postgresoperatorsdknew_v1_postgres_crd.yaml b/postgres-operator-sdk-new/deploy/crds/postgresoperatorsdknew_v1_postgres_crd.yaml new file mode 100644 index 0000000..831c019 --- /dev/null +++ b/postgres-operator-sdk-new/deploy/crds/postgresoperatorsdknew_v1_postgres_crd.yaml @@ -0,0 +1,13 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: postgres.postgresoperatorsdknew.kubeplus +spec: + group: postgresoperatorsdknew.kubeplus + names: + kind: Postgres + listKind: PostgresList + plural: postgres + singular: postgres + scope: Namespaced + version: v1 diff --git a/postgres-operator-sdk-new/deploy/operator.yaml b/postgres-operator-sdk-new/deploy/operator.yaml new file mode 100644 index 0000000..3a37155 --- /dev/null +++ b/postgres-operator-sdk-new/deploy/operator.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres-operator-sdk-new +spec: + replicas: 1 + selector: + matchLabels: + name: postgres-operator-sdk-new + template: + metadata: + labels: + name: postgres-operator-sdk-new + spec: + serviceAccountName: postgres-operator-sdk-new + containers: + - name: postgres-operator-sdk-new + # Replace this with the built image name + image: REPLACE_IMAGE + ports: + - containerPort: 60000 + name: metrics + command: + - postgres-operator-sdk-new + imagePullPolicy: Always + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: "postgres-operator-sdk-new" diff --git a/postgres-operator-sdk-new/deploy/role.yaml b/postgres-operator-sdk-new/deploy/role.yaml new file mode 100644 index 0000000..cffc217 --- /dev/null +++ b/postgres-operator-sdk-new/deploy/role.yaml @@ -0,0 +1,40 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + creationTimestamp: null + name: postgres-operator-sdk-new +rules: +- apiGroups: + - "" + resources: + - pods + - services + - endpoints + - persistentvolumeclaims + - events + - configmaps + - secrets + verbs: + - '*' +- apiGroups: + - apps + resources: + - deployments + - daemonsets + - replicasets + - statefulsets + verbs: + - '*' +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - get + - create +- apiGroups: + - postgresoperatorsdknew.kubeplus + resources: + - '*' + verbs: + - '*' diff --git a/postgres-operator-sdk-new/deploy/role_binding.yaml b/postgres-operator-sdk-new/deploy/role_binding.yaml new file mode 100644 index 0000000..6642ad0 --- /dev/null +++ b/postgres-operator-sdk-new/deploy/role_binding.yaml @@ -0,0 +1,11 @@ +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: postgres-operator-sdk-new +subjects: +- kind: ServiceAccount + name: postgres-operator-sdk-new +roleRef: + kind: Role + name: postgres-operator-sdk-new + apiGroup: rbac.authorization.k8s.io diff --git a/postgres-operator-sdk-new/deploy/service_account.yaml b/postgres-operator-sdk-new/deploy/service_account.yaml new file mode 100644 index 0000000..91fe75f --- /dev/null +++ b/postgres-operator-sdk-new/deploy/service_account.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: postgres-operator-sdk-new diff --git a/postgres-operator-sdk-new/pkg/apis/addtoscheme_postgresoperatorsdknew_v1.go b/postgres-operator-sdk-new/pkg/apis/addtoscheme_postgresoperatorsdknew_v1.go new file mode 100644 index 0000000..1b5850f --- /dev/null +++ b/postgres-operator-sdk-new/pkg/apis/addtoscheme_postgresoperatorsdknew_v1.go @@ -0,0 +1,10 @@ +package apis + +import ( + "labs/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, v1.SchemeBuilder.AddToScheme) +} diff --git a/postgres-operator-sdk-new/pkg/apis/apis.go b/postgres-operator-sdk-new/pkg/apis/apis.go new file mode 100644 index 0000000..07dc961 --- /dev/null +++ b/postgres-operator-sdk-new/pkg/apis/apis.go @@ -0,0 +1,13 @@ +package apis + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +// AddToSchemes may be used to add all resources defined in the project to a Scheme +var AddToSchemes runtime.SchemeBuilder + +// AddToScheme adds all Resources to the Scheme +func AddToScheme(s *runtime.Scheme) error { + return AddToSchemes.AddToScheme(s) +} diff --git a/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1/doc.go b/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1/doc.go new file mode 100644 index 0000000..bfe033a --- /dev/null +++ b/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1/doc.go @@ -0,0 +1,4 @@ +// Package v1 contains API Schema definitions for the postgresoperatorsdknew v1 API group +// +k8s:deepcopy-gen=package,register +// +groupName=postgresoperatorsdknew.kubeplus +package v1 diff --git a/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1/postgres_types.go b/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1/postgres_types.go new file mode 100644 index 0000000..de04c4b --- /dev/null +++ b/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1/postgres_types.go @@ -0,0 +1,57 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// PostgresSpec defines the desired state of Postgres +type PostgresSpec struct { + DeploymentName string `json:"deploymentName"` + Image string `json:"image"` + Replicas *int32 `json:"replicas"` + Users []UserSpec `json:"users"` + Databases []string `json:"databases"` + Commands []string `json:"initcommands"` +} +// PostgresStatus defines the observed state of Postgres +type PostgresStatus struct { + AvailableReplicas int32 `json:"availableReplicas"` + ActionHistory []string `json:"actionHistory"` + Users []UserSpec `json:"users"` + Databases []string `json:"databases"` + VerifyCmd string `json:"verifyCommand"` + ServiceIP string `json:"serviceIP"` + ServicePort string `json:"servicePort"` + Status string `json:"status"` +} +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Postgres is the Schema for the postgres API +// +k8s:openapi-gen=true +type Postgres struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata"` + Spec PostgresSpec `json:"spec"` + Status PostgresStatus `json:"status,omitempty"` +} + +type UserSpec struct { + User string `json:"username"` + Password string `json:"password"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PostgresList contains a list of Postgres +type PostgresList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + Items []Postgres `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Postgres{}, &PostgresList{}) +} diff --git a/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1/register.go b/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1/register.go new file mode 100644 index 0000000..166b8ac --- /dev/null +++ b/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1/register.go @@ -0,0 +1,19 @@ +// NOTE: Boilerplate only. Ignore this file. + +// Package v1 contains API Schema definitions for the postgresoperatorsdknew v1 API group +// +k8s:deepcopy-gen=package,register +// +groupName=postgresoperatorsdknew.kubeplus +package v1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/runtime/scheme" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "postgresoperatorsdknew.kubeplus", Version: "v1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} +) diff --git a/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1/zz_generated.deepcopy.go b/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1/zz_generated.deepcopy.go new file mode 100644 index 0000000..292e8d1 --- /dev/null +++ b/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1/zz_generated.deepcopy.go @@ -0,0 +1,169 @@ +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Postgres) DeepCopyInto(out *Postgres) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Postgres. +func (in *Postgres) DeepCopy() *Postgres { + if in == nil { + return nil + } + out := new(Postgres) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Postgres) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresList) DeepCopyInto(out *PostgresList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Postgres, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresList. +func (in *PostgresList) DeepCopy() *PostgresList { + if in == nil { + return nil + } + out := new(PostgresList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PostgresList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make([]UserSpec, len(*in)) + copy(*out, *in) + } + if in.Databases != nil { + in, out := &in.Databases, &out.Databases + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Commands != nil { + in, out := &in.Commands, &out.Commands + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresSpec. +func (in *PostgresSpec) DeepCopy() *PostgresSpec { + if in == nil { + return nil + } + out := new(PostgresSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresStatus) DeepCopyInto(out *PostgresStatus) { + *out = *in + if in.ActionHistory != nil { + in, out := &in.ActionHistory, &out.ActionHistory + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make([]UserSpec, len(*in)) + copy(*out, *in) + } + if in.Databases != nil { + in, out := &in.Databases, &out.Databases + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresStatus. +func (in *PostgresStatus) DeepCopy() *PostgresStatus { + if in == nil { + return nil + } + out := new(PostgresStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UserSpec) DeepCopyInto(out *UserSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserSpec. +func (in *UserSpec) DeepCopy() *UserSpec { + if in == nil { + return nil + } + out := new(UserSpec) + in.DeepCopyInto(out) + return out +} diff --git a/postgres-operator-sdk-new/pkg/controller/add_postgres.go b/postgres-operator-sdk-new/pkg/controller/add_postgres.go new file mode 100644 index 0000000..3745e7b --- /dev/null +++ b/postgres-operator-sdk-new/pkg/controller/add_postgres.go @@ -0,0 +1,10 @@ +package controller + +import ( + "labs/postgres-operator-sdk-new/pkg/controller/postgres" +) + +func init() { + // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. + AddToManagerFuncs = append(AddToManagerFuncs, postgres.Add) +} diff --git a/postgres-operator-sdk-new/pkg/controller/controller.go b/postgres-operator-sdk-new/pkg/controller/controller.go new file mode 100644 index 0000000..7c069f3 --- /dev/null +++ b/postgres-operator-sdk-new/pkg/controller/controller.go @@ -0,0 +1,18 @@ +package controller + +import ( + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +// AddToManagerFuncs is a list of functions to add all Controllers to the Manager +var AddToManagerFuncs []func(manager.Manager) error + +// AddToManager adds all Controllers to the Manager +func AddToManager(m manager.Manager) error { + for _, f := range AddToManagerFuncs { + if err := f(m); err != nil { + return err + } + } + return nil +} diff --git a/postgres-operator-sdk-new/pkg/controller/postgres/database.go b/postgres-operator-sdk-new/pkg/controller/postgres/database.go new file mode 100644 index 0000000..258c02a --- /dev/null +++ b/postgres-operator-sdk-new/pkg/controller/postgres/database.go @@ -0,0 +1,44 @@ +package postgres + +import ( + "fmt" + "strings" +) + +func getDatabaseCommands(desiredList []string, currentList []string) ([]string, []string) { + var createDatabaseCommands []string + var deleteDatabaseCommands []string + + if len(currentList) == 0 { + createDatabaseCommands = getCreateDatabaseCommands(desiredList) + } else { + addList := getDiffList(desiredList, currentList) + createDatabaseCommands = getCreateDatabaseCommands(addList) + + dropList := getDiffList(currentList, desiredList) + deleteDatabaseCommands = getDropDatabaseCommands(dropList) + } + return createDatabaseCommands, deleteDatabaseCommands +} + +func getCreateDatabaseCommands(dbList []string) []string { + var cmdList []string + for _, db := range dbList { + createDBCmd := strings.Fields("create database " + db + ";") + var cmdString = strings.Join(createDBCmd, " ") + fmt.Printf("CreateDBCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} + +func getDropDatabaseCommands(dbList []string) []string { + var cmdList []string + for _, db := range dbList { + dropDBCmd := strings.Fields("drop database " + db + ";") + var cmdString = strings.Join(dropDBCmd, " ") + fmt.Printf("DropDBCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} diff --git a/postgres-operator-sdk-new/pkg/controller/postgres/postgres_controller.go b/postgres-operator-sdk-new/pkg/controller/postgres/postgres_controller.go new file mode 100644 index 0000000..ec586b5 --- /dev/null +++ b/postgres-operator-sdk-new/pkg/controller/postgres/postgres_controller.go @@ -0,0 +1,612 @@ +package postgres + +import ( + postgresv1 "labs/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1" + "log" + "os" + "io/ioutil" + "context" + "strconv" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "labs/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + appsv1 "k8s.io/api/apps/v1" + apiv1 "k8s.io/api/core/v1" + apiutil "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/runtime/schema" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "strings" + "fmt" + "time" + + _ "github.com/lib/pq" + "database/sql" +) + +const controllerAgentName = "postgres-controller" + +const ( + // SuccessSynced is used as part of the Event 'reason' when a Foo is synced + SuccessSynced = "Synced" + // ErrResourceExists is used as part of the Event 'reason' when a Foo fails + // to sync due to a Deployment of the same name already existing. + ErrResourceExists = "ErrResourceExists" + + // MessageResourceExists is the message used for Events when a resource + // fails to sync due to a Deployment already existing + MessageResourceExists = "Resource %q already exists and is not managed by Foo" + // MessageResourceSynced is the message used for an Event fired when a Foo + // is synced successfully + MessageResourceSynced = "Foo synced successfully" +) + +const ( + PGPASSWORD = "mysecretpassword" + MINIKUBE_IP = "192.168.99.100" +) + +var ( + id_list = []string{} +) + + +/** +* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller +* business logic. Delete these comments after modifying this file.* + */ + +// Add creates a new Postgres Controller and adds it to the Manager. The Manager will set fields on the Controller +// and Start it when the Manager is Started. +func Add(mgr manager.Manager) error { + return add(mgr, newReconciler(mgr)) +} + +// newReconciler returns a new reconcile.Reconciler +func newReconciler(mgr manager.Manager) reconcile.Reconciler { + return &ReconcilePostgres{client: mgr.GetClient(), scheme: mgr.GetScheme(), cache: mgr.GetCache()} +} + +// add adds a new Controller to mgr with r as the reconcile.Reconciler +func add(mgr manager.Manager, r reconcile.Reconciler) error { + // Create a new controller + log.Print("I AM HERE\n") + c, err := controller.New("postgres-controller", mgr, controller.Options{Reconciler: r}) + if err != nil { + return err + } + + log.Print("Did I make it here????\n") + + // Watch for changes to primary resource Postgres + err = c.Watch(&source.Kind{Type: &v1.Postgres{}}, &handler.EnqueueRequestForObject{}) + if err != nil { + return err + } + + log.Print("I also made it here!!!\n") + // TODO(user): Modify this to be the types you create that are owned by the primary resource + // Watch for changes to secondary resource Pods and requeue the owner Postgres + err = c.Watch(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForOwner{ + IsController: true, + OwnerType: &postgresv1.Postgres{}, + }) + if err != nil { + return err + } + + return nil +} + +var _ reconcile.Reconciler = &ReconcilePostgres{} + +// ReconcilePostgres reconciles a Postgres object +type ReconcilePostgres struct { + // This client, initialized using mgr.Client() above, is a split client + // that reads objects from the cache and writes to the apiserver + client client.Client + scheme *runtime.Scheme + cache cache.Cache +} + +// Reconcile reads that state of the cluster for a Postgres object and makes changes based on the state read +// and what is in the Postgres.Spec +// TODO(user): Modify this Reconcile function to implement your Controller logic. This example creates +// a Pod as an example +// Note: +// The Controller will requeue the Request to be processed again if the returned error is non-nil or +// Result.Requeue is true, otherwise upon completion it will remove the work from the queue. +func (r *ReconcilePostgres) Reconcile(request reconcile.Request) (reconcile.Result, error) { + log.Printf("Reconciling Postgres %s/%s\n", request.Namespace, request.Name) + + // Fetch the Postgres instance + instance := &postgresv1.Postgres{} + err := r.client.Get(context.TODO(), request.NamespacedName, instance) + if err != nil { + if errors.IsNotFound(err) { + // Request object not found, could have been deleted after reconcile request. + // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. + // Return and don't requeue + return reconcile.Result{}, nil + } + // Error reading the object - requeue the request. + return reconcile.Result{}, err + } + + deploymentName := instance.Spec.DeploymentName + + var verifyCmd string + var actionHistory []string + var serviceIP string + var servicePort string + var setupCommands []string + var databases []string + var users []postgresv1.UserSpec + + serviceIP, servicePort, setupCommands, databases, users, verifyCmd, err = createDeployment(instance, r) + + if err != nil { + if apierrors.IsAlreadyExists(err) { + + fmt.Printf("CRD %s created\n", deploymentName) + fmt.Printf("Check using: kubectl describe postgres %s \n", deploymentName) + + pgresObj := instance + err2 := r.client.Get(context.TODO(), request.NamespacedName, pgresObj) + + if err2 != nil { + panic(err2) + } + + actionHistory := pgresObj.Status.ActionHistory + serviceIP := pgresObj.Status.ServiceIP + servicePort := pgresObj.Status.ServicePort + verifyCmd := pgresObj.Status.VerifyCmd + fmt.Printf("Action History:[%s]\n", actionHistory) + fmt.Printf("Service IP:[%s]\n", serviceIP) + fmt.Printf("Service Port:[%s]\n", servicePort) + fmt.Printf("Verify cmd: %v\n", verifyCmd) + + //setupCommands = canonicalize(foo.Spec.Commands) + + var commandsToRun []string + + desiredDatabases := instance.Spec.Databases + currentDatabases := pgresObj.Status.Databases + fmt.Printf("Current Databases:%v\n", currentDatabases) + fmt.Printf("Desired Databases:%v\n", desiredDatabases) + createDBCommands, dropDBCommands := getDatabaseCommands(desiredDatabases, + currentDatabases) + appendList(&commandsToRun, createDBCommands) + appendList(&commandsToRun, dropDBCommands) + + desiredUsers := instance.Spec.Users + currentUsers := pgresObj.Status.Users + fmt.Printf("Current Users:%v\n", currentUsers) + fmt.Printf("Desired Users:%v\n", desiredUsers) + createUserCmds, dropUserCmds, alterUserCmds := getUserCommands(desiredUsers, + currentUsers) + appendList(&commandsToRun, createUserCmds) + appendList(&commandsToRun, dropUserCmds) + appendList(&commandsToRun, alterUserCmds) + + //commandsToRun = getCommandsToRun(actionHistory, setupCommands) + fmt.Printf("commandsToRun: %v\n", commandsToRun) + + if len(commandsToRun) > 0 { + err2 := updateFooStatus(instance, &actionHistory, ¤tUsers, &desiredDatabases, + verifyCmd, serviceIP, servicePort, "UPDATING", r) + if err2 != nil { + return reconcile.Result{}, err2 + } + updateCRD(pgresObj, commandsToRun) + } + + pgresObj2 := &postgresv1.Postgres{} + err3 := r.client.Get(context.TODO(), request.NamespacedName, pgresObj2) + + if err3 != nil { + panic(err3) + } + + actionHistory = pgresObj2.Status.ActionHistory + fmt.Printf("1111 Action History:%s\n", actionHistory) + for _, cmds := range commandsToRun { + actionHistory = append(actionHistory, cmds) + } + + err3 = updateFooStatus(pgresObj2, &actionHistory, &desiredUsers, &desiredDatabases, + verifyCmd, serviceIP, servicePort, "READY", r) + + if err3 != nil { + panic(err) + return reconcile.Result{}, err3 + } + + } else { + panic(err) + } + } else { + for _, cmds := range setupCommands { + if !strings.Contains(cmds, "\\c") { + actionHistory = append(actionHistory, cmds) + } + } + fmt.Printf("Setup Commands: %v\n", setupCommands) + fmt.Printf("Verify using: %v\n", verifyCmd) + + err1 := updateFooStatus(instance, &actionHistory, &users, &databases, verifyCmd, serviceIP, servicePort, "READY", r) + + if err1 != nil { + return reconcile.Result{}, err1 + } + } + + return reconcile.Result{}, nil +} + +func updateCRD(foo *postgresv1.Postgres, setupCommands []string) { + serviceIP := foo.Status.ServiceIP + servicePort := foo.Status.ServicePort + + fmt.Printf("Service IP:[%s]\n", serviceIP) + fmt.Printf("Service Port:[%s]\n", servicePort) + fmt.Printf("Command:[%s]\n", setupCommands) + + if len(setupCommands) > 0 { + //file := createTempDBFile(setupCommands) + fmt.Println("Now setting up the database") + //setupDatabase(serviceIP, servicePort, file) + var dummyList []string + setupDatabase(serviceIP, servicePort, setupCommands, dummyList) + } +} + +func updateFooStatus(foo *postgresv1.Postgres, + actionHistory *[]string, users *[]postgresv1.UserSpec, databases *[]string, + verifyCmd string, serviceIP string, servicePort string, + status string, r *ReconcilePostgres) error { + + fooCopy := foo.DeepCopy() + fooCopy.Status.AvailableReplicas = 1 + + fooCopy.Status.VerifyCmd = verifyCmd + fooCopy.Status.ActionHistory = *actionHistory + fooCopy.Status.Users = *users + fooCopy.Status.Databases = *databases + fooCopy.Status.ServiceIP = serviceIP + fooCopy.Status.ServicePort = servicePort + fooCopy.Status.Status = status + err := r.client.Update(context.TODO(), fooCopy) + return err +} + +func createDeployment (foo *postgresv1.Postgres, r *ReconcilePostgres) (string, string, []string, []string, []postgresv1.UserSpec, string, error){ + + deploymentName := foo.Spec.DeploymentName + image := foo.Spec.Image + users := foo.Spec.Users + databases := foo.Spec.Databases + setupCommands := canonicalize(foo.Spec.Commands) + + var userAndDBCommands []string + var allCommands []string + + var currentDatabases []string + var currentUsers []postgresv1.UserSpec + createDBCmds, dropDBCmds := getDatabaseCommands(databases, currentDatabases) + createUserCmds, dropUserCmds, alterUserCmds := getUserCommands(users, currentUsers) + + fmt.Printf(" Deployment:%v, Image:%v\n", deploymentName, image) + fmt.Printf(" Users:%v\n", users) + fmt.Printf(" Databases:%v\n", databases) + fmt.Printf(" SetupCmds:%v\n", setupCommands) + fmt.Printf(" CreateDBCmds:%v\n", createDBCmds) + fmt.Printf(" DropDBCmds:%v\n", dropDBCmds) + fmt.Printf(" CreateUserCmds:%v\n", createUserCmds) + fmt.Printf(" DropUserCmds:%v\n", dropUserCmds) + fmt.Printf(" AlterUserCmds:%v\n", alterUserCmds) + + appendList(&userAndDBCommands, createDBCmds) + appendList(&userAndDBCommands, dropDBCmds) + appendList(&userAndDBCommands, createUserCmds) + appendList(&userAndDBCommands, dropUserCmds) + appendList(&userAndDBCommands, alterUserCmds) + fmt.Printf(" UserAndDBCmds:%v\n", userAndDBCommands) + fmt.Printf(" SetupCmds:%v\n", setupCommands) + + appendList(&allCommands, userAndDBCommands) + appendList(&allCommands, setupCommands) + + deployment := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: deploymentName, + Namespace: "default", + }, + Spec: appsv1.DeploymentSpec{ + Replicas: int32Ptr(1), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": deploymentName, + }, + }, + Template: apiv1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": deploymentName, + }, + }, + + Spec: apiv1.PodSpec{ + Containers: []apiv1.Container{ + { + Name: deploymentName, + Image: image, + Ports: []apiv1.ContainerPort{ + { + ContainerPort: 5432, + }, + }, + ReadinessProbe: &apiv1.Probe{ + Handler: apiv1.Handler{ + TCPSocket: &apiv1.TCPSocketAction{ + Port: apiutil.FromInt(5432), + }, + }, + InitialDelaySeconds: 5, + TimeoutSeconds: 60, + PeriodSeconds: 2, + }, + Env: []apiv1.EnvVar{ + { + Name: "POSTGRES_PASSWORD", + Value: PGPASSWORD, + }, + }, + }, + }, + }, + }, + }, + } + + fmt.Println("Creating deployment...") + err := r.client.Create(context.TODO(), deployment) + fmt.Println(err) + if err != nil { + return "", "", nil, nil, nil, "", err + } + + fmt.Printf("Created deployment %q.\n", deployment.GetObjectMeta().GetName()) + fmt.Printf("------------------------------\n") + + fmt.Printf("Creating service.....\n") + + service := &apiv1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: deploymentName, + Namespace: "default", + Labels: map[string]string{ + "app": deploymentName, + }, + }, + Spec: apiv1.ServiceSpec{ + Ports: []apiv1.ServicePort { + { + Name: "my-port", + Port: 5432, + TargetPort: apiutil.FromInt(5432), + Protocol: apiv1.ProtocolTCP, + }, + }, + Selector: map[string]string { + "app": deploymentName, + }, + Type: apiv1.ServiceTypeNodePort, + }, + } + + found := &apiv1.Service{} + err2 := r.client.Get(context.TODO(), types.NamespacedName{Name: deploymentName, Namespace: "default"}, found) + if err2 != nil { + fmt.Println(err2) + } + + err1 := r.client.Create(context.TODO(), service) + + if err1 != nil { + return "", "", nil, nil, nil, "", err + } + + fmt.Printf("Created service %q.\n", service.GetObjectMeta().GetName()) + fmt.Printf("------------------------------\n") + + serviceIP := MINIKUBE_IP + + nodePort1 := service.Spec.Ports[0].NodePort + nodePort := fmt.Sprint(nodePort1) + servicePort := nodePort + + fmt.Print("THIS IS THE SERVICE PORT HERE: ", servicePort) + + time.Sleep(time.Second * 5) + + for { + readyPods := 0 + pods := getPods() + + for _, d := range pods.Items { + podConditions := d.Status.Conditions + for _, podCond := range podConditions { + if podCond.Type == corev1.PodReady { + if podCond.Status == corev1.ConditionTrue { + readyPods += 1 + } + } + } + } + + if readyPods >= len(pods.Items) { + break + } else { + fmt.Println("Waiting for Pod to get ready.") + time.Sleep(time.Second * 4) + } + } + + time.Sleep(time.Second * 2) + + if len(userAndDBCommands) > 0 { + fmt.Printf("About to create temp db file for user and db commands") + //file := createTempDBFile(userAndDBCommands) + fmt.Println("Now setting up the database") + //setupDatabase_prev(serviceIP, servicePort, file) + var dummyList []string + setupDatabase(serviceIP, servicePort, userAndDBCommands, dummyList) + } + + if len(setupCommands) > 0 { + fmt.Printf("About to create temp db file for setup commands") + //file := createTempDBFile(setupCommands) + fmt.Println("Now setting up the database") + //setupDatabase(serviceIP, servicePort, file) + setupDatabase(serviceIP, servicePort, setupCommands, databases) + } + + verifyCmd := strings.Fields("psql -h " + serviceIP + " -p " + nodePort + " -U " + " -d ") + var verifyCmdString = strings.Join(verifyCmd, " ") + fmt.Printf("VerifyCmd: %v\n", verifyCmd) + return serviceIP, servicePort, allCommands, databases, users, verifyCmdString, err + +} + +func setupDatabase(serviceIP string, servicePort string, setupCommands []string, databases []string) { + + fmt.Println("Setting up database") + fmt.Println("Commands:") + fmt.Printf("%v", setupCommands) + + var host = serviceIP + port := -1 + port, _ = strconv.Atoi(servicePort) + var user = "postgres" + var password = PGPASSWORD + + var psqlInfo string + if len(databases) > 0 { + dbname := databases[0] + fmt.Println("%s\n", dbname) + psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+ + "password=%s dbname=%s sslmode=disable", + host, port, user, password, dbname) + } else { + psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+ + "password=%s sslmode=disable", + host, port, user, password) + } + + db, err := sql.Open("postgres", psqlInfo) + if err != nil { + panic(err) + } + defer db.Close() + + err = db.Ping() + if err != nil { + panic(err) + } + + fmt.Println("Successfully connected!") + + for _, command := range setupCommands { + _, err = db.Exec(command) + if err != nil { + panic(err) + } + } + fmt.Println("Done setting up the database") +} + +func createTempDBFile(setupCommands []string) (*os.File){ + file, err := ioutil.TempFile("/tmp", "create-db1") + if err != nil { + panic(err) + } + + fmt.Printf("Database setup file:%s\n", file.Name()) + + for _, command := range setupCommands { + //fmt.Printf("Command: %v\n", command) + // TODO: Interpolation of variables + file.WriteString(command) + file.WriteString("\n") + } + file.Sync() + file.Close() + return file +} + +// podList returns a v1.PodList object +func getPods() *apiv1.PodList { + return &apiv1.PodList{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + } +} + +func int32Ptr(i int32) *int32 { return &i } + +// newbusyBoxPod demonstrates how to create a busybox pod +func newbusyBoxPod(cr *postgresv1.Postgres) *corev1.Pod { + labels := map[string]string{ + "app": "busy-box", + } + return &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "busy-box", + Namespace: cr.Namespace, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(cr, schema.GroupVersionKind{ + Group: v1.SchemeGroupVersion.Group, + Version: v1.SchemeGroupVersion.Version, + Kind: "Postgres", + }), + }, + Labels: labels, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "busybox", + Image: "busybox", + Command: []string{"sleep", "3600"}, + }, + }, + }, + } +} + + diff --git a/postgres-operator-sdk-new/pkg/controller/postgres/user.go b/postgres-operator-sdk-new/pkg/controller/postgres/user.go new file mode 100644 index 0000000..5bb4846 --- /dev/null +++ b/postgres-operator-sdk-new/pkg/controller/postgres/user.go @@ -0,0 +1,98 @@ +package postgres + +import ( + "fmt" + "strings" + postgresv1 "labs/postgres-operator-sdk-new/pkg/apis/postgresoperatorsdknew/v1" +) + +func getCreateUserCommands(desiredList []postgresv1.UserSpec) []string { + var cmdList []string + for _, user := range desiredList { + username := user.User + password := user.Password + createUserCmd := strings.Fields("create user " + username + " with password '" + password + "';") + var cmdString = strings.Join(createUserCmd, " ") + fmt.Printf("CreateUserCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} + +func getDropUserCommands(desiredList []postgresv1.UserSpec) []string { + var cmdList []string + for _, user := range desiredList { + username := user.User + dropUserCmd := strings.Fields("drop user " + username + ";") + var cmdString = strings.Join(dropUserCmd, " ") + fmt.Printf("DropUserCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} + +func getAlterUserCommands(desiredList []postgresv1.UserSpec) []string { + var cmdList []string + for _, user := range desiredList { + username := user.User + password := user.Password + dropUserCmd := strings.Fields("alter user " + username + " with password '" + password + "';") + var cmdString = strings.Join(dropUserCmd, " ") + fmt.Printf("AlterUserCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} + +func getUserDiffList(desired []postgresv1.UserSpec, current []postgresv1.UserSpec) []postgresv1.UserSpec { + var diffList []postgresv1.UserSpec + for _, v := range desired { + var found bool = false + for _, v1 := range current { + if v.User == v1.User { + found = true + } + } + if !found { + diffList = append(diffList, v) + } + } + //fmt.Printf("-- DiffList: %v--\n", diffList) + return diffList +} + +func getUserCommonList(desired []postgresv1.UserSpec, current []postgresv1.UserSpec) []postgresv1.UserSpec { + var modifyList []postgresv1.UserSpec + for _, v := range desired { + for _, v1 := range current { + if v.User == v1.User { + if v.Password != v1.Password { + modifyList = append(modifyList, v) + } + } + } + } + //fmt.Printf("-- ModifyList: %v--\n", modifyList) + return modifyList +} + +func getUserCommands(desiredList []postgresv1.UserSpec, currentList []postgresv1.UserSpec) ([]string, []string, []string) { + + var createUserCommands []string + var dropUserCommands []string + var alterUserCommands []string + + if len(currentList) == 0 { + createUserCommands = getCreateUserCommands(desiredList) + } else { + addList := getUserDiffList(desiredList, currentList) + createUserCommands = getCreateUserCommands(addList) + + dropList := getUserDiffList(currentList, desiredList) + dropUserCommands = getDropUserCommands(dropList) + + alterList := getUserCommonList(desiredList, currentList) + alterUserCommands = getAlterUserCommands(alterList) + } + return createUserCommands, dropUserCommands, alterUserCommands +} diff --git a/postgres-operator-sdk-new/pkg/controller/postgres/utils.go b/postgres-operator-sdk-new/pkg/controller/postgres/utils.go new file mode 100644 index 0000000..2be3fb4 --- /dev/null +++ b/postgres-operator-sdk-new/pkg/controller/postgres/utils.go @@ -0,0 +1,55 @@ +package postgres + +import ( + "fmt" + "strings" +) + +func getCommandsToRun(actionHistory []string, setupCommands []string) []string { + var commandsToRun []string + for _, v := range setupCommands { + var found bool = false + for _, v1 := range actionHistory { + if v == v1 { + found = true + } + } + if !found { + commandsToRun = append(commandsToRun, v) + } + } + fmt.Printf("-- commandsToRun: %v--\n", commandsToRun) + return commandsToRun +} + +func getDiffList(desired []string, current []string) []string { + var diffList []string + for _, v := range desired { + var found bool = false + for _, v1 := range current { + if v == v1 { + found = true + } + } + if !found { + diffList = append(diffList, v) + } + } + //fmt.Printf("-- DiffList: %v--\n", diffList) + return diffList +} + +func canonicalize(setupCommands1 []string) []string { + var setupCommands []string + //Convert setupCommands to Lower case + for _, cmd := range setupCommands1 { + setupCommands = append(setupCommands, strings.ToLower(cmd)) + } + return setupCommands +} + +func appendList(parentList *[]string, childList []string) { + for _, val := range childList { + *parentList = append(*parentList, val) + } +} diff --git a/postgres-operator-sdk-new/version/version.go b/postgres-operator-sdk-new/version/version.go new file mode 100644 index 0000000..e3e130b --- /dev/null +++ b/postgres-operator-sdk-new/version/version.go @@ -0,0 +1,5 @@ +package version + +var ( + Version = "0.0.1" +) diff --git a/postgres-operator-sdk/Gopkg.lock b/postgres-operator-sdk/Gopkg.lock new file mode 100644 index 0000000..1d3b0e9 --- /dev/null +++ b/postgres-operator-sdk/Gopkg.lock @@ -0,0 +1,698 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:5c3894b2aa4d6bead0ceeea6831b305d62879c871780e7b76296ded1b004bc57" + name = "cloud.google.com/go" + packages = ["compute/metadata"] + pruneopts = "NUT" + revision = "dfffe386c33fb24c34ee501e5723df5b97b98514" + version = "v0.30.0" + +[[projects]] + digest = "1:d8ebbd207f3d3266d4423ce4860c9f3794956306ded6c7ba312ecc69cdfbf04c" + name = "github.com/PuerkitoBio/purell" + packages = ["."] + pruneopts = "NUT" + revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4" + version = "v1.1.0" + +[[projects]] + branch = "master" + digest = "1:8098cd40cd09879efbf12e33bcd51ead4a66006ac802cd563a66c4f3373b9727" + name = "github.com/PuerkitoBio/urlesc" + packages = ["."] + pruneopts = "NUT" + revision = "de5bf2ad457846296e2031421a34e2568e304e35" + +[[projects]] + branch = "master" + digest = "1:707ebe952a8b3d00b343c01536c79c73771d100f63ec6babeaed5c79e2b8a8dd" + name = "github.com/beorn7/perks" + packages = ["quantile"] + pruneopts = "NUT" + revision = "3a771d992973f24aa725d07868b467d1ddfceafb" + +[[projects]] + digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" + name = "github.com/davecgh/go-spew" + packages = ["spew"] + pruneopts = "NUT" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" + +[[projects]] + digest = "1:3537d33c077a9666720dc987fddfecb07270606ac0a58f67abd08e3b252c0a45" + name = "github.com/emicklei/go-restful" + packages = [ + ".", + "log", + ] + pruneopts = "NUT" + revision = "3eb9738c1697594ea6e71a7156a9bb32ed216cf0" + version = "v2.8.0" + +[[projects]] + digest = "1:81466b4218bf6adddac2572a30ac733a9255919bc2f470b4827a317bd4ee1756" + name = "github.com/ghodss/yaml" + packages = ["."] + pruneopts = "NUT" + revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" + version = "v1.0.0" + +[[projects]] + digest = "1:260f7ebefc63024c8dfe2c9f1a2935a89fa4213637a1f522f592f80c001cc441" + name = "github.com/go-openapi/jsonpointer" + packages = ["."] + pruneopts = "NUT" + revision = "ef5f0afec364d3b9396b7b77b43dbe26bf1f8004" + version = "v0.17.0" + +[[projects]] + digest = "1:98abd61947ff5c7c6fcfec5473d02a4821ed3a2dd99a4fbfdb7925b0dd745546" + name = "github.com/go-openapi/jsonreference" + packages = ["."] + pruneopts = "NUT" + revision = "8483a886a90412cd6858df4ea3483dce9c8e35a3" + version = "v0.17.0" + +[[projects]] + digest = "1:dfab391de021809e0041f0ab5648da6b74dd16a685472a1b8c3dc06b3dca1ee2" + name = "github.com/go-openapi/spec" + packages = ["."] + pruneopts = "NUT" + revision = "5bae59e25b21498baea7f9d46e9c147ec106a42e" + version = "v0.17.0" + +[[projects]] + digest = "1:983f95b2fae6fe8fdd361738325ed6090f4f3bd15ce4db745e899fb5b0fdfc46" + name = "github.com/go-openapi/swag" + packages = ["."] + pruneopts = "NUT" + revision = "5899d5c5e619fda5fa86e14795a835f473ca284c" + version = "v0.17.0" + +[[projects]] + digest = "1:8679b8a64f3613e9749c5640c3535c83399b8e69f67ce54d91dc73f6d77373af" + name = "github.com/gogo/protobuf" + packages = [ + "proto", + "sortkeys", + ] + pruneopts = "NUT" + revision = "636bf0302bc95575d69441b25a2603156ffdddf1" + version = "v1.1.1" + +[[projects]] + branch = "master" + digest = "1:e2b86e41f3d669fc36b50d31d32d22c8ac656c75aa5ea89717ce7177e134ff2a" + name = "github.com/golang/glog" + packages = ["."] + pruneopts = "NUT" + revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" + +[[projects]] + digest = "1:63ccdfbd20f7ccd2399d0647a7d100b122f79c13bb83da9660b1598396fd9f62" + name = "github.com/golang/protobuf" + packages = [ + "proto", + "ptypes", + "ptypes/any", + "ptypes/duration", + "ptypes/timestamp", + ] + pruneopts = "NUT" + revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" + version = "v1.2.0" + +[[projects]] + branch = "master" + digest = "1:05f95ffdfcf651bdb0f05b40b69e7f5663047f8da75c72d58728acb59b5cc107" + name = "github.com/google/btree" + packages = ["."] + pruneopts = "NUT" + revision = "4030bb1f1f0c35b30ca7009e9ebd06849dd45306" + +[[projects]] + branch = "master" + digest = "1:52c5834e2bebac9030c97cc0798ac11c3aa8a39f098aeb419f142533da6cd3cc" + name = "github.com/google/gofuzz" + packages = ["."] + pruneopts = "NUT" + revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1" + +[[projects]] + digest = "1:06a7dadb7b760767341ffb6c8d377238d68a1226f2b21b5d497d2e3f6ecf6b4e" + name = "github.com/googleapis/gnostic" + packages = [ + "OpenAPIv2", + "compiler", + "extensions", + ] + pruneopts = "NUT" + revision = "7c663266750e7d82587642f65e60bc4083f1f84e" + version = "v0.2.0" + +[[projects]] + branch = "master" + digest = "1:7fdf3223c7372d1ced0b98bf53457c5e89d89aecbad9a77ba9fcc6e01f9e5621" + name = "github.com/gregjones/httpcache" + packages = [ + ".", + "diskcache", + ] + pruneopts = "NUT" + revision = "9cad4c3443a7200dd6400aef47183728de563a38" + +[[projects]] + digest = "1:b42cde0e1f3c816dd57f57f7bbcf05ca40263ad96f168714c130c611fc0856a6" + name = "github.com/hashicorp/golang-lru" + packages = [ + ".", + "simplelru", + ] + pruneopts = "NUT" + revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768" + version = "v0.5.0" + +[[projects]] + digest = "1:9a52adf44086cead3b384e5d0dbf7a1c1cce65e67552ee3383a8561c42a18cd3" + name = "github.com/imdario/mergo" + packages = ["."] + pruneopts = "NUT" + revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4" + version = "v0.3.6" + +[[projects]] + digest = "1:8e36686e8b139f8fe240c1d5cf3a145bc675c22ff8e707857cdd3ae17b00d728" + name = "github.com/json-iterator/go" + packages = ["."] + pruneopts = "NUT" + revision = "1624edc4454b8682399def8740d46db5e4362ba4" + version = "v1.1.5" + +[[projects]] + digest = "1:4059c14e87a2de3a434430340521b5feece186c1469eff0834c29a63870de3ed" + name = "github.com/konsorten/go-windows-terminal-sequences" + packages = ["."] + pruneopts = "NUT" + revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242" + version = "v1.0.1" + +[[projects]] + branch = "master" + digest = "1:84a5a2b67486d5d67060ac393aa255d05d24ed5ee41daecd5635ec22657b6492" + name = "github.com/mailru/easyjson" + packages = [ + "buffer", + "jlexer", + "jwriter", + ] + pruneopts = "NUT" + revision = "60711f1a8329503b04e1c88535f419d0bb440bff" + +[[projects]] + digest = "1:5985ef4caf91ece5d54817c11ea25f182697534f8ae6521eadcd628c142ac4b6" + name = "github.com/matttproud/golang_protobuf_extensions" + packages = ["pbutil"] + pruneopts = "NUT" + revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" + version = "v1.0.1" + +[[projects]] + digest = "1:2f42fa12d6911c7b7659738758631bec870b7e9b4c6be5444f963cdcfccc191f" + name = "github.com/modern-go/concurrent" + packages = ["."] + pruneopts = "NUT" + revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" + version = "1.0.3" + +[[projects]] + digest = "1:c6aca19413b13dc59c220ad7430329e2ec454cc310bc6d8de2c7e2b93c18a0f6" + name = "github.com/modern-go/reflect2" + packages = ["."] + pruneopts = "NUT" + revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" + version = "1.0.1" + +[[projects]] + branch = "master" + digest = "1:1b840e172a4dd78f2638a662264f4d8538384dced8b57cb0b9b739c3f361f7ea" + name = "github.com/operator-framework/operator-sdk" + packages = [ + "pkg/k8sclient", + "pkg/sdk", + "pkg/sdk/internal/metrics", + "pkg/util/k8sutil", + "version", + ] + pruneopts = "NUT" + revision = "65db9c4cbd762449423e7d50ef9333c3e972d874" + +[[projects]] + branch = "master" + digest = "1:3bf17a6e6eaa6ad24152148a631d18662f7212e21637c2699bff3369b7f00fa2" + name = "github.com/petar/GoLLRB" + packages = ["llrb"] + pruneopts = "NUT" + revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" + +[[projects]] + digest = "1:6c6d91dc326ed6778783cff869c49fb2f61303cdd2ebbcf90abe53505793f3b6" + name = "github.com/peterbourgon/diskv" + packages = ["."] + pruneopts = "NUT" + revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" + version = "v2.0.1" + +[[projects]] + digest = "1:03bca087b180bf24c4f9060775f137775550a0834e18f0bca0520a868679dbd7" + name = "github.com/prometheus/client_golang" + packages = [ + "prometheus", + "prometheus/promhttp", + ] + pruneopts = "NUT" + revision = "c5b7fccd204277076155f10851dad72b76a49317" + version = "v0.8.0" + +[[projects]] + branch = "master" + digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" + name = "github.com/prometheus/client_model" + packages = ["go"] + pruneopts = "NUT" + revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f" + +[[projects]] + branch = "master" + digest = "1:fad5a35eea6a1a33d6c8f949fbc146f24275ca809ece854248187683f52cc30b" + name = "github.com/prometheus/common" + packages = [ + "expfmt", + "internal/bitbucket.org/ww/goautoneg", + "model", + ] + pruneopts = "NUT" + revision = "c7de2306084e37d54b8be01f3541a8464345e9a5" + +[[projects]] + branch = "master" + digest = "1:102dea0c03a915acfc634b7c67f2662012b5483b56d9025e33f5188e112759b6" + name = "github.com/prometheus/procfs" + packages = [ + ".", + "internal/util", + "nfs", + "xfs", + ] + pruneopts = "NUT" + revision = "185b4288413d2a0dd0806f78c90dde719829e5ae" + +[[projects]] + digest = "1:ecf78eacf406c42f07f66d6b79fda24d2b92dc711bfd0760d0c931678f9621fe" + name = "github.com/sirupsen/logrus" + packages = ["."] + pruneopts = "NUT" + revision = "ad15b42461921f1fb3529b058c6786c6a45d5162" + version = "v1.1.1" + +[[projects]] + digest = "1:9d8420bbf131d1618bde6530af37c3799340d3762cc47210c1d9532a4c3a2779" + name = "github.com/spf13/pflag" + packages = ["."] + pruneopts = "NUT" + revision = "298182f68c66c05229eb03ac171abe6e309ee79a" + version = "v1.0.3" + +[[projects]] + branch = "master" + digest = "1:3f3a05ae0b95893d90b9b3b5afdb79a9b3d96e4e36e099d841ae602e4aca0da8" + name = "golang.org/x/crypto" + packages = ["ssh/terminal"] + pruneopts = "NUT" + revision = "a92615f3c49003920a58dedcf32cf55022cefb8d" + +[[projects]] + branch = "master" + digest = "1:2fa379f32ae3b5110f7f4393c98200804c0934d8b24087e433cda464eb5d7859" + name = "golang.org/x/net" + packages = [ + "context", + "context/ctxhttp", + "http/httpguts", + "http2", + "http2/hpack", + "idna", + ] + pruneopts = "NUT" + revision = "49bb7cea24b1df9410e1712aa6433dae904ff66a" + +[[projects]] + branch = "master" + digest = "1:dcb89c032286a9c3c5118a1496f8e0e237c1437f5356ac9602f6fdef560a5c21" + name = "golang.org/x/oauth2" + packages = [ + ".", + "google", + "internal", + "jws", + "jwt", + ] + pruneopts = "NUT" + revision = "c57b0facaced709681d9f90397429b9430a74754" + +[[projects]] + branch = "master" + digest = "1:71bcac4a94abe8132ef9c2bf008cca9579cda20194383b210b8d2e125777e982" + name = "golang.org/x/sys" + packages = [ + "unix", + "windows", + ] + pruneopts = "NUT" + revision = "fa43e7bc11baaae89f3f902b2b4d832b68234844" + +[[projects]] + digest = "1:e33513a825fcd765e97b5de639a2f7547542d1a8245df0cef18e1fd390b778a9" + 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", + "width", + ] + pruneopts = "NUT" + revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" + version = "v0.3.0" + +[[projects]] + branch = "master" + digest = "1:c9e7a4b4d47c0ed205d257648b0e5b0440880cb728506e318f8ac7cd36270bc4" + name = "golang.org/x/time" + packages = ["rate"] + pruneopts = "NUT" + revision = "fbb02b2291d28baffd63558aa44b4b56f178d650" + +[[projects]] + branch = "master" + digest = "1:cbc1eeeae3ba77f83a567a6fbd8589ae76be165296bbffa8c58ab31c5ae5f1b0" + name = "golang.org/x/tools" + packages = [ + "go/ast/astutil", + "imports", + "internal/fastwalk", + "internal/gopathwalk", + ] + pruneopts = "NUT" + revision = "5e66757b835f155f7e50931f54c9f6af8af86f75" + +[[projects]] + digest = "1:e2da54c7866453ac5831c61c7ec5d887f39328cac088c806553303bff4048e6f" + name = "google.golang.org/appengine" + packages = [ + ".", + "internal", + "internal/app_identity", + "internal/base", + "internal/datastore", + "internal/log", + "internal/modules", + "internal/remote_api", + "internal/urlfetch", + "urlfetch", + ] + pruneopts = "NUT" + revision = "ae0ab99deb4dc413a2b4bd6c8bdd0eb67f1e4d06" + version = "v1.2.0" + +[[projects]] + digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a" + name = "gopkg.in/inf.v0" + packages = ["."] + pruneopts = "NUT" + revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf" + version = "v0.9.1" + +[[projects]] + digest = "1:7c95b35057a0ff2e19f707173cc1a947fa43a6eb5c4d300d196ece0334046082" + name = "gopkg.in/yaml.v2" + packages = ["."] + pruneopts = "NUT" + revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" + version = "v2.2.1" + +[[projects]] + digest = "1:ef716a2116d8a040e16fbcd7fca71d3354915a94720de6af22c7a09970234296" + 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 = "NUT" + revision = "2d6f90ab1293a1fb871cf149423ebb72aa7423aa" + +[[projects]] + digest = "1:69b102c3ee60ab3704fac6e46bac1e8894e20b11498ec832846a229f21946200" + 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/net", + "pkg/util/runtime", + "pkg/util/sets", + "pkg/util/validation", + "pkg/util/validation/field", + "pkg/util/wait", + "pkg/util/yaml", + "pkg/version", + "pkg/watch", + "third_party/forked/golang/reflect", + ] + pruneopts = "NUT" + revision = "103fd098999dc9c0c88536f5c9ad2e5da39373ae" + +[[projects]] + digest = "1:6cca3c9f626aeb165dad88de9db9b71585a92514134f466268dd83fd449df9a7" + name = "k8s.io/client-go" + packages = [ + "discovery", + "discovery/cached", + "dynamic", + "kubernetes", + "kubernetes/scheme", + "kubernetes/typed/admissionregistration/v1alpha1", + "kubernetes/typed/admissionregistration/v1beta1", + "kubernetes/typed/apps/v1", + "kubernetes/typed/apps/v1beta1", + "kubernetes/typed/apps/v1beta2", + "kubernetes/typed/authentication/v1", + "kubernetes/typed/authentication/v1beta1", + "kubernetes/typed/authorization/v1", + "kubernetes/typed/authorization/v1beta1", + "kubernetes/typed/autoscaling/v1", + "kubernetes/typed/autoscaling/v2beta1", + "kubernetes/typed/batch/v1", + "kubernetes/typed/batch/v1beta1", + "kubernetes/typed/batch/v2alpha1", + "kubernetes/typed/certificates/v1beta1", + "kubernetes/typed/core/v1", + "kubernetes/typed/events/v1beta1", + "kubernetes/typed/extensions/v1beta1", + "kubernetes/typed/networking/v1", + "kubernetes/typed/policy/v1beta1", + "kubernetes/typed/rbac/v1", + "kubernetes/typed/rbac/v1alpha1", + "kubernetes/typed/rbac/v1beta1", + "kubernetes/typed/scheduling/v1alpha1", + "kubernetes/typed/scheduling/v1beta1", + "kubernetes/typed/settings/v1alpha1", + "kubernetes/typed/storage/v1", + "kubernetes/typed/storage/v1alpha1", + "kubernetes/typed/storage/v1beta1", + "pkg/apis/clientauthentication", + "pkg/apis/clientauthentication/v1alpha1", + "pkg/apis/clientauthentication/v1beta1", + "pkg/version", + "plugin/pkg/client/auth/exec", + "plugin/pkg/client/auth/gcp", + "rest", + "rest/watch", + "restmapper", + "third_party/forked/golang/template", + "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/jsonpath", + "util/retry", + "util/workqueue", + ] + pruneopts = "NUT" + revision = "1f13a808da65775f22cbf47862c4e5898d8f4ca1" + +[[projects]] + digest = "1:8ab487a323486c8bbbaa3b689850487fdccc6cbea8690620e083b2d230a4447e" + name = "k8s.io/code-generator" + packages = [ + "cmd/client-gen", + "cmd/client-gen/args", + "cmd/client-gen/generators", + "cmd/client-gen/generators/fake", + "cmd/client-gen/generators/scheme", + "cmd/client-gen/generators/util", + "cmd/client-gen/path", + "cmd/client-gen/types", + "cmd/conversion-gen", + "cmd/conversion-gen/args", + "cmd/conversion-gen/generators", + "cmd/deepcopy-gen", + "cmd/deepcopy-gen/args", + "cmd/defaulter-gen", + "cmd/defaulter-gen/args", + "cmd/informer-gen", + "cmd/informer-gen/args", + "cmd/informer-gen/generators", + "cmd/lister-gen", + "cmd/lister-gen/args", + "cmd/lister-gen/generators", + "cmd/openapi-gen", + "cmd/openapi-gen/args", + "pkg/util", + ] + pruneopts = "T" + revision = "6702109cc68eb6fe6350b83e14407c8d7309fd1a" + +[[projects]] + branch = "master" + digest = "1:5249c83f0fb9e277b2d28c19eca814feac7ef05dc762e4deaf0a2e4b1a7c5df3" + name = "k8s.io/gengo" + packages = [ + "args", + "examples/deepcopy-gen/generators", + "examples/defaulter-gen/generators", + "examples/set-gen/sets", + "generator", + "namer", + "parser", + "types", + ] + pruneopts = "NUT" + revision = "4242d8e6c5dba56827bb7bcf14ad11cda38f3991" + +[[projects]] + branch = "master" + digest = "1:11a6835553568158414400041e840b68e1128453e65240a986f197605b0345eb" + name = "k8s.io/kube-openapi" + packages = [ + "cmd/openapi-gen/args", + "pkg/common", + "pkg/generators", + "pkg/generators/rules", + "pkg/util/sets", + ] + pruneopts = "NUT" + revision = "9dfdf9be683f61f82cda12362c44c784e0778b56" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/operator-framework/operator-sdk/pkg/sdk", + "github.com/operator-framework/operator-sdk/pkg/util/k8sutil", + "github.com/operator-framework/operator-sdk/version", + "github.com/prometheus/client_golang/prometheus", + "github.com/sirupsen/logrus", + "k8s.io/api/core/v1", + "k8s.io/apimachinery/pkg/api/errors", + "k8s.io/apimachinery/pkg/apis/meta/v1", + "k8s.io/apimachinery/pkg/runtime", + "k8s.io/apimachinery/pkg/runtime/schema", + "k8s.io/client-go/plugin/pkg/client/auth/gcp", + "k8s.io/code-generator/cmd/client-gen", + "k8s.io/code-generator/cmd/conversion-gen", + "k8s.io/code-generator/cmd/deepcopy-gen", + "k8s.io/code-generator/cmd/defaulter-gen", + "k8s.io/code-generator/cmd/informer-gen", + "k8s.io/code-generator/cmd/lister-gen", + "k8s.io/code-generator/cmd/openapi-gen", + "k8s.io/gengo/args", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/postgres-operator-sdk/Gopkg.toml b/postgres-operator-sdk/Gopkg.toml new file mode 100644 index 0000000..c71557a --- /dev/null +++ b/postgres-operator-sdk/Gopkg.toml @@ -0,0 +1,58 @@ + +# Force dep to vendor the code generators, which aren't imported just used at dev time. +# Picking a subpackage with Go code won't be necessary once https://github.com/golang/dep/pull/1545 is merged. +required = [ + "k8s.io/code-generator/cmd/defaulter-gen", + "k8s.io/code-generator/cmd/deepcopy-gen", + "k8s.io/code-generator/cmd/conversion-gen", + "k8s.io/code-generator/cmd/client-gen", + "k8s.io/code-generator/cmd/lister-gen", + "k8s.io/code-generator/cmd/informer-gen", + "k8s.io/code-generator/cmd/openapi-gen", + "k8s.io/gengo/args", +] + +[[override]] + name = "k8s.io/code-generator" + # revision for tag "kubernetes-1.11.2" + revision = "6702109cc68eb6fe6350b83e14407c8d7309fd1a" + +[[override]] + name = "k8s.io/api" + # revision for tag "kubernetes-1.11.2" + revision = "2d6f90ab1293a1fb871cf149423ebb72aa7423aa" + +[[override]] + name = "k8s.io/apiextensions-apiserver" + # revision for tag "kubernetes-1.11.2" + revision = "408db4a50408e2149acbd657bceb2480c13cb0a4" + +[[override]] + name = "k8s.io/apimachinery" + # revision for tag "kubernetes-1.11.2" + revision = "103fd098999dc9c0c88536f5c9ad2e5da39373ae" + +[[override]] + name = "k8s.io/client-go" + # revision for tag "kubernetes-1.11.2" + revision = "1f13a808da65775f22cbf47862c4e5898d8f4ca1" + +[[override]] + name = "sigs.k8s.io/controller-runtime" + version = "v0.1.3" + +[prune] + go-tests = true + non-go = true + unused-packages = true + + [[prune.project]] + name = "k8s.io/code-generator" + non-go = false + unused-packages = false + +[[constraint]] + name = "github.com/operator-framework/operator-sdk" + # The version rule is used for a specific release and the master branch for in between releases. + branch = "master" #osdk_branch_annotation + # version = "=v0.0.6" #osdk_version_annotation diff --git a/postgres-operator-sdk/README.md b/postgres-operator-sdk/README.md new file mode 100644 index 0000000..68a602a --- /dev/null +++ b/postgres-operator-sdk/README.md @@ -0,0 +1,288 @@ +# postgres-operator +PostgreSQL Operator generated using Operator SDK library. + +## Operator SDK +This project makes use of the Operator SDK library to create a Kubernetes operator that +will handle and watch the creation of Postgres resources. + +## Requirements + +* [Minikube](https://kubernetes.io/docs/setup/minikube/) - For testing on a local cluster. +* kubectl - To interact with Kubernetes cluster. +* [golang](https://golang.org/dl/) - Language used for Operator development and deployment. +* PostgreSQL - Execute SQL queries locally that interact with the Postgres deployment. + +## How to test: + +After running the Minikube cluster, go into pkg/stub/handler.go and modify the variable MINIKUBE_IP to match the IP address +of your Minikube cluster. + +To find the IP address of your Minikube cluster run: + +``` +$ minikube ip +``` + +Before running the Operator make sure the CRD is applied to the cluster as a supported API resource: + +``` +$ kubectl create -f deploy/crd.yaml +``` + +Run the Operator locally: + +``` +$ operator-sdk up local --kubeconfig=$HOME/.kube/config +``` + +NOTE: The flag --kubeconfig redirects the operator to be configured to interact with minikube. + The handler needs to know where minikube is and how to execute functions to interact with + minikube. The above path is the default path to the .kube/config. If for whatever reason + your path is different, make the necessary changes to match your path. + +Open a new terminal while the Operator is running in the other one. + +Run the following command to begin testing: + +``` +$ kubectl create -f deploy/client1.yaml +``` + +The above command creates a Postgres instance named client1 that contains a Postgres image +and several Postgres queries to be ran upon launch of the instance. +The Postgres Operator should catch this deployment and begin creating the corresponding Deployment and Service objects. + +Run the following commands to verify that the operator is doing what it is supposed to do: + +``` +$ kubectl get crd +$ kubectl get postgres client1 +$ kubectl describe postgres client1 +$ psql -h -p -U -d +``` +For the last command above, plug in the values of the IP and port numbers generated in the Operator output +as per service object availability. +For the username and db-name, go to deploy/client1.yaml and use the values for the username and database specified. +When prompted for password, use the password in the client1.yaml as well. + +## Acknowledgements +This Postgres operator is completely based off of Cloud-Ark's [Postgres Custom Controller](https://github.com/cloud-ark/kubeplus/tree/master/postgres-crd-v2) as is most of the code in the Handler.go file + +------ + +## Purpose of Experiment + +--> Talk about the hypothesis of the experiment that Operator SDK might be less performant than sample-controller. +Give the reason why we think this might be the case. Refer to the Github issue about this from Operator SDK repo. +Refer to CloudARK blog on 'Writing Kubernetes Custom Controller' +to build your understanding of Listers, Informers, etc. +Then present your understanding of what a Lister is and why you think Operator SDK might be less performant. +Point out specific places in the Operator SDK code where you see the problem. + +After reading the Operator SDK library and the different CRUD functions inplemented in the sdk file, it became apparent that +the SDK library does not use Listers upon performing CRUD operations and instead uses dynamic clientsets. + +Listers play a role in serving as objects that allow indexing into a client side cache for Kubernetes resources. Rather than write to +the server upon every call performed, the Lister allows for changes and retrievals to be made on the client side in which changes to the +resource are later synced with the server. + +For an overview of the Custom Controller architecture, refer to the [Cloud-ARK](https://medium.com/@cloudark/kubernetes-custom-controllers-b6c7d0668fdf) blog post. + +[Issues](https://github.com/operator-framework/operator-sdk/issues/258) related to the usage of Clientsets in Operator SDK. + +To learn more about how Operator SDK uses Clinetsets with CRUD operations refer to operator-framework/operator-sdk/pkg/sdk/action.go in the SDK [Github](https://github.com/operator-framework/operator-sdk/) repo. + + +## Testing the hypothesis + +The experiment that will be performed involves modifying the Kubernetes open source project to a point that allows us to distunguish when an API call tied to our +Postgres Operator is made. + +We will then run an experiment that measures how many API calls are made when performing different actions such as Creating a Databse or Adding a user to our Postgres cluster. + +The way we will compare our Operator SDK results is by measuring it against the performance of a Postgres Sample Controller implementation. This implementation involves the usage of Listers instead of Clientsets when performing CRUD actions. For more information refer to Cloud-ARK's [implementation](https://github.com/cloud-ark/kubeplus/tree/master/postgres-crd-v2). + +After measuring the perforamance, another experiment will be conducted using a modified version of the Postgres Operator SDK implementation, but this time all SDK library functions will be replaced with Lister calls. This will allow us to solidify our experiment by narrowing down the differing number of API calls to certain code portions. + +## Experiment Setup + + +### Experiment Resources +* AWS EC2 Instance - We will run the modified Kubernetes cluster here. +* [Kubernetes Setup Guide](https://dzone.com/articles/easy-step-by-step-local-kubernetes-source-code-cha) +* [PostgresController](https://github.com/NoahElzein/postgrescontroller) + +### Experiment Steps +* Setup AWS EC2 Instance. +* Setup Kubernetes on EC2 instance and make sure it works. +* Modify Kubernetes to allow for API call detection. +* Clone Postgres Operator repo. +* Make sure all dependencies are satisfied. +* Begin experiment. + +### Setup AWS EC2 Instance +Go to AWS and create an EC2 instance with these specifications: +- Ubuntu 18.04 image +- m5d.xlarge instance +- A minimum requirement of 16GB of RAM is required. The m5d.xlarge satisfies this requirement and offers around 150GB +of SSD storage which should suffice. + +### Setup Kubernetes on EC2 Instance and make sure it works +Follow the article referred to above for detailed steps in initializing Kubernetes on your AWS instance. +[Link Here](https://dzone.com/articles/easy-step-by-step-local-kubernetes-source-code-cha) + +### Modify Kubernetes to allow for API call detection +Turn off Kubernetes instance if currently running. +Navigate to kubernetes/staging/src/k8s.io/apiserver/pkg/server/filters/ +Open the wrap.go file. +Find this line: +``` +logger := httplog.NewLogged(req, &w) +``` +Right before the above line, insert this code: +``` +urlPath := req.URL.String() + +if strings.Contains(urlPath, "postgres") { + glog.Infof("==========================================================") + glog.Infof("Hello hello Postgreses") + glog.Infof("==========================================================") +} +``` + +Note: Do not forget to import the strings library into wrap.go. + +## Clone Postgres Operator repo +After setting up Kubernetes, we need to setup our repo. The guide in the article also deals with setting up the $GOPATH +so a lot of the work is done thankfully. Clone the repo using: + +``` +git clone https://github.com/NoahElzein/postgrescontroller +``` + +We need to install two dependencies for this repo. + +[lib/pq](https://github.com/lib/pq) + +Clone the above repo under the github.com folder. + +``` +git clone https://github.com/lib/pq.git +``` + +OPTIONAL: You can install the Postgres command line tool to verify that Postgres container is running on Kubernetes: + +``` +sudo apt-get install postgresql postgresql-contrib +``` + +## Begin Experiment + +--> Say that you will need 4 windows - create them. + +Run the Kubernetes cluster as referred to in the article about setting up Kubernetes. + +Run the Postgres Operator in a separate window as described earlier in the post. + +Access the tmp folder and tail the apiserver logs in a third terminal window. + +``` +tail -f kube-apiserver.log +``` + +Open a fourth terminal terminal window + +Go into the Postgres Operator project + +Register a Postgres object using the kubectl command: + +``` +kubectl create -f deploy/crd.yaml +``` + +You should see the message we inserted in wrap.go pop up in the log file (The log terminal window). + +Now it is time to begin the experiment: + +The way we will measure performance of the library is based on the number of API calls that appear over a period of time during experimentation. The reason for this is because at the moment there is no way to differentiate between Resync API calls and API calls resulting from Library calls in Postgrescontroller. + +To begin the experiment, run the Postgres Operator as defined by the steps above. + +The format I followed was a 5 minute interval involving creation of DB as well as Users in the Postgrescontroller. + +Minute 0: Turn on Kubernetes cluster and record calls. + +Minute 2: Create this resource: + +``` +kubectl create -f deploy/initializeclient.yaml +``` + +Minute 3: +``` +kubectl create -f deploy/add-db.yaml +``` + +Minute 4: + +``` +kubectl create -f deploy/add-user.yaml +``` + +Minute 5: Conclude experiment. + +For comparison, we ran the same experiment with Postgres custom-controller based on sample-controller. + + +Clone this repository [Custom Controller](https://github.com/cloud-ark/kubeplus/tree/master/postgres-crd-v2): + +``` +git clone https://github.com/cloud-ark/kubeplus/tree/master/postgres-crd.git +``` + +Follow the steps outlined in the Github repo. No major changes are required. + +Perform the same experiment on the Sample Postgres Controller. + + +## Results + +Create a table with following rows and columns: + +Rows: +- postgres based on sample-controller +- postgres with Operator SDK without Listers +- postgres with Operator SDK with Listers + +Columns: +- Number of API calls for initializeclient +- Number of API calls for add-db +- Number of API calls for add-user + +Is it possible for you to distinguish above three calls in your log file? I think it should be possible. +Then the way you want to fill above columns is as follows: +- for each run of the experiment calculate calls for initializeclient and then take the average over 3 runs +- for each run of the experiment calculate calls for add-db and then take the average over 3 runs +- for each run of the experiment calculate calls for add-user and then take the average over 3 runs + + +## Experience of using Operator SDK + +While running the experiment originally, I faced a challenge in dealing with circulating events. This involved events that kept circulating in the queue even after being dealt with. This added unnecessary overhead and I had to manually code in a way to ignore this event from circulating. (Relevant issues [Issue #335](https://github.com/operator-framework/operator-sdk/issues/335), [Issue 268](https://github.com/operator-framework/operator-sdk/issues/268)) + +One way I also found to fix this is to set the resyncPeriod to 0 in the main.go file. --> Add reference where this suggestion was made + + +## Conclusion + +--> Comment about what you learned from this research. + +What are the next steps? Is there any recommendation you can make to +the Operator developers? (for this focus on your experience of working with Operator SDK -- how easy/difficult was it, +what are some of the things on which they may trip). + + + + + + diff --git a/postgres-operator-sdk/cmd/postgres-operator-sdk/main.go b/postgres-operator-sdk/cmd/postgres-operator-sdk/main.go new file mode 100644 index 0000000..fc37655 --- /dev/null +++ b/postgres-operator-sdk/cmd/postgres-operator-sdk/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "context" + "runtime" + "time" + + stub "github.com/cloud-ark/labs/postgres-operator-sdk/pkg/stub" + sdk "github.com/operator-framework/operator-sdk/pkg/sdk" + k8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil" + sdkVersion "github.com/operator-framework/operator-sdk/version" + + "github.com/sirupsen/logrus" + "fmt" +) + +func printVersion() { + logrus.Infof("Go Version: %s", runtime.Version()) + logrus.Infof("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH) + logrus.Infof("operator-sdk Version: %v", sdkVersion.Version) +} + +func main() { + printVersion() + + sdk.ExposeMetricsPort() + + resource := "postgres-operator-sdk.kubeplus/v1" + kind := "Postgres" + namespace, err := k8sutil.GetWatchNamespace() + if err != nil { + logrus.Fatalf("Failed to get watch namespace: %v", err) + } + resyncPeriod := time.Duration(0) * time.Second + logrus.Infof("Watching %s, %s, %s, %d", resource, kind, namespace, resyncPeriod) + sdk.Watch(resource, kind, namespace, resyncPeriod) + sdk.Handle(stub.NewHandler()) + sdk.Run(context.TODO()) +} diff --git a/postgres-operator-sdk/config/config.yaml b/postgres-operator-sdk/config/config.yaml new file mode 100644 index 0000000..277f96e --- /dev/null +++ b/postgres-operator-sdk/config/config.yaml @@ -0,0 +1,3 @@ +apiVersion: postgres-operator-sdk.kubeplus/v1 +kind: Postgres +projectName: postgres-operator-sdk diff --git a/postgres-operator-sdk/deletecrds.sh b/postgres-operator-sdk/deletecrds.sh new file mode 100755 index 0000000..6ea8c41 --- /dev/null +++ b/postgres-operator-sdk/deletecrds.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +for crd in "$@" +do + kubectl delete deployments "$crd" + kubectl delete services "$crd" + kubectl delete postgres "$crd" +done diff --git a/postgres-operator-sdk/deploy/add-db.yaml b/postgres-operator-sdk/deploy/add-db.yaml new file mode 100644 index 0000000..d189524 --- /dev/null +++ b/postgres-operator-sdk/deploy/add-db.yaml @@ -0,0 +1,11 @@ +apiVersion: postgres-operator-sdk.kubeplus/v1 +kind: Postgres +metadata: + name: client25 +spec: + deploymentName: client25 + image: postgres:9.3 + replicas: 1 + users: [{"username": "devdatta", "password": "pass123"}, + {"username": "pallavi", "password": "pass234"}] + databases: ["moodle", "wordpress", "ecommerce"] diff --git a/postgres-operator-sdk/deploy/add-user.yaml b/postgres-operator-sdk/deploy/add-user.yaml new file mode 100644 index 0000000..982d243 --- /dev/null +++ b/postgres-operator-sdk/deploy/add-user.yaml @@ -0,0 +1,12 @@ +apiVersion: postgres-operator-sdk.kubeplus/v1 +kind: Postgres +metadata: + name: client25 +spec: + deploymentName: client25 + image: postgres:9.3 + replicas: 1 + users: [{"username": "devdatta", "password": "pass123"}, + {"username": "pallavi", "password": "pass234"}, + {"username": "shrinivas", "password": "pass1"}] + databases: ["moodle", "wordpress"] diff --git a/postgres-operator-sdk/deploy/cr.yaml b/postgres-operator-sdk/deploy/cr.yaml new file mode 100644 index 0000000..16a8c7a --- /dev/null +++ b/postgres-operator-sdk/deploy/cr.yaml @@ -0,0 +1,4 @@ +apiVersion: "postgres-operator-sdk.kubeplus/v1" +kind: "Postgres" +metadata: + name: "example" diff --git a/postgres-operator-sdk/deploy/crd.yaml b/postgres-operator-sdk/deploy/crd.yaml new file mode 100644 index 0000000..ad6e97b --- /dev/null +++ b/postgres-operator-sdk/deploy/crd.yaml @@ -0,0 +1,13 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: postgreses.postgres-operator-sdk.kubeplus +spec: + group: postgres-operator-sdk.kubeplus + names: + kind: Postgres + listKind: PostgresList + plural: postgreses + singular: postgres + scope: Namespaced + version: v1 diff --git a/postgres-operator-sdk/deploy/initializeclient.yaml b/postgres-operator-sdk/deploy/initializeclient.yaml new file mode 100644 index 0000000..4588681 --- /dev/null +++ b/postgres-operator-sdk/deploy/initializeclient.yaml @@ -0,0 +1,19 @@ +apiVersion: postgresoperatorsdknew.kubeplus/v1 +kind: Postgres +metadata: + name: client25 +spec: + deploymentName: client25 + image: postgres:9.3 + replicas: 1 + users: [{"username": "devdatta", "password": "pass123"}, + {"username": "pallavi", "password": "pass234"}] + databases: ["moodle"] + initcommands: [ "create table moodle_data1 (items varchar(250));", + "insert into moodle_data1 (items) values ('Moodle data1');", + "insert into moodle_data1 (items) values ('Moodle data1');", + "insert into moodle_data1 (items) values ('Moodle data2');", + "GRANT ALL PRIVILEGES ON TABLE moodle_data1 TO devdatta;", + "GRANT ALL PRIVILEGES ON TABLE moodle_data1 TO pallavi;" + ] + diff --git a/postgres-operator-sdk/deploy/operator.yaml b/postgres-operator-sdk/deploy/operator.yaml new file mode 100644 index 0000000..1a5a7d9 --- /dev/null +++ b/postgres-operator-sdk/deploy/operator.yaml @@ -0,0 +1,31 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres-operator-sdk +spec: + replicas: 1 + selector: + matchLabels: + name: postgres-operator-sdk + template: + metadata: + labels: + name: postgres-operator-sdk + spec: + serviceAccountName: postgres-operator-sdk + containers: + - name: postgres-operator-sdk + image: REPLACE_IMAGE + ports: + - containerPort: 60000 + name: metrics + command: + - postgres-operator-sdk + imagePullPolicy: Always + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: OPERATOR_NAME + value: "postgres-operator-sdk" diff --git a/postgres-operator-sdk/deploy/rbac.yaml b/postgres-operator-sdk/deploy/rbac.yaml new file mode 100644 index 0000000..1f01fe0 --- /dev/null +++ b/postgres-operator-sdk/deploy/rbac.yaml @@ -0,0 +1,46 @@ +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: postgres-operator-sdk +rules: +- apiGroups: + - postgres-operator-sdk.kubeplus + resources: + - "*" + verbs: + - "*" +- apiGroups: + - "" + resources: + - pods + - services + - endpoints + - persistentvolumeclaims + - events + - configmaps + - secrets + verbs: + - "*" +- apiGroups: + - apps + resources: + - deployments + - daemonsets + - replicasets + - statefulsets + verbs: + - "*" + +--- + +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: postgres-operator-sdk +subjects: +- kind: ServiceAccount + name: postgres-operator-sdk +roleRef: + kind: Role + name: postgres-operator-sdk + apiGroup: rbac.authorization.k8s.io diff --git a/postgres-operator-sdk/deploy/sa.yaml b/postgres-operator-sdk/deploy/sa.yaml new file mode 100644 index 0000000..43ce318 --- /dev/null +++ b/postgres-operator-sdk/deploy/sa.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: postgres-operator-sdk diff --git a/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1/doc.go b/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1/doc.go new file mode 100644 index 0000000..b1e84da --- /dev/null +++ b/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1/doc.go @@ -0,0 +1,3 @@ +// +k8s:deepcopy-gen=package +// +groupName=postgres-operator-sdk.kubeplus +package v1 diff --git a/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1/register.go b/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1/register.go new file mode 100644 index 0000000..88947a9 --- /dev/null +++ b/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1/register.go @@ -0,0 +1,35 @@ +package v1 + +import ( + sdkK8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +const ( + version = "v1" + groupName = "postgres-operator-sdk.kubeplus" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme + // SchemeGroupVersion is the group version used to register these objects. + SchemeGroupVersion = schema.GroupVersion{Group: groupName, Version: version} +) + +func init() { + sdkK8sutil.AddToSDKScheme(AddToScheme) +} + +// addKnownTypes adds the set of types defined in this package to the supplied scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Postgres{}, + &PostgresList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1/types.go b/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1/types.go new file mode 100644 index 0000000..4b6a60d --- /dev/null +++ b/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1/types.go @@ -0,0 +1,48 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +genclient:noStatus +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type Postgres struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata"` + Spec PostgresSpec `json:"spec"` + Status PostgresStatus `json:"status,omitempty"` +} + +type UserSpec struct { + User string `json:"username"` + Password string `json:"password"` +} + +type PostgresSpec struct { + DeploymentName string `json:"deploymentName"` + Image string `json:"image"` + Replicas *int32 `json:"replicas"` + Users []UserSpec `json:"users"` + Databases []string `json:"databases"` + Commands []string `json:"initcommands"` +} +type PostgresStatus struct { + AvailableReplicas int32 `json:"availableReplicas"` + ActionHistory []string `json:"actionHistory"` + Users []UserSpec `json:"users"` + Databases []string `json:"databases"` + VerifyCmd string `json:"verifyCommand"` + ServiceIP string `json:"serviceIP"` + ServicePort string `json:"servicePort"` + Status string `json:"status"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type PostgresList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + Items []Postgres `json:"items"` +} diff --git a/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1/zz_generated.deepcopy.go b/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1/zz_generated.deepcopy.go new file mode 100644 index 0000000..ba38c36 --- /dev/null +++ b/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1/zz_generated.deepcopy.go @@ -0,0 +1,102 @@ +// +build !ignore_autogenerated + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Postgres) DeepCopyInto(out *Postgres) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Postgres. +func (in *Postgres) DeepCopy() *Postgres { + if in == nil { + return nil + } + out := new(Postgres) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Postgres) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresList) DeepCopyInto(out *PostgresList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Postgres, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresList. +func (in *PostgresList) DeepCopy() *PostgresList { + if in == nil { + return nil + } + out := new(PostgresList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PostgresList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresSpec. +func (in *PostgresSpec) DeepCopy() *PostgresSpec { + if in == nil { + return nil + } + out := new(PostgresSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresStatus) DeepCopyInto(out *PostgresStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresStatus. +func (in *PostgresStatus) DeepCopy() *PostgresStatus { + if in == nil { + return nil + } + out := new(PostgresStatus) + in.DeepCopyInto(out) + return out +} diff --git a/postgres-operator-sdk/pkg/stub/database.go b/postgres-operator-sdk/pkg/stub/database.go new file mode 100644 index 0000000..03a01d8 --- /dev/null +++ b/postgres-operator-sdk/pkg/stub/database.go @@ -0,0 +1,44 @@ +package stub + +import ( + "fmt" + "strings" +) + +func getDatabaseCommands(desiredList []string, currentList []string) ([]string, []string) { + var createDatabaseCommands []string + var deleteDatabaseCommands []string + + if len(currentList) == 0 { + createDatabaseCommands = getCreateDatabaseCommands(desiredList) + } else { + addList := getDiffList(desiredList, currentList) + createDatabaseCommands = getCreateDatabaseCommands(addList) + + dropList := getDiffList(currentList, desiredList) + deleteDatabaseCommands = getDropDatabaseCommands(dropList) + } + return createDatabaseCommands, deleteDatabaseCommands +} + +func getCreateDatabaseCommands(dbList []string) []string { + var cmdList []string + for _, db := range dbList { + createDBCmd := strings.Fields("create database " + db + ";") + var cmdString = strings.Join(createDBCmd, " ") + fmt.Printf("CreateDBCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} + +func getDropDatabaseCommands(dbList []string) []string { + var cmdList []string + for _, db := range dbList { + dropDBCmd := strings.Fields("drop database " + db + ";") + var cmdString = strings.Join(dropDBCmd, " ") + fmt.Printf("DropDBCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} \ No newline at end of file diff --git a/postgres-operator-sdk/pkg/stub/handler.go b/postgres-operator-sdk/pkg/stub/handler.go new file mode 100644 index 0000000..2760c01 --- /dev/null +++ b/postgres-operator-sdk/pkg/stub/handler.go @@ -0,0 +1,554 @@ +package stub + +import ( + "os" + "io/ioutil" + "context" + "strconv" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/cloud-ark/labs/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1" + + "github.com/operator-framework/operator-sdk/pkg/sdk" + "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + appsv1 "k8s.io/api/apps/v1" + apiv1 "k8s.io/api/core/v1" + apiutil "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/runtime/schema" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "strings" + "fmt" + "time" + + _ "github.com/lib/pq" + + postgresv1 "github.com/cloud-ark/labs/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1" + + "database/sql" +) + +const controllerAgentName = "postgres-controller" + +const ( + // SuccessSynced is used as part of the Event 'reason' when a Foo is synced + SuccessSynced = "Synced" + // ErrResourceExists is used as part of the Event 'reason' when a Foo fails + // to sync due to a Deployment of the same name already existing. + ErrResourceExists = "ErrResourceExists" + + // MessageResourceExists is the message used for Events when a resource + // fails to sync due to a Deployment already existing + MessageResourceExists = "Resource %q already exists and is not managed by Foo" + // MessageResourceSynced is the message used for an Event fired when a Foo + // is synced successfully + MessageResourceSynced = "Foo synced successfully" +) + +const ( + PGPASSWORD = "mysecretpassword" + MINIKUBE_IP = "192.168.99.100" +) + +// define a list var and add an init function +var ( + id_list = []string{} +) + +func NewHandler() sdk.Handler { + return &Handler{} +} + +type Handler struct { + // Fill me +} + +func (h *Handler) Handle(ctx context.Context, event sdk.Event) error { + switch o := event.Object.(type) { + case *v1.Postgres: + foo := o + + fmt.Println("The uid is: " + foo.ResourceVersion) + + for counter := 0; counter < len(id_list); counter++ { + fmt.Println("This element: " + id_list[counter]) + } + + for counter := 0; counter < len(id_list); counter++ { + if id_list[counter] == foo.ResourceVersion { + return nil + } + } + + id_list = append(id_list, foo.ResourceVersion) + + deploymentName := foo.Spec.DeploymentName + + if deploymentName == "" { + logrus.Errorf("Deployment name must be specified") + return nil + } + + var verifyCmd string + var actionHistory []string + var serviceIP string + var servicePort string + var setupCommands []string + var databases []string + var users []postgresv1.UserSpec + var err error + + serviceIP, servicePort, setupCommands, databases, users, verifyCmd, err = createDeployment(foo) + + if err != nil { + if apierrors.IsAlreadyExists(err) { + + fmt.Printf("CRD %s created\n", deploymentName) + fmt.Printf("Check using: kubectl describe postgres %s \n", deploymentName) + + pgresObj := foo + opts := &metav1.GetOptions{} + err2 := sdk.Get(pgresObj, sdk.WithGetOptions(opts)) + + if err2 != nil { + panic(err2) + } + + actionHistory := pgresObj.Status.ActionHistory + serviceIP := pgresObj.Status.ServiceIP + servicePort := pgresObj.Status.ServicePort + verifyCmd := pgresObj.Status.VerifyCmd + fmt.Printf("Action History:[%s]\n", actionHistory) + fmt.Printf("Service IP:[%s]\n", serviceIP) + fmt.Printf("Service Port:[%s]\n", servicePort) + fmt.Printf("Verify cmd: %v\n", verifyCmd) + + //setupCommands = canonicalize(foo.Spec.Commands) + + var commandsToRun []string + + desiredDatabases := foo.Spec.Databases + currentDatabases := pgresObj.Status.Databases + fmt.Printf("Current Databases:%v\n", currentDatabases) + fmt.Printf("Desired Databases:%v\n", desiredDatabases) + createDBCommands, dropDBCommands := getDatabaseCommands(desiredDatabases, + currentDatabases) + appendList(&commandsToRun, createDBCommands) + appendList(&commandsToRun, dropDBCommands) + + desiredUsers := foo.Spec.Users + currentUsers := pgresObj.Status.Users + fmt.Printf("Current Users:%v\n", currentUsers) + fmt.Printf("Desired Users:%v\n", desiredUsers) + createUserCmds, dropUserCmds, alterUserCmds := getUserCommands(desiredUsers, + currentUsers) + appendList(&commandsToRun, createUserCmds) + appendList(&commandsToRun, dropUserCmds) + appendList(&commandsToRun, alterUserCmds) + + //commandsToRun = getCommandsToRun(actionHistory, setupCommands) + fmt.Printf("commandsToRun: %v\n", commandsToRun) + + if len(commandsToRun) > 0 { + err2 := updateFooStatus(foo, &actionHistory, ¤tUsers, &desiredDatabases, + verifyCmd, serviceIP, servicePort, "UPDATING") + if err2 != nil { + return err + } + updateCRD(pgresObj, commandsToRun) + } + + opts2 := &metav1.GetOptions{} + pgresObj2 := pgresObj + err3 := sdk.Get(pgresObj2, sdk.WithGetOptions(opts2)) + + if err3 != nil { + panic(err3) + } + + actionHistory = pgresObj2.Status.ActionHistory + fmt.Printf("1111 Action History:%s\n", actionHistory) + for _, cmds := range commandsToRun { + actionHistory = append(actionHistory, cmds) + } + + err3 = updateFooStatus(pgresObj2, &actionHistory, &desiredUsers, &desiredDatabases, + verifyCmd, serviceIP, servicePort, "READY") + + if err3 != nil { + panic(err) + return err3 + } + + } else { + panic(err) + } + } else { + for _, cmds := range setupCommands { + if !strings.Contains(cmds, "\\c") { + actionHistory = append(actionHistory, cmds) + } + } + fmt.Printf("Setup Commands: %v\n", setupCommands) + fmt.Printf("Verify using: %v\n", verifyCmd) + + err1 := updateFooStatus(foo, &actionHistory, &users, &databases, verifyCmd, serviceIP, servicePort, "READY") + + if err1 != nil { + return err1 + } + } + } + + return nil +} + +func updateCRD(foo *v1.Postgres, setupCommands []string) { + serviceIP := foo.Status.ServiceIP + servicePort := foo.Status.ServicePort + + fmt.Printf("Service IP:[%s]\n", serviceIP) + fmt.Printf("Service Port:[%s]\n", servicePort) + fmt.Printf("Command:[%s]\n", setupCommands) + + if len(setupCommands) > 0 { + //file := createTempDBFile(setupCommands) + fmt.Println("Now setting up the database") + //setupDatabase(serviceIP, servicePort, file) + var dummyList []string + setupDatabase(serviceIP, servicePort, setupCommands, dummyList) + } +} + +func updateFooStatus(foo *postgresv1.Postgres, + actionHistory *[]string, users *[]postgresv1.UserSpec, databases *[]string, + verifyCmd string, serviceIP string, servicePort string, + status string) error { + + fooCopy := foo.DeepCopy() + fooCopy.Status.AvailableReplicas = 1 + + fooCopy.Status.VerifyCmd = verifyCmd + fooCopy.Status.ActionHistory = *actionHistory + fooCopy.Status.Users = *users + fooCopy.Status.Databases = *databases + fooCopy.Status.ServiceIP = serviceIP + fooCopy.Status.ServicePort = servicePort + fooCopy.Status.Status = status + err := sdk.Update(fooCopy) + return err +} + +func createDeployment (foo *v1.Postgres) (string, string, []string, []string, []postgresv1.UserSpec, string, error){ + + deploymentName := foo.Spec.DeploymentName + image := foo.Spec.Image + users := foo.Spec.Users + databases := foo.Spec.Databases + setupCommands := canonicalize(foo.Spec.Commands) + + var userAndDBCommands []string + var allCommands []string + + var currentDatabases []string + var currentUsers []postgresv1.UserSpec + createDBCmds, dropDBCmds := getDatabaseCommands(databases, currentDatabases) + createUserCmds, dropUserCmds, alterUserCmds := getUserCommands(users, currentUsers) + + fmt.Printf(" Deployment:%v, Image:%v\n", deploymentName, image) + fmt.Printf(" Users:%v\n", users) + fmt.Printf(" Databases:%v\n", databases) + fmt.Printf(" SetupCmds:%v\n", setupCommands) + fmt.Printf(" CreateDBCmds:%v\n", createDBCmds) + fmt.Printf(" DropDBCmds:%v\n", dropDBCmds) + fmt.Printf(" CreateUserCmds:%v\n", createUserCmds) + fmt.Printf(" DropUserCmds:%v\n", dropUserCmds) + fmt.Printf(" AlterUserCmds:%v\n", alterUserCmds) + + appendList(&userAndDBCommands, createDBCmds) + appendList(&userAndDBCommands, dropDBCmds) + appendList(&userAndDBCommands, createUserCmds) + appendList(&userAndDBCommands, dropUserCmds) + appendList(&userAndDBCommands, alterUserCmds) + fmt.Printf(" UserAndDBCmds:%v\n", userAndDBCommands) + fmt.Printf(" SetupCmds:%v\n", setupCommands) + + appendList(&allCommands, userAndDBCommands) + appendList(&allCommands, setupCommands) + + deployment := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: deploymentName, + Namespace: "default", + }, + Spec: appsv1.DeploymentSpec{ + Replicas: int32Ptr(1), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": deploymentName, + }, + }, + Template: apiv1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": deploymentName, + }, + }, + + Spec: apiv1.PodSpec{ + Containers: []apiv1.Container{ + { + Name: deploymentName, + Image: image, + Ports: []apiv1.ContainerPort{ + { + ContainerPort: 5432, + }, + }, + ReadinessProbe: &apiv1.Probe{ + Handler: apiv1.Handler{ + TCPSocket: &apiv1.TCPSocketAction{ + Port: apiutil.FromInt(5432), + }, + }, + InitialDelaySeconds: 5, + TimeoutSeconds: 60, + PeriodSeconds: 2, + }, + Env: []apiv1.EnvVar{ + { + Name: "POSTGRES_PASSWORD", + Value: PGPASSWORD, + }, + }, + }, + }, + }, + }, + }, + } + + fmt.Println("Creating deployment...") + err := sdk.Create(deployment) + fmt.Println(err) + if err != nil { + return "", "", nil, nil, nil, "", err + } + + fmt.Printf("Created deployment %q.\n", deployment.GetObjectMeta().GetName()) + fmt.Printf("------------------------------\n") + + fmt.Printf("Creating service.....\n") + + service := &apiv1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: deploymentName, + Namespace: "default", + Labels: map[string]string{ + "app": deploymentName, + }, + }, + Spec: apiv1.ServiceSpec{ + Ports: []apiv1.ServicePort { + { + Name: "my-port", + Port: 5432, + TargetPort: apiutil.FromInt(5432), + Protocol: apiv1.ProtocolTCP, + }, + }, + Selector: map[string]string { + "app": deploymentName, + }, + Type: apiv1.ServiceTypeNodePort, + }, + } + + err2 := sdk.Get(service) + if err2 != nil { + fmt.Println(err2) + } + err1 := sdk.Create(service) + + if err1 != nil { + return "", "", nil, nil, nil, "", err + } + + fmt.Printf("Created service %q.\n", service.GetObjectMeta().GetName()) + fmt.Printf("------------------------------\n") + + serviceIP := MINIKUBE_IP + + nodePort1 := service.Spec.Ports[0].NodePort + nodePort := fmt.Sprint(nodePort1) + servicePort := nodePort + + time.Sleep(time.Second * 5) + + for { + readyPods := 0 + pods := getPods() + + for _, d := range pods.Items { + podConditions := d.Status.Conditions + for _, podCond := range podConditions { + if podCond.Type == corev1.PodReady { + if podCond.Status == corev1.ConditionTrue { + readyPods += 1 + } + } + } + } + + if readyPods >= len(pods.Items) { + break + } else { + fmt.Println("Waiting for Pod to get ready.") + time.Sleep(time.Second * 4) + } + } + + time.Sleep(time.Second * 2) + + if len(userAndDBCommands) > 0 { + fmt.Printf("About to create temp db file for user and db commands") + //file := createTempDBFile(userAndDBCommands) + fmt.Println("Now setting up the database") + //setupDatabase_prev(serviceIP, servicePort, file) + var dummyList []string + setupDatabase(serviceIP, servicePort, userAndDBCommands, dummyList) + } + + if len(setupCommands) > 0 { + fmt.Printf("About to create temp db file for setup commands") + //file := createTempDBFile(setupCommands) + fmt.Println("Now setting up the database") + //setupDatabase(serviceIP, servicePort, file) + setupDatabase(serviceIP, servicePort, setupCommands, databases) + } + + verifyCmd := strings.Fields("psql -h " + serviceIP + " -p " + nodePort + " -U " + " -d ") + var verifyCmdString = strings.Join(verifyCmd, " ") + fmt.Printf("VerifyCmd: %v\n", verifyCmd) + return serviceIP, servicePort, allCommands, databases, users, verifyCmdString, err + +} + +func setupDatabase(serviceIP string, servicePort string, setupCommands []string, databases []string) { + + fmt.Println("Setting up database") + fmt.Println("Commands:") + fmt.Printf("%v", setupCommands) + + var host = serviceIP + port := -1 + port, _ = strconv.Atoi(servicePort) + var user = "postgres" + var password = PGPASSWORD + + var psqlInfo string + if len(databases) > 0 { + dbname := databases[0] + fmt.Println("%s\n", dbname) + psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+ + "password=%s dbname=%s sslmode=disable", + host, port, user, password, dbname) + } else { + psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+ + "password=%s sslmode=disable", + host, port, user, password) + } + + db, err := sql.Open("postgres", psqlInfo) + if err != nil { + panic(err) + } + defer db.Close() + + err = db.Ping() + if err != nil { + panic(err) + } + + fmt.Println("Successfully connected!") + + for _, command := range setupCommands { + _, err = db.Exec(command) + if err != nil { + panic(err) + } + } + fmt.Println("Done setting up the database") +} + +func createTempDBFile(setupCommands []string) (*os.File){ + file, err := ioutil.TempFile("/tmp", "create-db1") + if err != nil { + panic(err) + } + + fmt.Printf("Database setup file:%s\n", file.Name()) + + for _, command := range setupCommands { + //fmt.Printf("Command: %v\n", command) + // TODO: Interpolation of variables + file.WriteString(command) + file.WriteString("\n") + } + file.Sync() + file.Close() + return file +} + +// podList returns a v1.PodList object +func getPods() *apiv1.PodList { + return &apiv1.PodList{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + } +} + +func int32Ptr(i int32) *int32 { return &i } + +// newbusyBoxPod demonstrates how to create a busybox pod +func newbusyBoxPod(cr *v1.Postgres) *corev1.Pod { + labels := map[string]string{ + "app": "busy-box", + } + return &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "busy-box", + Namespace: cr.Namespace, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(cr, schema.GroupVersionKind{ + Group: v1.SchemeGroupVersion.Group, + Version: v1.SchemeGroupVersion.Version, + Kind: "Postgres", + }), + }, + Labels: labels, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "busybox", + Image: "busybox", + Command: []string{"sleep", "3600"}, + }, + }, + }, + } +} diff --git a/postgres-operator-sdk/pkg/stub/user.go b/postgres-operator-sdk/pkg/stub/user.go new file mode 100644 index 0000000..d423c9a --- /dev/null +++ b/postgres-operator-sdk/pkg/stub/user.go @@ -0,0 +1,98 @@ +package stub + +import ( + "fmt" + "strings" + postgresv1 "github.com/cloud-ark/labs/postgres-operator-sdk/pkg/apis/postgres-operator-sdk/v1" +) + +func getCreateUserCommands(desiredList []postgresv1.UserSpec) []string { + var cmdList []string + for _, user := range desiredList { + username := user.User + password := user.Password + createUserCmd := strings.Fields("create user " + username + " with password '" + password + "';") + var cmdString = strings.Join(createUserCmd, " ") + fmt.Printf("CreateUserCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} + +func getDropUserCommands(desiredList []postgresv1.UserSpec) []string { + var cmdList []string + for _, user := range desiredList { + username := user.User + dropUserCmd := strings.Fields("drop user " + username + ";") + var cmdString = strings.Join(dropUserCmd, " ") + fmt.Printf("DropUserCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} + +func getAlterUserCommands(desiredList []postgresv1.UserSpec) []string { + var cmdList []string + for _, user := range desiredList { + username := user.User + password := user.Password + dropUserCmd := strings.Fields("alter user " + username + " with password '" + password + "';") + var cmdString = strings.Join(dropUserCmd, " ") + fmt.Printf("AlterUserCmd: %v\n", cmdString) + cmdList = append(cmdList, cmdString) + } + return cmdList +} + +func getUserDiffList(desired []postgresv1.UserSpec, current []postgresv1.UserSpec) []postgresv1.UserSpec { + var diffList []postgresv1.UserSpec + for _, v := range desired { + var found bool = false + for _, v1 := range current { + if v.User == v1.User { + found = true + } + } + if !found { + diffList = append(diffList, v) + } + } + //fmt.Printf("-- DiffList: %v--\n", diffList) + return diffList +} + +func getUserCommonList(desired []postgresv1.UserSpec, current []postgresv1.UserSpec) []postgresv1.UserSpec { + var modifyList []postgresv1.UserSpec + for _, v := range desired { + for _, v1 := range current { + if v.User == v1.User { + if v.Password != v1.Password { + modifyList = append(modifyList, v) + } + } + } + } + //fmt.Printf("-- ModifyList: %v--\n", modifyList) + return modifyList +} + +func getUserCommands(desiredList []postgresv1.UserSpec, currentList []postgresv1.UserSpec) ([]string, []string, []string) { + + var createUserCommands []string + var dropUserCommands []string + var alterUserCommands []string + + if len(currentList) == 0 { + createUserCommands = getCreateUserCommands(desiredList) + } else { + addList := getUserDiffList(desiredList, currentList) + createUserCommands = getCreateUserCommands(addList) + + dropList := getUserDiffList(currentList, desiredList) + dropUserCommands = getDropUserCommands(dropList) + + alterList := getUserCommonList(desiredList, currentList) + alterUserCommands = getAlterUserCommands(alterList) + } + return createUserCommands, dropUserCommands, alterUserCommands +} diff --git a/postgres-operator-sdk/pkg/stub/utils.go b/postgres-operator-sdk/pkg/stub/utils.go new file mode 100644 index 0000000..f0dcd94 --- /dev/null +++ b/postgres-operator-sdk/pkg/stub/utils.go @@ -0,0 +1,55 @@ +package stub + +import ( + "fmt" + "strings" +) + +func getCommandsToRun(actionHistory []string, setupCommands []string) []string { + var commandsToRun []string + for _, v := range setupCommands { + var found bool = false + for _, v1 := range actionHistory { + if v == v1 { + found = true + } + } + if !found { + commandsToRun = append(commandsToRun, v) + } + } + fmt.Printf("-- commandsToRun: %v--\n", commandsToRun) + return commandsToRun +} + +func getDiffList(desired []string, current []string) []string { + var diffList []string + for _, v := range desired { + var found bool = false + for _, v1 := range current { + if v == v1 { + found = true + } + } + if !found { + diffList = append(diffList, v) + } + } + //fmt.Printf("-- DiffList: %v--\n", diffList) + return diffList +} + +func canonicalize(setupCommands1 []string) []string { + var setupCommands []string + //Convert setupCommands to Lower case + for _, cmd := range setupCommands1 { + setupCommands = append(setupCommands, strings.ToLower(cmd)) + } + return setupCommands +} + +func appendList(parentList *[]string, childList []string) { + for _, val := range childList { + *parentList = append(*parentList, val) + } +} diff --git a/postgres-operator-sdk/tmp/build/Dockerfile b/postgres-operator-sdk/tmp/build/Dockerfile new file mode 100644 index 0000000..65d3f7f --- /dev/null +++ b/postgres-operator-sdk/tmp/build/Dockerfile @@ -0,0 +1,6 @@ +FROM alpine:3.6 + +RUN adduser -D postgres-operator-sdk +USER postgres-operator-sdk + +ADD tmp/_output/bin/postgres-operator-sdk /usr/local/bin/postgres-operator-sdk diff --git a/postgres-operator-sdk/tmp/build/go-test.sh b/postgres-operator-sdk/tmp/build/go-test.sh new file mode 100755 index 0000000..03bb22c --- /dev/null +++ b/postgres-operator-sdk/tmp/build/go-test.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +memcached-operator-test -test.parallel=1 -test.failfast -root=/ -kubeconfig=incluster -namespacedMan=namespaced.yaml -test.v diff --git a/postgres-operator-sdk/tmp/build/test-framework/Dockerfile b/postgres-operator-sdk/tmp/build/test-framework/Dockerfile new file mode 100644 index 0000000..ffc98b0 --- /dev/null +++ b/postgres-operator-sdk/tmp/build/test-framework/Dockerfile @@ -0,0 +1,8 @@ +ARG BASEIMAGE + +FROM ${BASEIMAGE} + +ADD tmp/_output/bin/memcached-operator-test /usr/local/bin/memcached-operator-test +ARG NAMESPACEDMAN +ADD $NAMESPACEDMAN /namespaced.yaml +ADD tmp/build/go-test.sh /go-test.sh diff --git a/postgres-operator-sdk/tmp/codegen/boilerplate.go.txt b/postgres-operator-sdk/tmp/codegen/boilerplate.go.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/postgres-operator-sdk/tmp/codegen/boilerplate.go.txt @@ -0,0 +1 @@ + diff --git a/postgres-operator-sdk/tmp/codegen/update-generated.sh b/postgres-operator-sdk/tmp/codegen/update-generated.sh new file mode 100755 index 0000000..ad63480 --- /dev/null +++ b/postgres-operator-sdk/tmp/codegen/update-generated.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +vendor/k8s.io/code-generator/generate-groups.sh \ +deepcopy \ +github.com/cloud-ark/labs/postgres-operator-sdk/pkg/generated \ +github.com/cloud-ark/labs/postgres-operator-sdk/pkg/apis \ +postgres-operator-sdk:v1 \ +--go-header-file "./tmp/codegen/boilerplate.go.txt" diff --git a/postgres-operator-sdk/version/version.go b/postgres-operator-sdk/version/version.go new file mode 100644 index 0000000..e3e130b --- /dev/null +++ b/postgres-operator-sdk/version/version.go @@ -0,0 +1,5 @@ +package version + +var ( + Version = "0.0.1" +)