refactor project
This commit is contained in:
@@ -1 +0,0 @@
|
||||
tmp/
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,2 +0,0 @@
|
||||
pkg/cmd/api/spec/api.swagger.json linguist-generated=true
|
||||
pkg/cmd/api/spec/static.go linguist-generated=true
|
||||
32
.travis.yml
32
.travis.yml
@@ -1,32 +0,0 @@
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.10
|
||||
- tip
|
||||
|
||||
go_import_path: kubesphere.io/kubesphere
|
||||
|
||||
before_install:
|
||||
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
|
||||
- curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
- sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
||||
- sudo apt-get update
|
||||
- sudo apt-get -y install docker-ce
|
||||
- dep ensure -v
|
||||
|
||||
before_script:
|
||||
- docker --version
|
||||
|
||||
script:
|
||||
- make fmt-check && make build
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
script: bash install/scripts/docker_push
|
||||
on:
|
||||
branch: master
|
||||
17
Dockerfile
17
Dockerfile
@@ -1,17 +0,0 @@
|
||||
# Copyright 2018 The KubeSphere Authors. All rights reserved.
|
||||
# Use of this source code is governed by a Apache license
|
||||
# that can be found in the LICENSE file.
|
||||
|
||||
FROM kubesphere/kubesphere-builder as builder
|
||||
|
||||
WORKDIR /go/src/kubesphere.io/kubesphere/
|
||||
COPY . .
|
||||
|
||||
RUN go generate kubesphere.io/kubesphere/pkg/version && \
|
||||
go install kubesphere.io/kubesphere/cmd/...
|
||||
|
||||
FROM alpine:3.6
|
||||
RUN apk add --update ca-certificates && update-ca-certificates
|
||||
COPY --from=builder /go/bin/* /usr/local/bin/
|
||||
|
||||
CMD ["sh"]
|
||||
@@ -1,10 +0,0 @@
|
||||
FROM alpine:3.6
|
||||
|
||||
RUN apk add --update ca-certificates \
|
||||
&& update-ca-certificates \
|
||||
&& mkdir -p /etc/kubesphere/ingress-controller
|
||||
|
||||
COPY ./bin/* /usr/local/bin/
|
||||
COPY ./install/ingress-controller /etc/kubesphere/ingress-controller
|
||||
COPY ./install/swagger-ui /usr/lib/kubesphere/swagger-ui
|
||||
CMD ["sh"]
|
||||
812
Gopkg.lock
generated
812
Gopkg.lock
generated
@@ -1,812 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
digest = "1:bf42be3cb1519bf8018dfd99720b1005ee028d947124cab3ccf965da59381df6"
|
||||
name = "github.com/Microsoft/go-winio"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "7da180ee92d8bd8bb8c37fc560e673e6557c392f"
|
||||
version = "v0.4.7"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d1665c44bd5db19aaee18d1b6233c99b0b9a986e8bccb24ef54747547a48027f"
|
||||
name = "github.com/PuerkitoBio/purell"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c739832d67eb1e9cc478a19cc1a1ccd78df0397bf8a32978b759152e205f644b"
|
||||
name = "github.com/PuerkitoBio/urlesc"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9e9193aa51197513b3abcb108970d831fbcf40ef96aa845c4f03276e1fa316d2"
|
||||
name = "github.com/Sirupsen/logrus"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
|
||||
version = "v1.0.5"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:e49fec8537ec021eeb41d394684bce0365c8db14c8099215f7b509189ddb5852"
|
||||
name = "github.com/antonholmquist/jason"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c23cef7eaa75a6a5b8810120e167bd590d8fd2ab"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:4fe4dc4ce7ebb5a4b0544c5b411196d23e221800d279a207d76f02812f756c3d"
|
||||
name = "github.com/coreos/etcd"
|
||||
packages = [
|
||||
"auth/authpb",
|
||||
"clientv3",
|
||||
"etcdserver/api/v3rpc/rpctypes",
|
||||
"etcdserver/etcdserverpb",
|
||||
"mvcc/mvccpb",
|
||||
"pkg/tlsutil",
|
||||
"pkg/transport",
|
||||
"pkg/types",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "33245c6b5b49130ca99280408fadfab01aac0e48"
|
||||
version = "v3.3.8"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
pruneopts = "UT"
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:4189ee6a3844f555124d9d2656fe7af02fca961c2a9bad9074789df13a0c62e0"
|
||||
name = "github.com/docker/distribution"
|
||||
packages = [
|
||||
"digestset",
|
||||
"reference",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "749f6afb4572201e3c37325d0ffedb6f32be8950"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ec821dda59d7dd340498d74f798aa218b2c782bba54a690e866dc4f520d900d5"
|
||||
name = "github.com/docker/docker"
|
||||
packages = [
|
||||
"api",
|
||||
"api/types",
|
||||
"api/types/blkiodev",
|
||||
"api/types/container",
|
||||
"api/types/events",
|
||||
"api/types/filters",
|
||||
"api/types/image",
|
||||
"api/types/mount",
|
||||
"api/types/network",
|
||||
"api/types/registry",
|
||||
"api/types/strslice",
|
||||
"api/types/swarm",
|
||||
"api/types/time",
|
||||
"api/types/versions",
|
||||
"api/types/volume",
|
||||
"client",
|
||||
"pkg/ioutils",
|
||||
"pkg/longpath",
|
||||
"pkg/system",
|
||||
"pkg/tlsconfig",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "90d35abf7b3535c1c319c872900fbd76374e521c"
|
||||
version = "v17.05.0-ce-rc3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:811c86996b1ca46729bad2724d4499014c4b9effd05ef8c71b852aad90deb0ce"
|
||||
name = "github.com/docker/go-connections"
|
||||
packages = [
|
||||
"nat",
|
||||
"sockets",
|
||||
"tlsconfig",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "7395e3f8aa162843a74ed6d48e79627d9792ac55"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:6f82cacd0af5921e99bf3f46748705239b36489464f4529a1589bc895764fb18"
|
||||
name = "github.com/docker/go-units"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "47565b4f722fb6ceae66b95f853feed578a4a51c"
|
||||
version = "v0.3.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:4841e14252a2cecf11840bd05230412ad469709bbacfc12467e2ce5ad07f339b"
|
||||
name = "github.com/docker/libtrust"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "aabc10ec26b754e797f9028f4589c5b7bd90dc20"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:dbb3d1675f5beeb37de6e9b95cc460158ff212902a916e67688b01e0660f41bd"
|
||||
name = "github.com/docker/spdystream"
|
||||
packages = [
|
||||
".",
|
||||
"spdy",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "bc6354cbbc295e925e4c611ffe90c1f287ee54db"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:798072bbab2506719d8292cd9b5840a0b5babe0348393bd7097d8fb25ecf0b82"
|
||||
name = "github.com/emicklei/go-restful"
|
||||
packages = [
|
||||
".",
|
||||
"log",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "3658237ded108b4134956c1b3050349d93e7b895"
|
||||
version = "v2.7.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:e2300c0b15e8b7cb908da64f50e748725c739bcf042a19ceb971680763339888"
|
||||
name = "github.com/emicklei/go-restful-openapi"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "51bf251d405ad1e23511fef0a2dbe40bc70ce2c6"
|
||||
version = "v0.11.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2cd7915ab26ede7d95b8749e6b1f933f1c6d5398030684e6505940a10f31cfda"
|
||||
name = "github.com/ghodss/yaml"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:2997679181d901ac8aaf4330d11138ecf3974c6d3334995ff36f20cbd597daf8"
|
||||
name = "github.com/go-openapi/jsonpointer"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "3a0015ad55fa9873f41605d3e8f28cd279c32ab2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:1ae3f233d75a731b164ca9feafd8ed646cbedf1784095876ed6988ce8aa88b1f"
|
||||
name = "github.com/go-openapi/jsonreference"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "3fb327e6747da3043567ee86abd02bb6376b6be2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:cbd9c1cc4ce36075f4ebf0e0525e6cda8597daac1a5eb5f7f88480a3c00e7319"
|
||||
name = "github.com/go-openapi/spec"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "bce47c9386f9ecd6b86f450478a80103c3fe1402"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:731022b436cdb9b4b2a53be2ead693467a1474b8b873d4f90cb424fffdc3d0ff"
|
||||
name = "github.com/go-openapi/swag"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "2b0bd4f193d011c203529df626a65d63cb8a79e8"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:adea5a94903eb4384abef30f3d878dc9ff6b6b5b0722da25b82e5169216dfb61"
|
||||
name = "github.com/go-sql-driver/mysql"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "d523deb1b23d913de5bdada721a6071e71283618"
|
||||
version = "v1.4.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:cd559bf134bbedd0dfd5db4d988c88d8f96674fa3f2af0cb5b0dcd5fc0a0a019"
|
||||
name = "github.com/gogo/protobuf"
|
||||
packages = [
|
||||
"gogoproto",
|
||||
"proto",
|
||||
"protoc-gen-gogo/descriptor",
|
||||
"sortkeys",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:1ba1d79f2810270045c328ae5d674321db34e3aae468eb4233883b473c5c0467"
|
||||
name = "github.com/golang/glog"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = [
|
||||
"proto",
|
||||
"ptypes",
|
||||
"ptypes/any",
|
||||
"ptypes/duration",
|
||||
"ptypes/timestamp",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:3ee90c0d94da31b442dde97c99635aaafec68d0b8a3c12ee2075c6bdabeec6bb"
|
||||
name = "github.com/google/gofuzz"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:65c4414eeb350c47b8de71110150d0ea8a281835b1f386eacaa3ad7325929c21"
|
||||
name = "github.com/googleapis/gnostic"
|
||||
packages = [
|
||||
"OpenAPIv2",
|
||||
"compiler",
|
||||
"extensions",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "7c663266750e7d82587642f65e60bc4083f1f84e"
|
||||
version = "v0.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e"
|
||||
name = "github.com/gorilla/websocket"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:cf296baa185baae04a9a7004efee8511d08e2f5f51d4cbe5375da89722d681db"
|
||||
name = "github.com/hashicorp/golang-lru"
|
||||
packages = [
|
||||
".",
|
||||
"simplelru",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:0778dc7fce1b4669a8bfa7ae506ec1f595b6ab0f8989c1c0d22a8ca1144e9972"
|
||||
name = "github.com/howeyc/gopass"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3e260afa138eab6492b531a3b3d10ab4cb70512d423faa78b8949dec76e66a21"
|
||||
name = "github.com/imdario/mergo"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "9316a62528ac99aaecb4e47eadd6dc8aa6533d58"
|
||||
version = "v0.3.5"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:235ae01f32fb5f12c5f6d2e0e05ab48e651ab31c126e45a4efc4f510810941ac"
|
||||
name = "github.com/jinzhu/gorm"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "6ed508ec6a4ecb3531899a69cbc746ccf65a4166"
|
||||
version = "v1.9.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:fd97437fbb6b7dce04132cf06775bd258cce305c44add58eb55ca86c6c325160"
|
||||
name = "github.com/jinzhu/inflection"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "04140366298a54a039076d798123ffa108fff46c"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b1d4df033414c1a0d85fa7037b9aaf03746314811c860a95ea2d5fd481cd6c35"
|
||||
name = "github.com/json-iterator/go"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4"
|
||||
version = "1.1.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:ada518b8c338e10e0afa443d84671476d3bd1d926e13713938088e8ddbee1a3e"
|
||||
name = "github.com/mailru/easyjson"
|
||||
packages = [
|
||||
"buffer",
|
||||
"jlexer",
|
||||
"jwriter",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "3fdea8d05856a0c8df22ed4bc71b3219245e4485"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563"
|
||||
name = "github.com/modern-go/concurrent"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
|
||||
version = "1.0.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d711dfcf661439f1ef0b202a02e8a1ff4deac48f26f34253520dcdbecbd7c5f1"
|
||||
name = "github.com/modern-go/reflect2"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "1df9eeb2bb81f327b96228865c5687bc2194af3f"
|
||||
version = "1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ee4d4af67d93cc7644157882329023ce9a7bcfce956a079069a9405521c7cc8d"
|
||||
name = "github.com/opencontainers/go-digest"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf"
|
||||
version = "v1.0.0-rc1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9424f440bba8f7508b69414634aef3b2b3a877e522d8a4624692412805407bb7"
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:3f3a05ae0b95893d90b9b3b5afdb79a9b3d96e4e36e099d841ae602e4aca0da8"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ssh/terminal"]
|
||||
pruneopts = "UT"
|
||||
revision = "7f39a6fea4fe9364fb61e1def6a268a51b4f3a06"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:bae20a4ea45ad83eb54271a18c820a4ca7c03880aa20d964e2d5bb1d57b1a41a"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
"context/ctxhttp",
|
||||
"http/httpguts",
|
||||
"http2",
|
||||
"http2/hpack",
|
||||
"idna",
|
||||
"internal/socks",
|
||||
"internal/timeseries",
|
||||
"proxy",
|
||||
"trace",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a17927b3d78603ae6691d5bf8d3d91467a6edd4ca43c9509347e016a54477f96"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"unix",
|
||||
"windows",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "fc8bd948cf46f9c7af0f07d34151ce25fe90e477"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0c56024909189aee3364b7f21a95a27459f718aa7c199a5c111c36cfffd9eaef"
|
||||
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 = "UT"
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c9e7a4b4d47c0ed205d257648b0e5b0440880cb728506e318f8ac7cd36270bc4"
|
||||
name = "golang.org/x/time"
|
||||
packages = ["rate"]
|
||||
pruneopts = "UT"
|
||||
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c25289f43ac4a68d88b02245742347c94f1e108c534dda442188015ff80669b3"
|
||||
name = "google.golang.org/appengine"
|
||||
packages = ["cloudsql"]
|
||||
pruneopts = "UT"
|
||||
revision = "b1f26356af11148e710935ed1ac8a7f5702c7612"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:601e63e7d4577f907118bec825902505291918859d223bce015539e79f1160e3"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
pruneopts = "UT"
|
||||
revision = "32ee49c4dd805befd833990acba36cb75042378c"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3a98314fd2e43bbd905b33125dad80b10111ba6e5e541db8ed2a953fe01fbb31"
|
||||
name = "google.golang.org/grpc"
|
||||
packages = [
|
||||
".",
|
||||
"balancer",
|
||||
"balancer/base",
|
||||
"balancer/roundrobin",
|
||||
"codes",
|
||||
"connectivity",
|
||||
"credentials",
|
||||
"encoding",
|
||||
"encoding/proto",
|
||||
"grpclog",
|
||||
"health/grpc_health_v1",
|
||||
"internal",
|
||||
"internal/backoff",
|
||||
"internal/channelz",
|
||||
"internal/grpcrand",
|
||||
"keepalive",
|
||||
"metadata",
|
||||
"naming",
|
||||
"peer",
|
||||
"resolver",
|
||||
"resolver/dns",
|
||||
"resolver/passthrough",
|
||||
"stats",
|
||||
"status",
|
||||
"tap",
|
||||
"transport",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8"
|
||||
version = "v1.13.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7a23929a5a0d4266c8f5444dae1e7594dbb0cae1c3091834119b162f81e229ff"
|
||||
name = "gopkg.in/igm/sockjs-go.v2"
|
||||
packages = ["sockjs"]
|
||||
pruneopts = "UT"
|
||||
revision = "d276e9ffe5cc5c271b81198cc77a2adf6c4482d2"
|
||||
version = "v2.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a"
|
||||
name = "gopkg.in/inf.v0"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
|
||||
version = "v0.9.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||
version = "v2.2.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:cae8f1d1d786aa486a7ed236a8c1f099b3b44697ec6bbb5951d7e9bdb53a5125"
|
||||
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",
|
||||
"settings/v1alpha1",
|
||||
"storage/v1",
|
||||
"storage/v1alpha1",
|
||||
"storage/v1beta1",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "73d903622b7391f3312dcbac6483fed484e185f8"
|
||||
version = "kubernetes-1.10.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d0089d5f7811ded4279da7a8a66d2721488afec8208d86bdad3f4a20d3687e81"
|
||||
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/httpstream",
|
||||
"pkg/util/httpstream/spdy",
|
||||
"pkg/util/intstr",
|
||||
"pkg/util/json",
|
||||
"pkg/util/net",
|
||||
"pkg/util/rand",
|
||||
"pkg/util/remotecommand",
|
||||
"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/netutil",
|
||||
"third_party/forked/golang/reflect",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "302974c03f7e50f16561ba237db776ab93594ef6"
|
||||
version = "kubernetes-1.10.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7ee72261d268f7443085aad95b39fefc17fca826a9bfd8bd2d431bc081852a62"
|
||||
name = "k8s.io/client-go"
|
||||
packages = [
|
||||
"discovery",
|
||||
"informers",
|
||||
"informers/admissionregistration",
|
||||
"informers/admissionregistration/v1alpha1",
|
||||
"informers/admissionregistration/v1beta1",
|
||||
"informers/apps",
|
||||
"informers/apps/v1",
|
||||
"informers/apps/v1beta1",
|
||||
"informers/apps/v1beta2",
|
||||
"informers/autoscaling",
|
||||
"informers/autoscaling/v1",
|
||||
"informers/autoscaling/v2beta1",
|
||||
"informers/batch",
|
||||
"informers/batch/v1",
|
||||
"informers/batch/v1beta1",
|
||||
"informers/batch/v2alpha1",
|
||||
"informers/certificates",
|
||||
"informers/certificates/v1beta1",
|
||||
"informers/core",
|
||||
"informers/core/v1",
|
||||
"informers/events",
|
||||
"informers/events/v1beta1",
|
||||
"informers/extensions",
|
||||
"informers/extensions/v1beta1",
|
||||
"informers/internalinterfaces",
|
||||
"informers/networking",
|
||||
"informers/networking/v1",
|
||||
"informers/policy",
|
||||
"informers/policy/v1beta1",
|
||||
"informers/rbac",
|
||||
"informers/rbac/v1",
|
||||
"informers/rbac/v1alpha1",
|
||||
"informers/rbac/v1beta1",
|
||||
"informers/scheduling",
|
||||
"informers/scheduling/v1alpha1",
|
||||
"informers/settings",
|
||||
"informers/settings/v1alpha1",
|
||||
"informers/storage",
|
||||
"informers/storage/v1",
|
||||
"informers/storage/v1alpha1",
|
||||
"informers/storage/v1beta1",
|
||||
"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/settings/v1alpha1",
|
||||
"kubernetes/typed/storage/v1",
|
||||
"kubernetes/typed/storage/v1alpha1",
|
||||
"kubernetes/typed/storage/v1beta1",
|
||||
"listers/admissionregistration/v1alpha1",
|
||||
"listers/admissionregistration/v1beta1",
|
||||
"listers/apps/v1",
|
||||
"listers/apps/v1beta1",
|
||||
"listers/apps/v1beta2",
|
||||
"listers/autoscaling/v1",
|
||||
"listers/autoscaling/v2beta1",
|
||||
"listers/batch/v1",
|
||||
"listers/batch/v1beta1",
|
||||
"listers/batch/v2alpha1",
|
||||
"listers/certificates/v1beta1",
|
||||
"listers/core/v1",
|
||||
"listers/events/v1beta1",
|
||||
"listers/extensions/v1beta1",
|
||||
"listers/networking/v1",
|
||||
"listers/policy/v1beta1",
|
||||
"listers/rbac/v1",
|
||||
"listers/rbac/v1alpha1",
|
||||
"listers/rbac/v1beta1",
|
||||
"listers/scheduling/v1alpha1",
|
||||
"listers/settings/v1alpha1",
|
||||
"listers/storage/v1",
|
||||
"listers/storage/v1alpha1",
|
||||
"listers/storage/v1beta1",
|
||||
"pkg/apis/clientauthentication",
|
||||
"pkg/apis/clientauthentication/v1alpha1",
|
||||
"pkg/version",
|
||||
"plugin/pkg/client/auth/exec",
|
||||
"rest",
|
||||
"rest/watch",
|
||||
"tools/auth",
|
||||
"tools/cache",
|
||||
"tools/clientcmd",
|
||||
"tools/clientcmd/api",
|
||||
"tools/clientcmd/api/latest",
|
||||
"tools/clientcmd/api/v1",
|
||||
"tools/metrics",
|
||||
"tools/pager",
|
||||
"tools/reference",
|
||||
"tools/remotecommand",
|
||||
"transport",
|
||||
"transport/spdy",
|
||||
"util/buffer",
|
||||
"util/cert",
|
||||
"util/exec",
|
||||
"util/flowcontrol",
|
||||
"util/homedir",
|
||||
"util/integer",
|
||||
"util/retry",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "23781f4d6632d88e869066eaebb743857aa1ef9b"
|
||||
version = "v7.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2bdbea32607f4effd9e91dadd90baab1ecf224839b613bcaa8f50db5a5f133f5"
|
||||
name = "k8s.io/kubernetes"
|
||||
packages = [
|
||||
"pkg/apis/core",
|
||||
"pkg/util/slice",
|
||||
"pkg/util/version",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "5ca598b4ba5abb89bb773071ce452e33fb66339d"
|
||||
version = "v1.10.4"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"github.com/antonholmquist/jason",
|
||||
"github.com/coreos/etcd/clientv3",
|
||||
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes",
|
||||
"github.com/coreos/etcd/pkg/transport",
|
||||
"github.com/docker/docker/api/types",
|
||||
"github.com/docker/docker/client",
|
||||
"github.com/emicklei/go-restful",
|
||||
"github.com/emicklei/go-restful-openapi",
|
||||
"github.com/go-openapi/spec",
|
||||
"github.com/go-sql-driver/mysql",
|
||||
"github.com/golang/glog",
|
||||
"github.com/jinzhu/gorm",
|
||||
"github.com/pkg/errors",
|
||||
"github.com/spf13/pflag",
|
||||
"gopkg.in/igm/sockjs-go.v2/sockjs",
|
||||
"gopkg.in/yaml.v2",
|
||||
"k8s.io/api/apps/v1",
|
||||
"k8s.io/api/apps/v1beta2",
|
||||
"k8s.io/api/autoscaling/v1",
|
||||
"k8s.io/api/batch/v1",
|
||||
"k8s.io/api/batch/v1beta1",
|
||||
"k8s.io/api/core/v1",
|
||||
"k8s.io/api/extensions/v1beta1",
|
||||
"k8s.io/api/policy/v1beta1",
|
||||
"k8s.io/api/rbac/v1",
|
||||
"k8s.io/api/storage/v1",
|
||||
"k8s.io/apimachinery/pkg/api/errors",
|
||||
"k8s.io/apimachinery/pkg/api/resource",
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||
"k8s.io/apimachinery/pkg/labels",
|
||||
"k8s.io/apimachinery/pkg/types",
|
||||
"k8s.io/apimachinery/pkg/util/sets",
|
||||
"k8s.io/apimachinery/pkg/util/wait",
|
||||
"k8s.io/client-go/informers",
|
||||
"k8s.io/client-go/kubernetes",
|
||||
"k8s.io/client-go/kubernetes/scheme",
|
||||
"k8s.io/client-go/listers/apps/v1",
|
||||
"k8s.io/client-go/listers/batch/v1",
|
||||
"k8s.io/client-go/listers/batch/v1beta1",
|
||||
"k8s.io/client-go/listers/core/v1",
|
||||
"k8s.io/client-go/listers/extensions/v1beta1",
|
||||
"k8s.io/client-go/listers/rbac/v1",
|
||||
"k8s.io/client-go/listers/storage/v1",
|
||||
"k8s.io/client-go/rest",
|
||||
"k8s.io/client-go/tools/cache",
|
||||
"k8s.io/client-go/tools/clientcmd",
|
||||
"k8s.io/client-go/tools/remotecommand",
|
||||
"k8s.io/kubernetes/pkg/apis/core",
|
||||
"k8s.io/kubernetes/pkg/util/slice",
|
||||
"k8s.io/kubernetes/pkg/util/version",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
90
Gopkg.toml
90
Gopkg.toml
@@ -1,90 +0,0 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/coreos/etcd"
|
||||
version = "3.3.7"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/docker/docker"
|
||||
version = "v17.05.0-ce"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/emicklei/go-restful"
|
||||
version = "2.7.1"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/glog"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/pflag"
|
||||
version = "1.0.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "gopkg.in/igm/sockjs-go.v2"
|
||||
version = "2.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "gopkg.in/yaml.v2"
|
||||
version = "2.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/api"
|
||||
version = "kubernetes-1.10.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/apimachinery"
|
||||
version = "kubernetes-1.10.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/client-go"
|
||||
version = "7.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/kubernetes"
|
||||
version = "1.10.4"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
||||
# To use reference package:
|
||||
# vendor/github.com/docker/docker/client/container_commit.go:17: undefined: reference.ParseNormalizedNamed
|
||||
# vendor/github.com/docker/docker/client/container_commit.go:25: undefined: reference.TagNameOnly
|
||||
# vendor/github.com/docker/docker/client/container_commit.go:30: undefined: reference.FamiliarNam
|
||||
[[override]]
|
||||
name = "github.com/docker/distribution"
|
||||
branch = "master"
|
||||
|
||||
# To use reference package:
|
||||
# vendor/github.com/docker/docker/registry/registry.go:30: cannot call non-function tlsconfig.ServerDefault (type tls.Config)
|
||||
# vendor/github.com/docker/docker/registry/registry.go:66: undefined: tlsconfig.SystemCertPool
|
||||
# vendor/github.com/docker/docker/registry/registry.go:168: cannot call non-function tlsconfig.ServerDefault (type tls.Config)
|
||||
# vendor/github.com/docker/docker/registry/service_v2.go:11: cannot call non-function tlsconfig.ServerDefault (type tls.Config)
|
||||
[[override]]
|
||||
name = "github.com/docker/go-connections"
|
||||
branch = "master"
|
||||
201
LICENSE
201
LICENSE
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
106
Makefile
106
Makefile
@@ -1,106 +0,0 @@
|
||||
# Copyright 2018 The KubeSphere Authors. All rights reserved.
|
||||
# Use of this source code is governed by a Apache license
|
||||
# that can be found in the LICENSE file.
|
||||
|
||||
TRAG.Org:=kubesphere
|
||||
TRAG.Name:=ks-apiserver
|
||||
TRAG.Gopkg:=kubesphere.io/kubesphere
|
||||
TRAG.Version:=$(TRAG.Gopkg)/pkg/version
|
||||
|
||||
DOCKER_TAGS=latest
|
||||
RUN_IN_DOCKER:=docker run -it --rm -v `pwd`:/go/src/$(TRAG.Gopkg) -v `pwd`/tmp/cache:/root/.cache/go-build -w /go/src/$(TRAG.Gopkg) -e GOBIN=/go/src/$(TRAG.Gopkg)/tmp/bin -e USER_ID=`id -u` -e GROUP_ID=`id -g` kubesphere/kubesphere-builder
|
||||
GO_FMT:=goimports -l -w -e -local=kubesphere -srcdir=/go/src/$(TRAG.Gopkg)
|
||||
GO_FILES:=./cmd ./pkg
|
||||
|
||||
define get_diff_files
|
||||
$(eval DIFF_FILES=$(shell git diff --name-only --diff-filter=ad | grep -E "^(test|cmd|pkg)/.+\.go"))
|
||||
endef
|
||||
define get_build_flags
|
||||
$(eval SHORT_VERSION=$(shell git describe --tags --always --dirty="-dev"))
|
||||
$(eval SHA1_VERSION=$(shell git show --quiet --pretty=format:%H))
|
||||
$(eval DATE=$(shell date +'%Y-%m-%dT%H:%M:%S'))
|
||||
$(eval BUILD_FLAG= -X $(TRAG.Version).ShortVersion="$(SHORT_VERSION)" \
|
||||
-X $(TRAG.Version).GitSha1Version="$(SHA1_VERSION)" \
|
||||
-X $(TRAG.Version).BuildDate="$(DATE)")
|
||||
endef
|
||||
|
||||
.PHONY: all
|
||||
all: generate build
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
# TODO: update help info to last version
|
||||
@echo "TODO"
|
||||
|
||||
.PHONY: init-vendor
|
||||
init-vendor:
|
||||
@if [[ ! -f "$$(which govendor)" ]]; then \
|
||||
go get -u github.com/kardianos/govendor; \
|
||||
fi
|
||||
govendor init
|
||||
govendor add +external
|
||||
@echo "init-vendor done"
|
||||
|
||||
.PHONY: update-vendor
|
||||
update-vendor:
|
||||
@if [[ ! -f "$$(which govendor)" ]]; then \
|
||||
go get -u github.com/kardianos/govendor; \
|
||||
fi
|
||||
govendor update +external
|
||||
govendor list
|
||||
@echo "update-vendor done"
|
||||
|
||||
.PHONY: update-builder
|
||||
update-builder:
|
||||
docker pull kubesphere/kubesphere-builder
|
||||
@echo "update-builder done"
|
||||
|
||||
.PHONY: generate-in-local
|
||||
generate-in-local:
|
||||
go generate ./pkg/version/
|
||||
|
||||
.PHONY: generate
|
||||
generate:
|
||||
$(RUN_IN_DOCKER) make generate-in-local
|
||||
@echo "generate done"
|
||||
|
||||
.PHONY: fmt-all
|
||||
fmt-all:
|
||||
mkdir -p ./tmp/bin && cp -r ./install ./tmp/
|
||||
$(RUN_IN_DOCKER) $(GO_FMT) $(GO_FILES)
|
||||
@echo "fmt done"
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
$(call get_diff_files)
|
||||
$(if $(DIFF_FILES), \
|
||||
$(RUN_IN_DOCKER) $(GO_FMT) ${DIFF_FILES}, \
|
||||
$(info cannot find modified files from git) \
|
||||
)
|
||||
@echo "fmt done"
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check: fmt-all
|
||||
$(call get_diff_files)
|
||||
$(if $(DIFF_FILES), \
|
||||
exit 2 \
|
||||
)
|
||||
|
||||
.PHONY: build
|
||||
build: fmt
|
||||
mkdir -p ./tmp/bin && cp -r ./install/ ./tmp/
|
||||
$(call get_build_flags)
|
||||
$(RUN_IN_DOCKER) time go install -ldflags '$(BUILD_FLAG)' $(TRAG.Gopkg)/cmd/...
|
||||
mv ./tmp/bin/cmd ./tmp/bin/$(TRAG.Name)
|
||||
@docker build -t $(TRAG.Org)/$(TRAG.Name) -f ./Dockerfile.dev ./tmp
|
||||
@docker image prune -f 1>/dev/null 2>&1
|
||||
@echo "build done"
|
||||
|
||||
.PHONY: release
|
||||
release:
|
||||
@echo "TODO"
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-make -C ./pkg/version clean
|
||||
@echo "ok"
|
||||
37
README.md
37
README.md
@@ -1,37 +0,0 @@
|
||||
# KubeSphere
|
||||
[](https://github.com/KubeSphere/KubeSphere/blob/master/LICENSE)
|
||||
[](https://travis-ci.org/kubesphere/kubesphere)
|
||||
|
||||
----
|
||||
***KubeSphere*** is a distribution of [Kubernetes](https://kubernetes.io), aimed to provide quick setup, friendly and easily use, and powerful management features for Kubernetes clusters, which could help both personal and enterprise users, reduce their learning curve of Kubernetes, accelerate their transform process from other container platforms to Kubernetes.
|
||||
|
||||
**Features:**
|
||||
- Multiple IaaS platform support, including baremetal/KVM/QingCloud, and more will be supported in future release.
|
||||
- Easy setup of Kubernetes standalone(only one master node) and cluster environment(including High Availability support).
|
||||
- Powerful management console to help business users to manage and monitor the Kubernetes.
|
||||
- Integrate with [OpenPitrix](https://github.com/openpitrix) to provide full life cycle of application management and be compatible of helm package.
|
||||
- Support popular open source network solutions, including calico and flannel, also could use [qingcloud hostnic solution](https://github.com/yunify/hostnic-cni) if the Kubernetes is deployed on QingCloud platform.
|
||||
- Support popular open source storage solutions, including Glusterfs and Cephfs, also could use [qingcloud storage solution](https://github.com/yunify/qingcloud-csi) or [qingstor storage solution](https://github.com/yunify/qingstor-csi) if the Kubernetes is deployed on QingCloud platform or QingStor NeonSAN.
|
||||
- CI/CD support.
|
||||
- Service Mesh support.
|
||||
- Multiple image registries support.
|
||||
- Integrate with QingCloud IAM.
|
||||
----
|
||||
|
||||
## Motivation
|
||||
|
||||
The project originates from the requirement and pains we heard from our customers on public and private QingCloud platform, who have strong will to deploy Kubernetes in their IT system but struggle on completed setup process and long learning curve. With help of KubeSphere, their IT operators could setup Kubernetes environment quickly and use an easy management UI interface to mange their applications, also KubeSphere provides more features to help customers to handle daily business more easily, including CI/CD, micro services management...etc.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
**TBD**
|
||||
|
||||
## Design
|
||||
|
||||
## Contributing to the project
|
||||
|
||||
All members of the KubeSphere community must abide by [Code of Conduct](docs/code-of-conduct.md). Only by respecting each other can we develop a productive, collaborative community.
|
||||
|
||||
You can then find out more detail [here](docs/welcome-toKubeSphere-new-developer-guide.md).
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
ls /go/bin
|
||||
go version
|
||||
exit
|
||||
@@ -1,19 +0,0 @@
|
||||
# Copyright 2018 The KubeSphere Authors. All rights reserved.
|
||||
# Use of this source code is governed by a Apache license
|
||||
# that can be found in the LICENSE file.
|
||||
|
||||
FROM golang:1.10.2-alpine3.7 as builder
|
||||
|
||||
RUN apk add --no-cache git curl openssl
|
||||
|
||||
RUN go get github.com/tools/godep
|
||||
#RUN go get github.com/emicklei/go-restful
|
||||
#RUN go get github.com/golang/glog
|
||||
#RUN go get github.com/spf13/pflag
|
||||
RUN go get golang.org/x/tools/cmd/goimports
|
||||
|
||||
FROM golang:1.10.2-alpine3.7
|
||||
|
||||
RUN apk add --no-cache git make curl openssl jq rsync godep
|
||||
|
||||
COPY --from=builder /go/bin /go/bin
|
||||
@@ -1,17 +0,0 @@
|
||||
# Copyright 2018 The KubeSphere Authors. All rights reserved.
|
||||
# Use of this source code is governed by a Apache license
|
||||
# that can be found in the LICENSE file.
|
||||
|
||||
default:
|
||||
docker build -t kubesphere/kubesphere-builder .
|
||||
@echo "ok"
|
||||
|
||||
pull:
|
||||
docker pull kubesphere/kubesphere-builder
|
||||
@echo "ok"
|
||||
|
||||
run:
|
||||
docker run --rm -it -v `pwd`:/root kubesphere/kubesphere-builder
|
||||
|
||||
clean:
|
||||
@echo "ok"
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/app"
|
||||
"kubesphere.io/kubesphere/pkg/logs"
|
||||
"kubesphere.io/kubesphere/pkg/options"
|
||||
"kubesphere.io/kubesphere/pkg/version"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
options.AddFlags(pflag.CommandLine)
|
||||
|
||||
pflag.Parse()
|
||||
logs.InitLogs()
|
||||
|
||||
defer logs.FlushLogs()
|
||||
|
||||
version.PrintAndExitIfRequested()
|
||||
|
||||
app.Run()
|
||||
}
|
||||
6
doc.go
6
doc.go
@@ -1,6 +0,0 @@
|
||||
// Copyright 2017 The OpenPitrix Authors. All rights reserved.
|
||||
// Use of this source code is governed by a Apache license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package openpitrix provides the best Paas and Iaas platform.
|
||||
package kubesphere
|
||||
@@ -1,14 +0,0 @@
|
||||
# KubeSphere Code of Conduct
|
||||
|
||||
KubeSphere follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||
|
||||
# Best practice of committing code
|
||||
Besides following above conduct from CNCF, we also hope every contributor in this project could help us to improve the quality of code, something you should know before checking in any new code:
|
||||
- As gopher, make sure you already read [the conduct of Go language](https://golang.org/conduct) and [the instruction of writting Go](https://golang.org/doc/effective_go.html).
|
||||
- Fork the project under your account and make the changes you want there.
|
||||
- Execute 'go fmt' for every piece of new code.
|
||||
- Every pulling request(PR) would be better constructed with only one commit, this could help code reviewer to go through your code efficiently, also helpful for every follower of this project to understand what happens in this PR. If you need to make any further code change to address the comments from reviewers, which means some new commits will be generated under this PR, you need to use 'git rebase' to combine those commits together.
|
||||
- Every PR should only solve one problem or provide one feature, don't put several different fixes into one PR.
|
||||
- At lease two code reviewers should involve into code reviewing process.
|
||||
- Please introduce new third-party packages as little as possible to reduce the vendor dependency of this project. For example, don't import a full unit converting package but only use one function from it. For this case, you'd better write that function by yourself.
|
||||
- more.
|
||||
@@ -1,6 +0,0 @@
|
||||
# Architecture
|
||||
|
||||
Basic idea is to decouple application repository and runtime environment. The runtime environment an application can run is by matching the labels of runtime environment and the selector of the repository where the application is from.
|
||||
|
||||
## Design key points:
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
// Copyright 2017 The OpenPitrix Authors. All rights reserved.
|
||||
// Use of this source code is governed by a Apache license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// dot -Tpng -o output.png input.dot
|
||||
|
||||
digraph G {
|
||||
rankdir = LR;
|
||||
|
||||
subgraph clusterClient {
|
||||
node [
|
||||
fixedsize = true,
|
||||
width = 1, height = 1,
|
||||
]
|
||||
WebUI [shape = doublecircle];
|
||||
MobileApp [shape = doublecircle];
|
||||
XClient [shape = doublecircle];
|
||||
}
|
||||
|
||||
WebUI -> ApiGateway[
|
||||
label = "rest api",
|
||||
dir = both,
|
||||
style = bold,
|
||||
];
|
||||
MobileApp -> ApiGateway[
|
||||
label = "rest api",
|
||||
dir = both,
|
||||
style = bold,
|
||||
];
|
||||
XClient -> ApiGateway[
|
||||
label = "rest api",
|
||||
dir = both,
|
||||
style = bold,
|
||||
];
|
||||
|
||||
subgraph clusterOpenpitrix {
|
||||
|
||||
// rest api gateway
|
||||
ApiGateway [shape = rect,
|
||||
fixedsize = true,
|
||||
width = 1.4, height = 6.2,
|
||||
];
|
||||
|
||||
// microservice
|
||||
subgraph clusterServices {
|
||||
node [
|
||||
shape=record,
|
||||
fixedsize = true,
|
||||
width = 2.6, height = 1.3,
|
||||
];
|
||||
Cluster [shape = Mrecord];
|
||||
Runtime [shape = Mrecord,
|
||||
label="Runtime |{ plugin |{k8s|QingCloud|Other} }"
|
||||
];
|
||||
App [shape = Mrecord];
|
||||
Repo [shape = Mrecord];
|
||||
}
|
||||
|
||||
// service database
|
||||
subgraph clusterDB {
|
||||
node [
|
||||
fixedsize = true,
|
||||
width = 2.6, height = 1.3,
|
||||
];
|
||||
RepoDB [shape = cylinder];
|
||||
AppDB [shape = cylinder];
|
||||
RuntimeDB [shape = cylinder];
|
||||
ClusterDB [shape = cylinder];
|
||||
}
|
||||
|
||||
// api gateway
|
||||
ApiGateway -> Cluster [
|
||||
label = "grpc",
|
||||
dir = both,
|
||||
style = bold,
|
||||
];
|
||||
ApiGateway -> Repo [
|
||||
label = "grpc",
|
||||
dir = both,
|
||||
style = bold,
|
||||
];
|
||||
ApiGateway -> App [
|
||||
label = "grpc",
|
||||
dir = both,
|
||||
style = bold,
|
||||
];
|
||||
ApiGateway -> Runtime [
|
||||
label = "grpc",
|
||||
dir = both,
|
||||
style = bold,
|
||||
];
|
||||
|
||||
Repo -> RepoDB [label="SQL"];
|
||||
App -> AppDB [label="SQL"];
|
||||
Runtime -> RuntimeDB [label="SQL"];
|
||||
Cluster -> ClusterDB [label="SQL"];
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 551 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 399 KiB |
@@ -1,57 +0,0 @@
|
||||
# Developing for KubeSphere [deprecated]
|
||||
|
||||
This document is intended to be the canonical source of truth for things like
|
||||
supported toolchain versions for building KubeSphere.
|
||||
If you find a requirement that this doc does not capture, or if you find other
|
||||
docs with references to requirements that are not simply links to this doc,
|
||||
please [submit an issue](https://github.com/kubesphere/kubesphere/issues/new).
|
||||
|
||||
This document is intended to be relative to the branch in which it is found.
|
||||
It is guaranteed that requirements will change over time for the development
|
||||
branch, but release branches should not change.
|
||||
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Setting up Go](#setting-up-go)
|
||||
- [To start developing KubeSphere](#to-start-developing-kubesphere)
|
||||
- [DevOps](#devops)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
KubeSphere only has few external dependencies you need to setup before being
|
||||
able to build and run the code.
|
||||
|
||||
### Setting up Go
|
||||
|
||||
KubeSphere written in the [Go](http://golang.org) programming language.
|
||||
To build, you'll need a Go (version 1.9+) development environment.
|
||||
If you haven't set up a Go development environment, please follow
|
||||
[these instructions](https://golang.org/doc/install)
|
||||
to install the Go tools.
|
||||
|
||||
Set up your GOPATH and add a path entry for Go binaries to your PATH. Typically
|
||||
added to your ~/.profile:
|
||||
|
||||
```shell
|
||||
$ export GOPATH=~/go
|
||||
$ export PATH=$PATH:$GOPATH/bin
|
||||
```
|
||||
|
||||
## To start developing KubeSphere
|
||||
|
||||
There are two options to get KubeSphere source code and build the project:
|
||||
|
||||
**You have a working Go environment.**
|
||||
|
||||
```shell
|
||||
$ go get -d kubesphere.io/kubesphere
|
||||
$ cd $GOPATH/src/kubesphere.io/kubesphere
|
||||
$ make all
|
||||
```
|
||||
|
||||
**You have a working Docker environment.**
|
||||
|
||||
```shell
|
||||
$ git clone https://github.com/kubesphere/kubesphere
|
||||
$ cd kubesphere
|
||||
$ make
|
||||
```
|
||||
@@ -1,316 +0,0 @@
|
||||
# Pull Request Process
|
||||
|
||||
This doc explains the process and best practices for submitting a PR to the [KubeSphere project](https://github.com/kubeSphere/kubeSphere). It should serve as a reference for all contributors, and be useful especially to new and infrequent submitters.
|
||||
|
||||
- [Before You Submit a PR](#before-you-submit-a-pr)
|
||||
* [Run Local Verifications](#run-local-verifications)
|
||||
- [The PR Submit Process](#the-pr-submit-process)
|
||||
* [Write Release Notes if Needed](#write-release-notes-if-needed)
|
||||
* [The Testing and Merge Workflow](#the-testing-and-merge-workflow)
|
||||
* [Marking Unfinished Pull Requests](#marking-unfinished-pull-requests)
|
||||
* [Comment Commands Reference](#comment-commands-reference)
|
||||
* [Automation](#automation)
|
||||
* [How the e2e Tests Work](#how-the-e2e-tests-work)
|
||||
- [Why was my PR closed?](#why-was-my-pr-closed)
|
||||
- [Why is my PR not getting reviewed?](#why-is-my-pr-not-getting-reviewed)
|
||||
- [Best Practices for Faster Reviews](#best-practices-for-faster-reviews)
|
||||
* [0. Familiarize yourself with project conventions](#0-familiarize-yourself-with-project-conventions)
|
||||
* [1. Is the feature wanted? Make a Design Doc or Sketch PR](#1-is-the-feature-wanted-make-a-design-doc-or-sketch-pr)
|
||||
* [2. Smaller Is Better: Small Commits, Small PRs](#2-smaller-is-better-small-commits-small-prs)
|
||||
* [3. Open a Different PR for Fixes and Generic Features](#3-open-a-different-pr-for-fixes-and-generic-features)
|
||||
* [4. Comments Matter](#4-comments-matter)
|
||||
* [5. Test](#5-test)
|
||||
* [6. Squashing and Commit Titles](#6-squashing-and-commit-titles)
|
||||
* [7. KISS, YAGNI, MVP, etc.](#7-kiss-yagni-mvp-etc)
|
||||
* [8. It's OK to Push Back](#8-its-ok-to-push-back)
|
||||
* [9. Common Sense and Courtesy](#9-common-sense-and-courtesy)
|
||||
|
||||
|
||||
# Before You Submit a PR
|
||||
|
||||
This guide is for contributors who already have a PR to submit. If you're looking for information on setting up your developer environment and creating code to contribute to KubeSphere, see the [development guide](development.md).
|
||||
|
||||
**Make sure your PR adheres to our best practices. These include following project conventions, making small PRs, and commenting thoroughly. Please read the more detailed section on [Best Practices for Faster Reviews](#best-practices-for-faster-reviews) at the end of this doc.**
|
||||
|
||||
## Run Local Verifications
|
||||
|
||||
You can run these local verifications before you submit your PR to predict the
|
||||
pass or fail of continuous integration.
|
||||
|
||||
# The PR Submit Process
|
||||
|
||||
Merging a PR requires the following steps to be completed before the PR will be merged automatically. For details about each step, see the [The Testing and Merge Workflow](#the-testing-and-merge-workflow) section below.
|
||||
|
||||
- Make the PR
|
||||
- Release notes - do one of the following:
|
||||
- Add notes in the release notes block, or
|
||||
- Update the release note label
|
||||
- Pass all tests
|
||||
- Get a `/lgtm` from a reviewer
|
||||
- Get approval from an owner
|
||||
|
||||
If your PR meets all of the steps above, it will enter the submit queue to be merged. When it is next in line to be merged, the tests will run a second time. If all tests pass, the PR will be merged automatically.
|
||||
|
||||
## Write Release Notes if Needed
|
||||
|
||||
Release notes are required for any PR with user-visible changes, such as bug-fixes, feature additions, and output format changes.
|
||||
|
||||
If you don't add release notes in the PR template, the `do-not-merge/release-note-label-needed` label is added to your PR automatically after you create it. There are a few ways to update it.
|
||||
|
||||
To add a release-note section to the PR description:
|
||||
|
||||
For PRs with a release note:
|
||||
|
||||
```release-note
|
||||
Your release note here
|
||||
```
|
||||
|
||||
For PRs that require additional action from users switching to the new release, include the string "action required" (case insensitive) in the release note:
|
||||
|
||||
```release-note
|
||||
action required: your release note here
|
||||
```
|
||||
|
||||
For PRs that don't need to be mentioned at release time, just write "NONE" (case insensitive):
|
||||
|
||||
```release-note
|
||||
NONE
|
||||
```
|
||||
|
||||
The `/release-note-none` comment command can still be used as an alternative to writing "NONE" in the release-note block if it is left empty.
|
||||
|
||||
To see how to format your release notes, view the [PR template](https://github.com/) for a brief example. PR titles and body comments can be modified at any time prior to the release to make them friendly for release notes.
|
||||
// PR template TODO
|
||||
|
||||
Release notes apply to PRs on the master branch. For cherry-pick PRs, see the [cherry-pick instructions](cherry-picks.md). The only exception to these rules is when a PR is not a cherry-pick and is targeted directly to the non-master branch. In this case, a `release-note-*` label is required for that non-master PR.
|
||||
|
||||
// cherry-pick TODO
|
||||
|
||||
Now that your release notes are in shape, let's look at how the PR gets tested and merged.
|
||||
|
||||
## The Testing and Merge Workflow
|
||||
|
||||
The KubeSphere merge workflow uses comments to run tests and labels for merging PRs.
|
||||
NOTE: For pull requests that are in progress but not ready for review,
|
||||
prefix the PR title with `WIP` or `[WIP]` and track any remaining TODOs
|
||||
in a checklist in the pull request description.
|
||||
|
||||
Here's the process the PR goes through on its way from submission to merging:
|
||||
|
||||
1. Make the pull request
|
||||
1. `@o8x-merge-robot` assigns reviewers //TODO
|
||||
|
||||
If you're **not** a member of the KubeSphere organization:
|
||||
|
||||
1. Reviewer/KubeSphere Member checks that the PR is safe to test. If so, they comment `/ok-to-test`
|
||||
1. Reviewer suggests edits
|
||||
1. Push edits to your PR branch
|
||||
1. Repeat the prior two steps as needed
|
||||
1. (Optional) Some reviewers prefer that you squash commits at this step
|
||||
1. Owner is assigned and will add the `/approve` label to the PR
|
||||
|
||||
If you are a member, or a member comments `/ok-to-test`, the PR will be considered to be trusted. Then the pre-submit tests will run:
|
||||
|
||||
1. Automatic tests run. See the current list of tests on the
|
||||
1. If tests fail, resolve issues by pushing edits to your PR branch
|
||||
1. If the failure is a flake, anyone on trusted PRs can comment `/retest` to rerun failed tests
|
||||
|
||||
Once the tests pass, all failures are commented as flakes, or the reviewer adds the labels `lgtm` and `approved`, the PR enters the final merge queue. The merge queue is needed to make sure no incompatible changes have been introduced by other PRs since the tests were last run on your PR.
|
||||
|
||||
Either the [on call contributor](on-call-rotations.md) will manage the merge queue manually. //TODO
|
||||
|
||||
1. The PR enters the merge queue ([http://submit-queue.KubeSphere.io]())
|
||||
1. The merge queue triggers a test re-run with the comment `/test all [submit-queue is verifying that this PR is safe to merge]`
|
||||
1. Author has signed the CLA (`cncf-cla: yes` label added to PR)
|
||||
1. No changes made since last `lgtm` label applied
|
||||
1. If tests fail, resolve issues by pushing edits to your PR branch
|
||||
1. If the failure is a flake, anyone can comment `/retest` if the PR is trusted
|
||||
1. If tests pass, the merge queue automatically merges the PR
|
||||
|
||||
That's the last step. Your PR is now merged.
|
||||
|
||||
## Marking Unfinished Pull Requests
|
||||
|
||||
If you want to solicit reviews before the implementation of your pull request is complete, you should hold your pull request to ensure that the merge queue does not pick it up and attempt to merge it. There are two methods to achieve this:
|
||||
|
||||
1. You may add the `/hold` or `/hold cancel` comment commands
|
||||
2. You may add or remove a `WIP` or `[WIP]` prefix to your pull request title
|
||||
|
||||
The GitHub robots will add and remove the `do-not-merge/hold` label as you use the comment commands and the `do-not-merge/work-in-progress` label as you edit your title. While either label is present, your pull request will not be considered for merging.
|
||||
|
||||
|
||||
## Comment Commands Reference//TODO
|
||||
|
||||
[The commands doc]() contains a reference for all comment commands. //TODO
|
||||
|
||||
## Automation//TODO
|
||||
|
||||
The KubeSphere developer community uses a variety of automation to manage pull requests. This automation is described in detail [in the automation doc](automation.md). //TODO
|
||||
|
||||
## How the Tests Work//TODO
|
||||
|
||||
The tests will post the status results to the PR. If an e2e test fails,
|
||||
`@o8x-ci-robot` will comment on the PR with the test history and the
|
||||
comment-command to re-run that test. e.g.
|
||||
|
||||
> The following tests failed, say /retest to rerun them all.
|
||||
|
||||
# Why was my PR closed?
|
||||
|
||||
Pull requests older than 90 days will be closed. Exceptions can be made for PRs that have active review comments, or that are awaiting other dependent PRs. Closed pull requests are easy to recreate, and little work is lost by closing a pull request that subsequently needs to be reopened. We want to limit the total number of PRs in flight to:
|
||||
* Maintain a clean project
|
||||
* Remove old PRs that would be difficult to rebase as the underlying code has changed over time
|
||||
* Encourage code velocity
|
||||
|
||||
# Why is my PR not getting reviewed?
|
||||
|
||||
A few factors affect how long your PR might wait for review.
|
||||
|
||||
If it's the last few weeks of a milestone, we need to reduce churn and stabilize.
|
||||
|
||||
Or, it could be related to best practices. One common issue is that the PR is too big to review. Let's say you've touched 39 files and have 8657 insertions. When your would-be reviewers pull up the diffs, they run away - this PR is going to take 4 hours to review and they don't have 4 hours right now. They'll get to it later, just as soon as they have more free time (ha!).
|
||||
|
||||
There is a detailed rundown of best practices, including how to avoid too-lengthy PRs, in the next section.
|
||||
|
||||
But, if you've already followed the best practices and you still aren't getting any PR love, here are some
|
||||
things you can do to move the process along:
|
||||
|
||||
* Make sure that your PR has an assigned reviewer (assignee in GitHub). If not, reply to the PR comment stream asking for a reviewer to be assigned.
|
||||
|
||||
* Ping the assignee (@username) on the PR comment stream, and ask for an estimate of when they can get to the review.
|
||||
|
||||
* Ping the assignee on [Slack](http://KubeSphere.slack.com). Remember that a person's GitHub username might not be the same as their Slack username.
|
||||
|
||||
* Ping the assignee by email (many of us have publicly available email addresses).
|
||||
|
||||
* If you're a member of the organization ping the [team](https://github.com/orgs/KubeSphere/teams) (via @team-name) that works in the area you're submitting code.
|
||||
|
||||
* If you have fixed all the issues from a review, and you haven't heard back, you should ping the assignee on the comment stream with a "please take another look" (`PTAL`) or similar comment indicating that you are ready for another review.
|
||||
|
||||
Read on to learn more about how to get faster reviews by following best practices.
|
||||
|
||||
# Best Practices for Faster Reviews
|
||||
|
||||
Most of this section is not specific to KubeSphere, but it's good to keep these best practices in mind when you're making a PR.
|
||||
|
||||
You've just had a brilliant idea on how to make KubeSphere better. Let's call that idea Feature-X. Feature-X is not even that complicated. You have a pretty good idea of how to implement it. You jump in and implement it, fixing a bunch of stuff along the way. You send your PR - this is awesome! And it sits. And sits. A week goes by and nobody reviews it. Finally, someone offers a few comments, which you fix up and wait for more review. And you wait. Another week or two go by. This is horrible.
|
||||
|
||||
Let's talk about best practices so your PR gets reviewed quickly.
|
||||
|
||||
## 0. Familiarize yourself with project conventions
|
||||
|
||||
* [Development guide](code-of-conduct.md)
|
||||
|
||||
## 1. Is the feature wanted? Make a Design Doc or Sketch PR
|
||||
|
||||
Are you sure Feature-X is something the KubeSphere team wants or will accept? Is it implemented to fit with other changes in flight? Are you willing to bet a few days or weeks of work on it?
|
||||
|
||||
It's better to get confirmation beforehand. There are two ways to do this:
|
||||
|
||||
- Make a proposal doc (in docs/proposals; for example [the QoS proposal]()
|
||||
- Coordinate your effort with [SIG Docs]() ahead of time. //TODO
|
||||
- Make a sketch PR (e.g., just the API or Go interface). Write or code up just enough to express the idea and the design and why you made those choices
|
||||
|
||||
Or, do all of the above.
|
||||
|
||||
Be clear about what type of feedback you are asking for when you submit a proposal doc or sketch PR.
|
||||
|
||||
Now, if we ask you to change the design, you won't have to re-write it all.
|
||||
|
||||
## 2. Smaller Is Better: Small Commits, Small PRs
|
||||
|
||||
Small commits and small PRs get reviewed faster and are more likely to be correct than big ones.
|
||||
|
||||
Attention is a scarce resource. If your PR takes 60 minutes to review, the reviewer's eye for detail is not as keen in the last 30 minutes as it was in the first. It might not get reviewed at all if it requires a large continuous block of time from the reviewer.
|
||||
|
||||
**Breaking up commits**
|
||||
|
||||
Break up your PR into multiple commits, at logical break points.
|
||||
|
||||
Making a series of discrete commits is a powerful way to express the evolution of an idea or the
|
||||
different ideas that make up a single feature. Strive to group logically distinct ideas into separate commits.
|
||||
|
||||
For example, if you found that Feature-X needed some prefactoring to fit in, make a commit that JUST does that prefactoring. Then make a new commit for Feature-X.
|
||||
|
||||
Strike a balance with the number of commits. A PR with 25 commits is still very cumbersome to review, so use
|
||||
judgment.
|
||||
|
||||
**Breaking up PRs**
|
||||
|
||||
Or, going back to our prefactoring example, you could also fork a new branch, do the prefactoring there and send a PR for that. If you can extract whole ideas from your PR and send those as PRs of their own, you can avoid the painful problem of continually rebasing.
|
||||
|
||||
KubeSphere is a fast-moving codebase - lock in your changes ASAP with your small PR, and make merges be someone else's problem.
|
||||
|
||||
Multiple small PRs are often better than multiple commits. Don't worry about flooding us with PRs. We'd rather have 100 small, obvious PRs than 10 unreviewable monoliths.
|
||||
|
||||
We want every PR to be useful on its own, so use your best judgment on what should be a PR vs. a commit.
|
||||
|
||||
As a rule of thumb, if your PR is directly related to Feature-X and nothing else, it should probably be part of the Feature-X PR. If you can explain why you are doing seemingly no-op work ("it makes the Feature-X change easier, I promise") we'll probably be OK with it. If you can imagine someone finding value independently of Feature-X, try it as a PR. (Do not link pull requests by `#` in a commit description, because GitHub creates lots of spam. Instead, reference other PRs via the PR your commit is in.)
|
||||
|
||||
## 3. Open a Different PR for Fixes and Generic Features
|
||||
|
||||
**Put changes that are unrelated to your feature into a different PR.**
|
||||
|
||||
Often, as you are implementing Feature-X, you will find bad comments, poorly named functions, bad structure, weak type-safety, etc.
|
||||
|
||||
You absolutely should fix those things (or at least file issues, please) - but not in the same PR as your feature. Otherwise, your diff will have way too many changes, and your reviewer won't see the forest for the trees.
|
||||
|
||||
**Look for opportunities to pull out generic features.**
|
||||
|
||||
For example, if you find yourself touching a lot of modules, think about the dependencies you are introducing between packages. Can some of what you're doing be made more generic and moved up and out of the Feature-X package? Do you need to use a function or type from an otherwise unrelated package? If so, promote! We have places for hosting more generic code.
|
||||
|
||||
Likewise, if Feature-X is similar in form to Feature-W which was checked in last month, and you're duplicating some tricky stuff from Feature-W, consider prefactoring the core logic out and using it in both Feature-W and
|
||||
Feature-X. (Do that in its own commit or PR, please.)
|
||||
|
||||
## 4. Comments Matter
|
||||
|
||||
In your code, if someone might not understand why you did something (or you won't remember why later), comment it. Many code-review comments are about this exact issue.
|
||||
|
||||
If you think there's something pretty obvious that we could follow up on, add a TODO.
|
||||
|
||||
Read up on [GoDoc](https://blog.golang.org/godoc-documenting-go-code) - follow those general rules for comments.
|
||||
|
||||
## 5. Test
|
||||
|
||||
Nothing is more frustrating than starting a review, only to find that the tests are inadequate or absent. Very few PRs can touch code and NOT touch tests.
|
||||
|
||||
If you don't know how to test Feature-X, please ask! We'll be happy to help you design things for easy testing or to suggest appropriate test cases.
|
||||
|
||||
## 6. Squashing and Commit Titles
|
||||
|
||||
Your reviewer has finally sent you feedback on Feature-X.
|
||||
|
||||
Make the fixups, and don't squash yet. Put them in a new commit, and re-push. That way your reviewer can look at the new commit on its own, which is much faster than starting over.
|
||||
|
||||
We might still ask you to clean up your commits at the very end for the sake of a more readable history, but don't do this until asked: typically at the point where the PR would otherwise be tagged `LGTM`.
|
||||
|
||||
Each commit should have a good title line (<70 characters) and include an additional description paragraph describing in more detail the change intended.
|
||||
|
||||
**General squashing guidelines:**
|
||||
|
||||
* Sausage => squash
|
||||
|
||||
Do squash when there are several commits to fix bugs in the original commit(s), address reviewer feedback, etc. Really we only want to see the end state and commit message for the whole PR.
|
||||
|
||||
* Layers => don't squash
|
||||
|
||||
Don't squash when there are independent changes layered to achieve a single goal. For instance, writing a code munger could be one commit, applying it could be another, and adding a precommit check could be a third. One could argue they should be separate PRs, but there's really no way to test/review the munger without seeing it applied, and there needs to be a precommit check to ensure the munged output doesn't immediately get out of date.
|
||||
|
||||
A commit, as much as possible, should be a single logical change.
|
||||
|
||||
## 7. KISS, YAGNI, MVP, etc.
|
||||
|
||||
Sometimes we need to remind each other of core tenets of software design - Keep It Simple, You Aren't Gonna Need It, Minimum Viable Product, and so on. Adding a feature "because we might need it later" is antithetical to software that ships. Add the things you need NOW and (ideally) leave room for things you might need later - but don't implement them now.
|
||||
|
||||
## 8. It's OK to Push Back
|
||||
|
||||
Sometimes reviewers make mistakes. It's OK to push back on changes your reviewer requested. If you have a good reason for doing something a certain way, you are absolutely allowed to debate the merits of a requested change. Both the reviewer and reviewee should strive to discuss these issues in a polite and respectful manner.
|
||||
|
||||
You might be overruled, but you might also prevail. We're pretty reasonable people. Mostly.
|
||||
|
||||
Another phenomenon of open-source projects (where anyone can comment on any issue) is the dog-pile - your PR gets so many comments from so many people it becomes hard to follow. In this situation, you can ask the primary reviewer (assignee) whether they want you to fork a new PR to clear out all the comments. You don't HAVE to fix every issue raised by every person who feels like commenting, but you should answer reasonable comments with an explanation.
|
||||
|
||||
## 9. Common Sense and Courtesy
|
||||
|
||||
No document can take the place of common sense and good taste. Use your best judgment, while you put
|
||||
a bit of thought into how your work can be made easier to review. If you do these things your PRs will get merged with less friction.
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
Welcome to KubeSphere! (New Developer Guide)
|
||||
============================================
|
||||
|
||||
_This document assumes that you know what KubeSphere does.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This
|
||||
document will help you understand the organization of the KubeSphere project and
|
||||
direct you to the best places to get started. By the end of this doc, you'll be
|
||||
able to pick up issues, write code to fix them, and get your work reviewed and
|
||||
merged.
|
||||
|
||||
If you have questions about the development process, feel free to jump into our
|
||||
[Slack workspace](http://KubeSphere.slack.com/). If you join the
|
||||
Slack workspace it is recommended to set your Slack display name to your GitHub
|
||||
account handle.
|
||||
|
||||
|
||||
|
||||
Downloading, Building, and Testing KubeSphere
|
||||
---------------------------------------------
|
||||
|
||||
This guide is non-technical, so it does not cover the technical details of
|
||||
working KubeSphere. We have plenty of documentation available under
|
||||
[docs.kubesphere.io](https://docs.kubesphere.io).
|
||||
|
||||
Pull-Request Process
|
||||
--------------------
|
||||
|
||||
The pull-request process is documented in [pull-requests.md](pull-requests.md).
|
||||
As described in that document, you must sign the CLA before
|
||||
KubeSphere can accept your contribution.
|
||||
|
||||
Thanks for reading!
|
||||
@@ -1,24 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kubesphere-router-gateway
|
||||
labels:
|
||||
app: kubesphere
|
||||
component: ks-router
|
||||
tier: backend
|
||||
spec:
|
||||
selector:
|
||||
app: kubesphere
|
||||
component: ks-router
|
||||
tier: backend
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
- name: https
|
||||
protocol: TCP
|
||||
port: 443
|
||||
targetPort: 443
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ks-router
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: kubesphere
|
||||
component: ks-router
|
||||
tier: backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: kubesphere
|
||||
component: ks-router
|
||||
tier: backend
|
||||
annotations:
|
||||
prometheus.io/port: '10254'
|
||||
prometheus.io/scrape: 'true'
|
||||
spec:
|
||||
serviceAccountName: kubesphere-router-serviceaccount
|
||||
containers:
|
||||
- name: nginx-ingress-controller
|
||||
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.16.2
|
||||
args:
|
||||
- /nginx-ingress-controller
|
||||
- --default-backend-service=$(POD_NAMESPACE)/default-http-backend
|
||||
- --configmap=$(POD_NAMESPACE)/nginx-configuration
|
||||
- --annotations-prefix=nginx.ingress.kubernetes.io
|
||||
- --force-namespace-isolation
|
||||
- --update-status
|
||||
- --update-status-on-shutdown
|
||||
env:
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
- name: https
|
||||
containerPort: 443
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 10254
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
readinessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 10254
|
||||
scheme: HTTP
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
securityContext:
|
||||
runAsNonRoot: false
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||
docker push kubesphere/ks-apiserver:latest
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 445 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,60 +0,0 @@
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
<style>
|
||||
html
|
||||
{
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after
|
||||
{
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
margin:0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="./swagger-ui-bundle.js"> </script>
|
||||
<script src="./swagger-ui-standalone-preset.js"> </script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
|
||||
// Build a system
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "/swagger-ui/api.json",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
})
|
||||
|
||||
window.ui = ui
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,67 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en-US">
|
||||
<body onload="run()">
|
||||
</body>
|
||||
</html>
|
||||
<script>
|
||||
'use strict';
|
||||
function run () {
|
||||
var oauth2 = window.opener.swaggerUIRedirectOauth2;
|
||||
var sentState = oauth2.state;
|
||||
var redirectUrl = oauth2.redirectUrl;
|
||||
var isValid, qp, arr;
|
||||
|
||||
if (/code|token|error/.test(window.location.hash)) {
|
||||
qp = window.location.hash.substring(1);
|
||||
} else {
|
||||
qp = location.search.substring(1);
|
||||
}
|
||||
|
||||
arr = qp.split("&")
|
||||
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
|
||||
qp = qp ? JSON.parse('{' + arr.join() + '}',
|
||||
function (key, value) {
|
||||
return key === "" ? value : decodeURIComponent(value)
|
||||
}
|
||||
) : {}
|
||||
|
||||
isValid = qp.state === sentState
|
||||
|
||||
if ((
|
||||
oauth2.auth.schema.get("flow") === "accessCode"||
|
||||
oauth2.auth.schema.get("flow") === "authorizationCode"
|
||||
) && !oauth2.auth.code) {
|
||||
if (!isValid) {
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "warning",
|
||||
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
|
||||
});
|
||||
}
|
||||
|
||||
if (qp.code) {
|
||||
delete oauth2.state;
|
||||
oauth2.auth.code = qp.code;
|
||||
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
||||
} else {
|
||||
let oauthErrorMsg
|
||||
if (qp.error) {
|
||||
oauthErrorMsg = "["+qp.error+"]: " +
|
||||
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
|
||||
(qp.error_uri ? "More info: "+qp.error_uri : "");
|
||||
}
|
||||
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "error",
|
||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
|
||||
});
|
||||
}
|
||||
} else {
|
||||
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
|
||||
}
|
||||
window.close();
|
||||
}
|
||||
</script>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package components
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/filter/route"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
ws.Route(ws.GET(subPath).To(handleGetComponents).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET(subPath+"/{namespace}/{componentName}").To(handleGetComponentStatus).
|
||||
Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET("/health").To(handleGetSystemHealthStatus).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
}
|
||||
|
||||
func handleGetSystemHealthStatus(request *restful.Request, response *restful.Response) {
|
||||
|
||||
if status, err := models.GetSystemHealthStatus(); err != nil {
|
||||
err = response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
} else {
|
||||
err = response.WriteAsJson(status)
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get a specific component status
|
||||
func handleGetComponentStatus(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
componentName := request.PathParameter("componentName")
|
||||
|
||||
if component, err := models.GetComponentStatus(namespace, componentName); err != nil {
|
||||
err = response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
} else {
|
||||
if err = response.WriteAsJson(component); err != nil {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get all components
|
||||
func handleGetComponents(request *restful.Request, response *restful.Response) {
|
||||
|
||||
result, err := models.GetAllComponentsStatus()
|
||||
|
||||
if err != nil {
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
} else {
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package containers
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/filter/route"
|
||||
"kubesphere.io/kubesphere/pkg/models/metrics"
|
||||
)
|
||||
|
||||
// {namespace} namespace name
|
||||
// {node} node host name
|
||||
// {pod} pod name
|
||||
// {container} container name
|
||||
func Register(ws *restful.WebService) {
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}/containers/{container}").To(handleContainerUnderNameSpaceAndPod).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}/containers").To(handleContainersUnderNameSpaceAndPod).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET("/nodes/{node}/namespaces/{namespace}/pods/{pod}/containers").To(handleContainersUnderNodeAndNameSpaceAndPod).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
}
|
||||
|
||||
func handleContainerUnderNameSpaceAndPod(request *restful.Request, response *restful.Response) {
|
||||
var resultContainer metrics.ContainerMetrics
|
||||
resultContainer = metrics.FormatContainerMetrics(request.PathParameter("namespace"), request.PathParameter("pod"), request.PathParameter("container"))
|
||||
resultContainer.NodeName = metrics.GetNodeNameForPod(request.PathParameter("pod"), request.PathParameter("namespace"))
|
||||
response.WriteAsJson(resultContainer)
|
||||
}
|
||||
func handleContainersUnderNameSpaceAndPod(request *restful.Request, response *restful.Response) {
|
||||
var resultNameSpace constants.PageableResponse
|
||||
resultNameSpace = metrics.FormatContainersMetrics("", request.PathParameter("namespace"), request.PathParameter("pod"))
|
||||
response.WriteAsJson(resultNameSpace)
|
||||
}
|
||||
|
||||
func handleContainersUnderNodeAndNameSpaceAndPod(request *restful.Request, response *restful.Response) {
|
||||
var resultNameSpace constants.PageableResponse
|
||||
|
||||
resultNameSpace = metrics.FormatContainersMetrics(request.PathParameter("node"), request.PathParameter("namespace"), request.PathParameter("pod"))
|
||||
|
||||
response.WriteAsJson(resultNameSpace)
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package daemonsets
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
"k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
tags := []string{"daemonsets"}
|
||||
|
||||
ws.Route(ws.GET(subPath).To(getDaemonSetRevision).Consumes("*/*").Metadata(restfulspec.KeyOpenAPITags, tags).Doc("Handle daemonset" +
|
||||
" operation").Param(ws.PathParameter("daemonset", "daemonset's name").DataType("string")).Param(ws.PathParameter("namespace",
|
||||
"daemonset's namespace").DataType("string")).Param(ws.PathParameter("revision", "daemonset's revision")).Writes(v1.DaemonSet{}))
|
||||
}
|
||||
|
||||
func getDaemonSetRevision(req *restful.Request, resp *restful.Response) {
|
||||
daemonset := req.PathParameter("daemonset")
|
||||
namespace := req.PathParameter("namespace")
|
||||
revision := req.PathParameter("revision")
|
||||
|
||||
res, err := models.GetDaemonSetRevision(namespace, daemonset, revision)
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
resp.WriteHeaderAndEntity(http.StatusNotFound, constants.MessageResponse{Message: err.Error()})
|
||||
} else {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
resp.WriteEntity(res)
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package deployments
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
"k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
tags := []string{"deployments"}
|
||||
|
||||
ws.Route(ws.GET(subPath).To(getDeployRevision).Consumes("*/*").Metadata(restfulspec.KeyOpenAPITags, tags).Doc("Handle deployment" +
|
||||
" operation").Param(ws.PathParameter("deployment", "deployment's name").DataType("string")).Param(ws.PathParameter("namespace",
|
||||
"deployment's namespace").DataType("string")).Param(ws.PathParameter("deployment", "deployment's name")).Writes(v1.ReplicaSet{}))
|
||||
}
|
||||
|
||||
func getDeployRevision(req *restful.Request, resp *restful.Response) {
|
||||
deploy := req.PathParameter("deployment")
|
||||
namespace := req.PathParameter("namespace")
|
||||
revision := req.PathParameter("revision")
|
||||
|
||||
res, err := models.GetDeployRevision(namespace, deploy, revision)
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
resp.WriteHeaderAndEntity(http.StatusNotFound, constants.MessageResponse{Message: err.Error()})
|
||||
} else {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
resp.WriteEntity(res)
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package hpa
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
"k8s.io/api/autoscaling/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/client"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
tags := []string{"horizontalpodautoscalers"}
|
||||
|
||||
ws.Route(ws.GET(subPath).To(getHpa).Consumes("*/*").Metadata(restfulspec.KeyOpenAPITags, tags).Doc(
|
||||
"get horizontalpodautoscalers").Param(ws.PathParameter("namespace",
|
||||
"horizontalpodautoscalers's namespace").DataType("string")).Param(ws.PathParameter(
|
||||
"horizontalpodautoscaler", "horizontalpodautoscaler's name")).Writes(v1.HorizontalPodAutoscaler{}))
|
||||
}
|
||||
|
||||
func getHpa(req *restful.Request, resp *restful.Response) {
|
||||
hpa := req.PathParameter("horizontalpodautoscaler")
|
||||
namespace := req.PathParameter("namespace")
|
||||
client := client.NewK8sClient()
|
||||
|
||||
res, err := client.AutoscalingV1().HorizontalPodAutoscalers(namespace).Get(hpa, metaV1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
resp.WriteHeaderAndEntity(http.StatusOK, nil)
|
||||
} else {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, nil)
|
||||
}
|
||||
}
|
||||
|
||||
resp.WriteEntity(res)
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
|
||||
*/
|
||||
|
||||
package iam
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"k8s.io/api/rbac/v1"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/filter/route"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam"
|
||||
)
|
||||
|
||||
type roleList struct {
|
||||
ClusterRoles []v1.ClusterRole `json:"clusterRoles" protobuf:"bytes,2,rep,name=clusterRoles"`
|
||||
Roles []v1.Role `json:"roles" protobuf:"bytes,2,rep,name=roles"`
|
||||
}
|
||||
|
||||
type userRuleList struct {
|
||||
ClusterRules []iam.Rule `json:"clusterRules"`
|
||||
Rules map[string][]iam.Rule `json:"rules"`
|
||||
}
|
||||
|
||||
func Register(ws *restful.WebService) {
|
||||
//roles
|
||||
ws.Route(ws.GET("/users/{username}/roles").To(userRolesHandler).Filter(route.RouteLogging)).Produces(restful.MIME_JSON)
|
||||
//rules define
|
||||
ws.Route(ws.GET("/roles/rules").To(roleRulesHandler).Filter(route.RouteLogging)).Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET("/clusterroles/rules").To(clusterRoleRulesHandler).Filter(route.RouteLogging)).Produces(restful.MIME_JSON)
|
||||
//user->rules
|
||||
ws.Route(ws.GET("/rules").To(usersRulesHandler).Filter(route.RouteLogging)).Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET("/users/{username}/rules").To(userRulesHandler).Filter(route.RouteLogging)).Produces(restful.MIME_JSON)
|
||||
//role->rules
|
||||
ws.Route(ws.GET("/clusterroles/{name}/rules").To(clusterRoleRulesHandler).Filter(route.RouteLogging)).Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/roles/{name}/rules").To(roleRulesHandler).Filter(route.RouteLogging)).Produces(restful.MIME_JSON)
|
||||
//role->users
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/roles/{name}/users").To(roleUsersHandler).Filter(route.RouteLogging)).Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET("/clusterroles/{name}/users").To(clusterRoleUsersHandler).Filter(route.RouteLogging)).Produces(restful.MIME_JSON)
|
||||
}
|
||||
|
||||
// username -> roles
|
||||
func userRolesHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
username := req.PathParameter("username")
|
||||
|
||||
roles, err := iam.GetRoles("", username)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
clusterRoles, err := iam.GetClusterRoles(username)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
roleList := roleList{}
|
||||
roleList.Roles = roles
|
||||
roleList.ClusterRoles = clusterRoles
|
||||
|
||||
resp.WriteEntity(roleList)
|
||||
}
|
||||
|
||||
// namespaces + role name -> users
|
||||
func roleUsersHandler(req *restful.Request, resp *restful.Response) {
|
||||
name := req.PathParameter("name")
|
||||
namespace := req.PathParameter("namespace")
|
||||
|
||||
roleBindings, err := iam.GetRoleBindings(namespace, name)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
users := make([]string, 0)
|
||||
|
||||
for _, roleBinding := range roleBindings {
|
||||
for _, subject := range roleBinding.Subjects {
|
||||
if subject.Kind == v1.UserKind &&
|
||||
!strings.HasPrefix(subject.Name, "system") &&
|
||||
!slice.ContainsString(users, subject.Name, nil) {
|
||||
users = append(users, subject.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp.WriteEntity(users)
|
||||
}
|
||||
|
||||
// cluster role name -> users
|
||||
func clusterRoleUsersHandler(req *restful.Request, resp *restful.Response) {
|
||||
name := req.PathParameter("name")
|
||||
|
||||
roleBindings, err := iam.GetClusterRoleBindings(name)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
users := make([]string, 0)
|
||||
|
||||
for _, roleBinding := range roleBindings {
|
||||
for _, subject := range roleBinding.Subjects {
|
||||
if subject.Kind == v1.UserKind && !strings.HasPrefix(subject.Name, "system") &&
|
||||
!slice.ContainsString(users, subject.Name, nil) {
|
||||
users = append(users, subject.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp.WriteEntity(users)
|
||||
}
|
||||
|
||||
// username -> rules
|
||||
func usersRulesHandler(req *restful.Request, resp *restful.Response) {
|
||||
users := strings.Split(req.QueryParameter("users"), ",")
|
||||
|
||||
usersRules := make(map[string]userRuleList, 0)
|
||||
|
||||
for _, username := range users {
|
||||
_, contains := usersRules[username]
|
||||
if username != "" && !contains {
|
||||
|
||||
userRuleList := userRuleList{}
|
||||
|
||||
clusterRules, err := iam.GetUserClusterRules(username)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
rules, err := iam.GetUserRules(username)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
userRuleList.ClusterRules = clusterRules
|
||||
userRuleList.Rules = rules
|
||||
|
||||
usersRules[username] = userRuleList
|
||||
}
|
||||
}
|
||||
|
||||
resp.WriteEntity(usersRules)
|
||||
}
|
||||
|
||||
// username -> rules
|
||||
func userRulesHandler(req *restful.Request, resp *restful.Response) {
|
||||
username := req.PathParameter("username")
|
||||
|
||||
userRuleList := userRuleList{}
|
||||
|
||||
clusterRules, err := iam.GetUserClusterRules(username)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
rules, err := iam.GetUserRules(username)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
userRuleList.ClusterRules = clusterRules
|
||||
userRuleList.Rules = rules
|
||||
|
||||
resp.WriteEntity(userRuleList)
|
||||
}
|
||||
|
||||
// cluster role name -> rules
|
||||
func clusterRoleRulesHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
name := req.PathParameter("name")
|
||||
|
||||
var rules []iam.Rule
|
||||
|
||||
if name == "" {
|
||||
rules = iam.ClusterRoleRuleMapping
|
||||
} else {
|
||||
var err error
|
||||
rules, err = iam.GetClusterRoleRules(name)
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
resp.WriteEntity(rules)
|
||||
}
|
||||
|
||||
// role name -> rules
|
||||
func roleRulesHandler(req *restful.Request, resp *restful.Response) {
|
||||
name := req.PathParameter("name")
|
||||
namespace := req.PathParameter("namespace")
|
||||
|
||||
var rules []iam.Rule
|
||||
|
||||
if namespace == "" && name == "" {
|
||||
rules = iam.RoleRuleMapping
|
||||
} else {
|
||||
var err error
|
||||
rules, err = iam.GetRoleRules(namespace, name)
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
resp.WriteEntity(rules)
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package v1alpha
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/components"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/containers"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/daemonsets"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/deployments"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/hpa"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/iam"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/jobs"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/monitoring"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/nodes"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/pods"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/quota"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/registries"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/resources"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/routes"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/statefulsets"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/storage"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/terminal"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/users"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/volumes"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/workloadstatus"
|
||||
"kubesphere.io/kubesphere/pkg/apis/v1alpha/workspaces"
|
||||
_ "kubesphere.io/kubesphere/pkg/filter/container"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
ws := new(restful.WebService)
|
||||
ws.Path("/api/v1alpha1")
|
||||
|
||||
registries.Register(ws, "/registries")
|
||||
storage.Register(ws, "/storage")
|
||||
volumes.Register(ws, "/volumes")
|
||||
nodes.Register(ws, "/nodes")
|
||||
pods.Register(ws)
|
||||
containers.Register(ws)
|
||||
iam.Register(ws)
|
||||
components.Register(ws, "/components")
|
||||
routes.Register(ws)
|
||||
user.Register(ws, "/users/{user}")
|
||||
terminal.Register(ws, "/namespaces/{namespace}/pod/{pod}/shell/{container}")
|
||||
workloadstatus.Register(ws, "/status")
|
||||
quota.Register(ws, "/quota")
|
||||
hpa.Register(ws, "/namespaces/{namespace}/horizontalpodautoscalers/{horizontalpodautoscaler}")
|
||||
jobs.Register(ws, "/namespaces/{namespace}/jobs/{job}")
|
||||
deployments.Register(ws, "/namespaces/{namespace}/deployments/{deployment}/revisions/{revision}")
|
||||
daemonsets.Register(ws, "/namespaces/{namespace}/daemonsets/{daemonset}/revisions/{revision}")
|
||||
statefulsets.Register(ws, "/namespaces/{namespace}/statefulsets/{statefulset}/revisions/{revision}")
|
||||
resources.Register(ws, "/resources")
|
||||
monitoring.Register(ws, "/monitoring")
|
||||
workspaces.Register(ws, "/workspaces")
|
||||
// add webservice to default container
|
||||
restful.Add(ws)
|
||||
|
||||
// add websocket handler to default container
|
||||
terminal.RegisterWebSocketHandler(restful.DefaultContainer, "/api/v1alpha1/sockjs/")
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package jobs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
|
||||
"fmt"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models/controllers"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
tags := []string{"jobs"}
|
||||
|
||||
ws.Route(ws.POST(subPath).To(handleJob).Consumes("*/*").Metadata(restfulspec.KeyOpenAPITags, tags).Doc("Handle job" +
|
||||
" operation").Param(ws.PathParameter("job", "job name").DataType("string")).Param(ws.PathParameter("namespace",
|
||||
"job's namespace").DataType("string")).Param(ws.QueryParameter("a",
|
||||
"action").DataType("string")).Writes(""))
|
||||
}
|
||||
|
||||
func handleJob(req *restful.Request, resp *restful.Response) {
|
||||
var res interface{}
|
||||
var err error
|
||||
|
||||
job := req.PathParameter("job")
|
||||
namespace := req.PathParameter("namespace")
|
||||
action := req.QueryParameter("a")
|
||||
|
||||
switch action {
|
||||
case "rerun":
|
||||
res, err = controllers.JobReRun(namespace, job)
|
||||
default:
|
||||
resp.WriteHeaderAndEntity(http.StatusForbidden, constants.MessageResponse{Message: fmt.Sprintf("invalid operation %s", action)})
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(res)
|
||||
}
|
||||
@@ -1,443 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
package monitoring
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/client"
|
||||
"kubesphere.io/kubesphere/pkg/filter/route"
|
||||
"kubesphere.io/kubesphere/pkg/models/metrics"
|
||||
)
|
||||
|
||||
func (u Monitor) monitorPod(request *restful.Request, response *restful.Response) {
|
||||
requestParams := client.ParseMonitoringRequestParams(request)
|
||||
podName := requestParams.PodName
|
||||
metricName := requestParams.MetricsName
|
||||
if podName != "" {
|
||||
// single pod single metric
|
||||
queryType, params, nullRule := metrics.AssemblePodMetricRequestInfo(requestParams, metricName)
|
||||
var res *metrics.FormatedMetric
|
||||
if !nullRule {
|
||||
res = metrics.GetMetric(queryType, params, metricName)
|
||||
}
|
||||
response.WriteAsJson(res)
|
||||
|
||||
} else {
|
||||
// multiple
|
||||
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelPod)
|
||||
// sorting
|
||||
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelPodName)
|
||||
// paging
|
||||
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
|
||||
|
||||
response.WriteAsJson(pagedMetrics)
|
||||
}
|
||||
}
|
||||
|
||||
func (u Monitor) monitorContainer(request *restful.Request, response *restful.Response) {
|
||||
requestParams := client.ParseMonitoringRequestParams(request)
|
||||
metricName := requestParams.MetricsName
|
||||
if requestParams.MetricsFilter != "" {
|
||||
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelContainer)
|
||||
// sorting
|
||||
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelContainerName)
|
||||
// paging
|
||||
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
|
||||
|
||||
response.WriteAsJson(pagedMetrics)
|
||||
|
||||
} else {
|
||||
res := metrics.MonitorContainer(requestParams, metricName)
|
||||
response.WriteAsJson(res)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (u Monitor) monitorWorkload(request *restful.Request, response *restful.Response) {
|
||||
requestParams := client.ParseMonitoringRequestParams(request)
|
||||
|
||||
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkload)
|
||||
|
||||
var sortedMetrics *metrics.FormatedLevelMetric
|
||||
var maxMetricCount int
|
||||
|
||||
wlKind := requestParams.WorkloadKind
|
||||
|
||||
// sorting
|
||||
if wlKind == "" {
|
||||
|
||||
sortedMetrics, maxMetricCount = metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelWorkload)
|
||||
} else {
|
||||
|
||||
sortedMetrics, maxMetricCount = metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelPodName)
|
||||
}
|
||||
|
||||
// paging
|
||||
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
|
||||
|
||||
response.WriteAsJson(pagedMetrics)
|
||||
|
||||
}
|
||||
|
||||
func (u Monitor) monitorAllWorkspaces(request *restful.Request, response *restful.Response) {
|
||||
|
||||
requestParams := client.ParseMonitoringRequestParams(request)
|
||||
|
||||
tp := requestParams.Tp
|
||||
if tp == "_statistics" {
|
||||
// merge multiple metric: all-devops, all-roles, all-projects...this api is designed for admin
|
||||
res := metrics.MonitorAllWorkspacesStatistics()
|
||||
|
||||
response.WriteAsJson(res)
|
||||
|
||||
} else if tp == "rank" {
|
||||
rawMetrics := metrics.MonitorAllWorkspaces(requestParams)
|
||||
// sorting
|
||||
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelWorkspace)
|
||||
// paging
|
||||
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
|
||||
|
||||
response.WriteAsJson(pagedMetrics)
|
||||
} else {
|
||||
res := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkspace)
|
||||
response.WriteAsJson(res)
|
||||
}
|
||||
}
|
||||
|
||||
func (u Monitor) monitorOneWorkspace(request *restful.Request, response *restful.Response) {
|
||||
requestParams := client.ParseMonitoringRequestParams(request)
|
||||
|
||||
tp := requestParams.Tp
|
||||
if tp == "rank" {
|
||||
// multiple
|
||||
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkspace)
|
||||
// sorting
|
||||
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelNamespace)
|
||||
// paging
|
||||
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
|
||||
|
||||
response.WriteAsJson(pagedMetrics)
|
||||
|
||||
} else if tp == "_statistics" {
|
||||
wsName := requestParams.WsName
|
||||
|
||||
// merge multiple metric: devops, roles, projects...
|
||||
res := metrics.MonitorOneWorkspaceStatistics(wsName)
|
||||
response.WriteAsJson(res)
|
||||
} else {
|
||||
res := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkspace)
|
||||
response.WriteAsJson(res)
|
||||
}
|
||||
}
|
||||
|
||||
func (u Monitor) monitorNamespace(request *restful.Request, response *restful.Response) {
|
||||
requestParams := client.ParseMonitoringRequestParams(request)
|
||||
metricName := requestParams.MetricsName
|
||||
nsName := requestParams.NsName
|
||||
if nsName != "" {
|
||||
// single
|
||||
queryType, params := metrics.AssembleNamespaceMetricRequestInfo(requestParams, metricName)
|
||||
res := metrics.GetMetric(queryType, params, metricName)
|
||||
response.WriteAsJson(res)
|
||||
} else {
|
||||
// multiple
|
||||
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelNamespace)
|
||||
// sorting
|
||||
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelNamespace)
|
||||
// paging
|
||||
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
|
||||
|
||||
response.WriteAsJson(pagedMetrics)
|
||||
}
|
||||
}
|
||||
|
||||
func (u Monitor) monitorCluster(request *restful.Request, response *restful.Response) {
|
||||
requestParams := client.ParseMonitoringRequestParams(request)
|
||||
|
||||
metricName := requestParams.MetricsName
|
||||
if metricName != "" {
|
||||
// single
|
||||
queryType, params := metrics.AssembleClusterMetricRequestInfo(requestParams, metricName)
|
||||
res := metrics.GetMetric(queryType, params, metricName)
|
||||
|
||||
response.WriteAsJson(res)
|
||||
} else {
|
||||
// multiple
|
||||
res := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelCluster)
|
||||
response.WriteAsJson(res)
|
||||
}
|
||||
}
|
||||
|
||||
func (u Monitor) monitorNode(request *restful.Request, response *restful.Response) {
|
||||
requestParams := client.ParseMonitoringRequestParams(request)
|
||||
|
||||
metricName := requestParams.MetricsName
|
||||
if metricName != "" {
|
||||
// single
|
||||
queryType, params := metrics.AssembleNodeMetricRequestInfo(requestParams, metricName)
|
||||
res := metrics.GetMetric(queryType, params, metricName)
|
||||
nodeAddress := metrics.GetNodeAddressInfo()
|
||||
metrics.AddNodeAddressMetric(res, nodeAddress)
|
||||
response.WriteAsJson(res)
|
||||
} else {
|
||||
// multiple
|
||||
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelNode)
|
||||
nodeAddress := metrics.GetNodeAddressInfo()
|
||||
|
||||
for i := 0; i < len(rawMetrics.Results); i++ {
|
||||
metrics.AddNodeAddressMetric(&rawMetrics.Results[i], nodeAddress)
|
||||
}
|
||||
|
||||
// sorting
|
||||
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelNode)
|
||||
// paging
|
||||
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
|
||||
|
||||
response.WriteAsJson(pagedMetrics)
|
||||
}
|
||||
}
|
||||
|
||||
// k8s component(controller, scheduler, etcd) status
|
||||
func (u Monitor) monitorComponentStatus(request *restful.Request, response *restful.Response) {
|
||||
requestParams := client.ParseMonitoringRequestParams(request)
|
||||
|
||||
status := metrics.MonitorComponentStatus(requestParams)
|
||||
response.WriteAsJson(status)
|
||||
}
|
||||
|
||||
type Monitor struct {
|
||||
}
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
tags := []string{"monitoring apis"}
|
||||
u := Monitor{}
|
||||
|
||||
ws.Route(ws.GET(subPath+"/clusters").To(u.monitorCluster).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor cluster level metrics").
|
||||
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("cluster_cpu_utilisation")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/nodes").To(u.monitorNode).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor nodes level metrics").
|
||||
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("node_cpu_utilisation")).
|
||||
Param(ws.QueryParameter("nodes_filter", "node re2 expression filter").DataType("string").Required(false).DefaultValue("")).
|
||||
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
|
||||
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/nodes/{node_id}").To(u.monitorNode).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor specific node level metrics").
|
||||
Param(ws.PathParameter("node_id", "specific node").DataType("string").Required(true).DefaultValue("")).
|
||||
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("node_cpu_utilisation")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/namespaces").To(u.monitorNamespace).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor namespaces level metrics").
|
||||
Param(ws.QueryParameter("namespaces_filter", "namespaces re2 expression filter").DataType("string").Required(false).DefaultValue("")).
|
||||
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("namespace_memory_utilisation")).
|
||||
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
|
||||
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/namespaces/{ns_name}").To(u.monitorNamespace).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor specific namespace level metrics").
|
||||
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
|
||||
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("namespace_memory_utilisation")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/namespaces/{ns_name}/pods").To(u.monitorPod).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor pods level metrics").
|
||||
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
|
||||
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("pod_memory_utilisation_wo_cache")).
|
||||
Param(ws.QueryParameter("pods_filter", "pod re2 expression filter").DataType("string").Required(false).DefaultValue("")).
|
||||
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
|
||||
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/namespaces/{ns_name}/pods/{pod_name}").To(u.monitorPod).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor specific pod level metrics").
|
||||
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
|
||||
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true).DefaultValue("")).
|
||||
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("pod_memory_utilisation_wo_cache")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/nodes/{node_id}/pods").To(u.monitorPod).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor pods level metrics by nodeid").
|
||||
Param(ws.PathParameter("node_id", "specific node").DataType("string").Required(true).DefaultValue("i-k89a62il")).
|
||||
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("pod_memory_utilisation_wo_cache")).
|
||||
Param(ws.QueryParameter("pods_filter", "pod re2 expression filter").DataType("string").Required(false).DefaultValue("openpitrix.*")).
|
||||
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
|
||||
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/nodes/{node_id}/pods/{pod_name}").To(u.monitorPod).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor specific pod level metrics by nodeid").
|
||||
Param(ws.PathParameter("node_id", "specific node").DataType("string").Required(true).DefaultValue("i-k89a62il")).
|
||||
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true).DefaultValue("")).
|
||||
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("pod_memory_utilisation_wo_cache")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/nodes/{node_id}/pods/{pod_name}/containers").To(u.monitorContainer).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor specific pod level metrics by nodeid").
|
||||
Param(ws.PathParameter("node_id", "specific node").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true)).
|
||||
Param(ws.QueryParameter("containers_filter", "container re2 expression filter").DataType("string").Required(false).DefaultValue("")).
|
||||
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("pod_memory_utilisation_wo_cache")).
|
||||
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
|
||||
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
|
||||
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/namespaces/{ns_name}/pods/{pod_name}/containers").To(u.monitorContainer).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor containers level metrics").
|
||||
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
|
||||
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true).DefaultValue("")).
|
||||
Param(ws.QueryParameter("containers_filter", "container re2 expression filter").DataType("string").Required(false).DefaultValue("")).
|
||||
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("container_memory_utilisation_wo_cache")).
|
||||
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
|
||||
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
|
||||
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/namespaces/{ns_name}/pods/{pod_name}/containers/{container_name}").To(u.monitorContainer).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor specific container level metrics").
|
||||
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
|
||||
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true).DefaultValue("")).
|
||||
Param(ws.PathParameter("container_name", "specific container").DataType("string").Required(true).DefaultValue("")).
|
||||
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("container_memory_utilisation_wo_cache")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/namespaces/{ns_name}/workloads/{workload_kind}").To(u.monitorWorkload).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor specific workload level metrics").
|
||||
Param(ws.PathParameter("ns_name", "namespace").DataType("string").Required(true).DefaultValue("kube-system")).
|
||||
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
|
||||
Param(ws.PathParameter("workload_kind", "workload kind").DataType("string").Required(false).DefaultValue("daemonset")).
|
||||
Param(ws.QueryParameter("workload_name", "workload name").DataType("string").Required(true).DefaultValue("")).
|
||||
Param(ws.QueryParameter("pods_filter", "pod re2 expression filter").DataType("string").Required(false).DefaultValue("openpitrix.*")).
|
||||
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
|
||||
Param(ws.QueryParameter("limit", "max metric items in a page").DataType("string").Required(false).DefaultValue("4")).
|
||||
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/namespaces/{ns_name}/workloads").To(u.monitorWorkload).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor all workload level metrics").
|
||||
Param(ws.PathParameter("ns_name", "namespace").DataType("string").Required(true).DefaultValue("kube-system")).
|
||||
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("workloads_filter", "pod re2 expression filter").DataType("string").Required(false).DefaultValue("")).
|
||||
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
|
||||
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
|
||||
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
// list all namespace in this workspace by selected metrics
|
||||
ws.Route(ws.GET(subPath+"/workspaces/{workspace_name}").To(u.monitorOneWorkspace).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor workspaces level metrics").
|
||||
Param(ws.PathParameter("workspace_name", "workspace name").DataType("string").Required(true)).
|
||||
Param(ws.QueryParameter("namespaces_filter", "namespaces filter").DataType("string").Required(false).DefaultValue("k.*")).
|
||||
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("namespace_memory_utilisation_wo_cache")).
|
||||
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
|
||||
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
|
||||
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/workspaces").To(u.monitorAllWorkspaces).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor workspaces level metrics").
|
||||
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("workspace_memory_utilisation")).
|
||||
Param(ws.QueryParameter("workspaces_filter", "workspaces re2 expression filter").DataType("string").Required(false).DefaultValue(".*")).
|
||||
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
|
||||
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
|
||||
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
|
||||
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/components").To(u.monitorComponentStatus).
|
||||
Filter(route.RouteLogging).
|
||||
Doc("monitor k8s components status").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"net/http"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/filter/route"
|
||||
"kubesphere.io/kubesphere/pkg/models/metrics"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
ws.Route(ws.GET(subPath).To(handleNodes).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET(subPath+"/{nodename}").To(handleSingleNode).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.POST(subPath+"/{nodename}/drainage").To(handleDrainNode).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
}
|
||||
|
||||
func MakeRequest(node string, ch chan<- metrics.NodeMetrics) {
|
||||
resultNode := metrics.FormatNodeMetrics(node)
|
||||
|
||||
ch <- resultNode
|
||||
}
|
||||
|
||||
func handleNodes(request *restful.Request, response *restful.Response) {
|
||||
var result constants.PageableResponse
|
||||
|
||||
nodes := metrics.GetNodes()
|
||||
|
||||
ch := make(chan metrics.NodeMetrics)
|
||||
for _, node := range nodes {
|
||||
go MakeRequest(node, ch)
|
||||
}
|
||||
|
||||
for _, _ = range nodes {
|
||||
result.Items = append(result.Items, <-ch)
|
||||
}
|
||||
|
||||
if result.Items == nil {
|
||||
result.Items = make([]interface{}, 0)
|
||||
}
|
||||
|
||||
result.TotalCount = len(result.Items)
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
|
||||
func handleSingleNode(request *restful.Request, response *restful.Response) {
|
||||
nodeName := request.PathParameter("nodename")
|
||||
var resultNode metrics.NodeMetrics
|
||||
|
||||
resultNode = metrics.FormatNodeMetrics(nodeName)
|
||||
|
||||
response.WriteAsJson(resultNode)
|
||||
}
|
||||
|
||||
func handleDrainNode(request *restful.Request, response *restful.Response) {
|
||||
|
||||
nodeName := request.PathParameter("nodename")
|
||||
|
||||
result, err := metrics.DrainNode(nodeName)
|
||||
|
||||
if err != nil {
|
||||
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
|
||||
} else {
|
||||
|
||||
response.WriteAsJson(result)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package pods
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/filter/route"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models/metrics"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService) {
|
||||
|
||||
ws.Route(ws.GET("/pods").To(handleAllPods).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}").To(handlePodUnderNameSpace).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/deployments/{deployment}/pods").
|
||||
To(handleGetDeploymentPodsMetrics).
|
||||
Filter(route.RouteLogging).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON))
|
||||
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/daemonsets/{daemonset}/pods").
|
||||
To(handleGetDaemonsetPodsMetrics).
|
||||
Filter(route.RouteLogging).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON))
|
||||
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/statefulsets/{statefulset}/pods").
|
||||
To(handleGetStatefulsetPodsMetrics).
|
||||
Filter(route.RouteLogging).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON))
|
||||
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/pods").To(handlePodsUnderNameSpace).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET("/nodes/{node}/pods").To(handlePodsUnderNode).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET("/nodes/{node}/namespaces/{namespace}/pods").To(handlePodsUnderNodeAndNameSpace).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
}
|
||||
|
||||
// Get all pods metrics in cluster
|
||||
func handleAllPods(_ *restful.Request, response *restful.Response) {
|
||||
var result constants.PageableResponse
|
||||
result = metrics.GetAllPodMetrics()
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
|
||||
// Get pods metrics in namespace
|
||||
func handlePodsUnderNameSpace(request *restful.Request, response *restful.Response) {
|
||||
var result constants.PageableResponse
|
||||
labelSelector := request.QueryParameter("labelSelector")
|
||||
result = metrics.GetPodMetricsInNamespace(request.PathParameter("namespace"), labelSelector)
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
|
||||
// Get pods metrics in a deployment
|
||||
func handleGetDeploymentPodsMetrics(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
deployment := request.PathParameter("deployment")
|
||||
result := metrics.GetPodMetricsInDeployment(namespace, deployment)
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
|
||||
// Get pods metrics in daemonset deployment
|
||||
func handleGetDaemonsetPodsMetrics(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
daemonset := request.PathParameter("daemonset")
|
||||
result := metrics.GetPodMetricsInDaemonset(namespace, daemonset)
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
|
||||
// Get pods metrics in statefulset deployment
|
||||
func handleGetStatefulsetPodsMetrics(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
statefulset := request.PathParameter("statefulset")
|
||||
result := metrics.GetPodMetricsInStatefulSet(namespace, statefulset)
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
|
||||
// Get all pods metrics located in node
|
||||
func handlePodsUnderNode(request *restful.Request, response *restful.Response) {
|
||||
var result constants.PageableResponse
|
||||
result = metrics.GetPodMetricsInNode(request.PathParameter("node"))
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
|
||||
// Get a specific pod metrics
|
||||
func handlePodUnderNameSpace(request *restful.Request, response *restful.Response) {
|
||||
var resultPod metrics.PodMetrics
|
||||
resultPod = metrics.FormatPodMetrics(request.PathParameter("namespace"), request.PathParameter("pod"))
|
||||
response.WriteAsJson(resultPod)
|
||||
}
|
||||
|
||||
// Get pod metrics in a namespace located in deployment
|
||||
func handlePodsUnderNodeAndNameSpace(request *restful.Request, response *restful.Response) {
|
||||
var result constants.PageableResponse
|
||||
nodeName := request.PathParameter("node")
|
||||
namespace := request.PathParameter("namespace")
|
||||
result = metrics.GetPodMetricsInNamespaceOfNode(namespace, nodeName)
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package quota
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
tags := []string{"quota"}
|
||||
|
||||
ws.Route(ws.GET(subPath).To(getClusterQuota).Produces(restful.MIME_JSON).Doc("get whole "+
|
||||
"cluster's resource usage").Writes(models.ResourceQuota{}).Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
|
||||
ws.Route(ws.GET(subPath+"/namespaces/{namespace}").Doc("get specified namespace's resource "+
|
||||
"quota and usage").Param(ws.PathParameter("namespace",
|
||||
"namespace's name").DataType("string")).Writes(models.ResourceQuota{}).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).To(getNamespaceQuota).Produces(restful.MIME_JSON))
|
||||
|
||||
}
|
||||
|
||||
func getNamespaceQuota(req *restful.Request, resp *restful.Response) {
|
||||
namespace := req.PathParameter("namespace")
|
||||
quota, err := models.GetNamespaceQuota(namespace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
}
|
||||
|
||||
resp.WriteEntity(quota)
|
||||
}
|
||||
|
||||
func getClusterQuota(req *restful.Request, resp *restful.Response) {
|
||||
quota, err := models.GetClusterQuota()
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
}
|
||||
|
||||
resp.WriteEntity(quota)
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package registries
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/filter/route"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
ws.Route(ws.POST(subPath + "/validation").To(handlerRegistryValidation).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.POST(subPath).To(handleCreateRegistries).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.PUT(subPath + "/{name}").To(handleUpdateRegistries).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath + "/{project}").To(handleQueryRegistries).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath).To(handlerListRegistries).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.DELETE(subPath + "/{name}").To(handlerDeleteRegistries).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET(subPath + "/detail/{name}").To(handlerGetRegistries).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath + "/{name}/namespaces/{namespace}/searchwords/{searchWord}").
|
||||
Param(ws.PathParameter("namespace", "registry secret's namespace")).
|
||||
Param(ws.PathParameter("name", "registry secret's name")).
|
||||
Param(ws.PathParameter("searchWord", "keyword use to search image")).
|
||||
To(handlerImageSearch).
|
||||
Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON)
|
||||
ws.Route(ws.GET(subPath + "/{name}/namespaces/{namespace}/tags").
|
||||
Param(ws.QueryParameter("image", "imageName")).
|
||||
Param(ws.PathParameter("namespace", "registry secret's namespace")).
|
||||
Param(ws.PathParameter("name", "registry secret's name")).
|
||||
To(handlerGetImageTags).
|
||||
Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
}
|
||||
|
||||
func handlerRegistryValidation(request *restful.Request, response *restful.Response) {
|
||||
|
||||
authinfo := models.AuthInfo{}
|
||||
|
||||
err := request.ReadEntity(&authinfo)
|
||||
|
||||
if err != nil {
|
||||
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
|
||||
} else {
|
||||
|
||||
result := models.RegistryLoginAuth(authinfo)
|
||||
|
||||
response.WriteAsJson(result)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func handlerImageSearch(request *restful.Request, response *restful.Response) {
|
||||
|
||||
registry := request.PathParameter("name")
|
||||
searchWord := request.PathParameter("searchWord")
|
||||
namespace := request.PathParameter("namespace")
|
||||
|
||||
res := models.ImageSearch(namespace, registry, searchWord)
|
||||
|
||||
response.WriteEntity(res)
|
||||
|
||||
}
|
||||
|
||||
func handlerGetImageTags(request *restful.Request, response *restful.Response) {
|
||||
|
||||
registry := request.PathParameter("name")
|
||||
image := request.QueryParameter("image")
|
||||
namespace := request.PathParameter("namespace")
|
||||
|
||||
res := models.GetImageTags(namespace, registry, image)
|
||||
|
||||
response.WriteEntity(res)
|
||||
|
||||
}
|
||||
|
||||
func handleCreateRegistries(request *restful.Request, response *restful.Response) {
|
||||
|
||||
registries := models.Registries{}
|
||||
|
||||
err := request.ReadEntity(®istries)
|
||||
|
||||
if err != nil {
|
||||
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
|
||||
} else {
|
||||
|
||||
result, err := models.CreateRegistries(registries)
|
||||
|
||||
if err != nil {
|
||||
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
|
||||
} else {
|
||||
|
||||
response.WriteAsJson(result)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func handleQueryRegistries(request *restful.Request, response *restful.Response) {
|
||||
|
||||
project := request.PathParameter("project")
|
||||
result, err := models.QueryRegistries(project)
|
||||
|
||||
if err != nil {
|
||||
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
|
||||
} else {
|
||||
|
||||
response.WriteAsJson(result)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func handlerListRegistries(request *restful.Request, response *restful.Response) {
|
||||
|
||||
result, err := models.ListAllRegistries()
|
||||
if err != nil {
|
||||
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
|
||||
} else {
|
||||
|
||||
response.WriteAsJson(result)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func handlerDeleteRegistries(request *restful.Request, response *restful.Response) {
|
||||
|
||||
name := request.PathParameter("name")
|
||||
result, err := models.DeleteRegistries(name)
|
||||
|
||||
if err != nil {
|
||||
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
|
||||
} else {
|
||||
|
||||
response.WriteAsJson(result)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func handleUpdateRegistries(request *restful.Request, response *restful.Response) {
|
||||
|
||||
name := request.PathParameter("name")
|
||||
|
||||
registries := models.Registries{}
|
||||
|
||||
err := request.ReadEntity(®istries)
|
||||
|
||||
if err != nil {
|
||||
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
|
||||
} else {
|
||||
|
||||
result, err := models.UpdateRegistries(name, registries)
|
||||
|
||||
if err != nil {
|
||||
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
|
||||
} else {
|
||||
|
||||
response.WriteAsJson(result)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func handlerGetRegistries(request *restful.Request, response *restful.Response) {
|
||||
|
||||
name := request.PathParameter("name")
|
||||
result, err := models.GetReisgtries(name)
|
||||
|
||||
if err != nil {
|
||||
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
|
||||
} else {
|
||||
|
||||
response.WriteAsJson(result)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
tags := []string{"resources"}
|
||||
|
||||
ws.Route(ws.GET(subPath+"/{resource}").To(listResource).
|
||||
Produces(restful.MIME_JSON).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get resource list").
|
||||
Param(ws.PathParameter("resource", "resource name").DataType("string")).
|
||||
Param(ws.QueryParameter("conditions", "search conditions").DataType("string")).
|
||||
Param(ws.QueryParameter("reverse", "support reverse ordering").DataType("bool").DefaultValue("false")).
|
||||
Param(ws.QueryParameter("order", "the field for sorting").DataType("string")).
|
||||
Param(ws.QueryParameter("paging", "support paging function").DataType("string")).
|
||||
Writes(models.ResourceList{}))
|
||||
}
|
||||
|
||||
func isInvalid(str string) bool {
|
||||
invalidList := []string{"exec", "insert", "select", "delete", "update", "count", "*", "%", "truncate", "drop"}
|
||||
str = strings.Replace(str, "=", " ", -1)
|
||||
str = strings.Replace(str, ",", " ", -1)
|
||||
str = strings.Replace(str, "~", " ", -1)
|
||||
items := strings.Split(str, " ")
|
||||
|
||||
for _, invalid := range invalidList {
|
||||
for _, item := range items {
|
||||
if item == invalid || strings.ToLower(item) == invalid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func listResource(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
resource := req.PathParameter("resource")
|
||||
if resource == "applications" {
|
||||
handleApplication(req, resp)
|
||||
return
|
||||
}
|
||||
conditions := req.QueryParameter("conditions")
|
||||
paging := req.QueryParameter("paging")
|
||||
orderField := req.QueryParameter("order")
|
||||
reverse := req.QueryParameter("reverse")
|
||||
|
||||
if len(orderField) > 0 {
|
||||
if reverse == "true" {
|
||||
orderField = fmt.Sprintf("%s %s", orderField, "desc")
|
||||
} else {
|
||||
orderField = fmt.Sprintf("%s %s", orderField, "asc")
|
||||
}
|
||||
}
|
||||
|
||||
if isInvalid(conditions) || isInvalid(paging) || isInvalid(orderField) {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: "invalid input"})
|
||||
return
|
||||
}
|
||||
|
||||
res, err := models.ListResource(resource, conditions, paging, orderField)
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(res)
|
||||
}
|
||||
|
||||
func handleApplication(req *restful.Request, resp *restful.Response) {
|
||||
paging := req.QueryParameter("paging")
|
||||
clusterId := req.QueryParameter("cluster_id")
|
||||
runtimeId := req.QueryParameter("runtime_id")
|
||||
conditions := req.QueryParameter("conditions")
|
||||
if len(clusterId) > 0 {
|
||||
app, err := models.GetApplication(clusterId)
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
resp.WriteEntity(app)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := models.ListApplication(runtimeId, conditions, paging)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(res)
|
||||
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/filter/route"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService) {
|
||||
ws.Route(ws.GET("/routers").To(GetAllRouters).
|
||||
Doc("Get all routers").
|
||||
Filter(route.RouteLogging).
|
||||
Produces(restful.MIME_JSON))
|
||||
|
||||
ws.Route(ws.GET("/users/{user}/routers").To(GetAllRoutersOfUser).
|
||||
Doc("Get routers for user").
|
||||
Filter(route.RouteLogging).
|
||||
Produces(restful.MIME_JSON))
|
||||
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/router").To(GetRouter).
|
||||
Doc("Get router of a specified project").
|
||||
Param(ws.PathParameter("namespace", "name of the project").DataType("string")).
|
||||
Filter(route.RouteLogging).
|
||||
Produces(restful.MIME_JSON))
|
||||
|
||||
ws.Route(ws.DELETE("/namespaces/{namespace}/router").To(DeleteRouter).
|
||||
Doc("Get router of a specified project").
|
||||
Param(ws.PathParameter("namespace", "name of the project").DataType("string")).
|
||||
Filter(route.RouteLogging).
|
||||
Produces(restful.MIME_JSON))
|
||||
|
||||
ws.Route(ws.POST("/namespaces/{namespace}/router").To(CreateRouter).
|
||||
Doc("Create a router for a specified project").
|
||||
Param(ws.PathParameter("namespace", "name of the project").DataType("string")).
|
||||
Filter(route.RouteLogging).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON))
|
||||
|
||||
ws.Route(ws.PUT("/namespaces/{namespace}/router").To(UpdateRouter).
|
||||
Doc("Update a router for a specified project").
|
||||
Param(ws.PathParameter("namespace", "name of the project").DataType("string")).
|
||||
Filter(route.RouteLogging).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON))
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
RouterType string `json:"type"`
|
||||
Annotations map[string]string `json:"annotations"`
|
||||
}
|
||||
|
||||
// Get all namespace ingress controller services
|
||||
func GetAllRouters(request *restful.Request, response *restful.Response) {
|
||||
|
||||
routers, err := models.GetAllRouters()
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
} else {
|
||||
response.WriteAsJson(routers)
|
||||
}
|
||||
}
|
||||
|
||||
// Get all namespace ingress controller services for user
|
||||
func GetAllRoutersOfUser(request *restful.Request, response *restful.Response) {
|
||||
username := request.PathParameter("user")
|
||||
|
||||
routers, err := models.GetAllRoutersOfUser(username)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
} else {
|
||||
response.WriteAsJson(routers)
|
||||
}
|
||||
}
|
||||
|
||||
// Get ingress controller service for specified namespace
|
||||
func GetRouter(request *restful.Request, response *restful.Response) {
|
||||
|
||||
namespace := request.PathParameter("namespace")
|
||||
router, err := models.GetRouter(namespace)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
} else if router == nil {
|
||||
response.WriteHeaderAndEntity(http.StatusNotFound, constants.MessageResponse{Message: "Reseource Not Found"})
|
||||
} else {
|
||||
response.WriteAsJson(router)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Create ingress controller and related services
|
||||
func CreateRouter(request *restful.Request, response *restful.Response) {
|
||||
|
||||
namespace := request.PathParameter("namespace")
|
||||
|
||||
newRouter := Router{}
|
||||
err := request.ReadEntity(&newRouter)
|
||||
|
||||
if err != nil {
|
||||
response.WriteAsJson(err)
|
||||
return
|
||||
}
|
||||
|
||||
var router *v1.Service
|
||||
|
||||
serviceType, annotationMap, err := ParseParameter(newRouter)
|
||||
|
||||
if err != nil {
|
||||
glog.Error("Wrong annotations, missing key or value")
|
||||
response.WriteHeaderAndEntity(http.StatusBadRequest,
|
||||
constants.MessageResponse{Message: "Wrong annotations, missing key or value"})
|
||||
return
|
||||
}
|
||||
|
||||
router, err = models.CreateRouter(namespace, serviceType, annotationMap)
|
||||
|
||||
if err != nil {
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
} else {
|
||||
response.WriteAsJson(*router)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete ingress controller and services
|
||||
func DeleteRouter(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
|
||||
router, err := models.DeleteRouter(namespace)
|
||||
|
||||
if err != nil {
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
} else {
|
||||
response.WriteAsJson(router)
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateRouter(request *restful.Request, response *restful.Response) {
|
||||
|
||||
namespace := request.PathParameter("namespace")
|
||||
|
||||
newRouter := Router{}
|
||||
err := request.ReadEntity(&newRouter)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
serviceType, annotationMap, err := ParseParameter(newRouter)
|
||||
|
||||
router, err := models.UpdateRouter(namespace, serviceType, annotationMap)
|
||||
|
||||
if err != nil {
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
} else {
|
||||
response.WriteAsJson(router)
|
||||
}
|
||||
}
|
||||
|
||||
func ParseParameter(router Router) (routerType v1.ServiceType, annotationMap map[string]string, err error) {
|
||||
|
||||
routerType = v1.ServiceTypeNodePort
|
||||
|
||||
if strings.Compare(strings.ToLower(router.RouterType), "loadbalancer") == 0 {
|
||||
return v1.ServiceTypeLoadBalancer, router.Annotations, nil
|
||||
} else {
|
||||
return v1.ServiceTypeNodePort, make(map[string]string, 0), nil
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package statefulsets
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
"k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
tags := []string{"statefulsets"}
|
||||
|
||||
ws.Route(ws.GET(subPath).To(getDaemonSetRevision).Consumes("*/*").Metadata(restfulspec.KeyOpenAPITags, tags).Doc("Handle statefulset" +
|
||||
" operation").Param(ws.PathParameter("statefulset", "statefulset's name").DataType("string")).Param(ws.PathParameter("namespace",
|
||||
"statefulset's namespace").DataType("string")).Param(ws.PathParameter("revision", "statefulset's revision")).Writes(v1.StatefulSet{}))
|
||||
}
|
||||
|
||||
func getDaemonSetRevision(req *restful.Request, resp *restful.Response) {
|
||||
statefulset := req.PathParameter("statefulset")
|
||||
namespace := req.PathParameter("namespace")
|
||||
revision := req.PathParameter("revision")
|
||||
|
||||
res, err := models.GetStatefulSetRevision(namespace, statefulset, revision)
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
resp.WriteHeaderAndEntity(http.StatusNotFound, constants.MessageResponse{Message: err.Error()})
|
||||
} else {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
resp.WriteEntity(res)
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/filter/route"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
ws.Route(ws.GET(subPath+"/storageclasses/{storageclass}/persistentvolumeclaims").
|
||||
To(GetPvcListBySc).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/storageclasses/{storageclass}/metrics").
|
||||
To(GetScMetrics).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
ws.Route(ws.GET(subPath+"/storageclasses/metrics").
|
||||
To(GetScMetricsList).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
}
|
||||
|
||||
// List all PersistentVolumeClaims of a specific StorageClass
|
||||
// Extended API URL: "GET /api/v1alpha1/storage/storageclasses/{storageclass}/persistentvolumeclaims"
|
||||
func GetPvcListBySc(request *restful.Request, response *restful.Response) {
|
||||
scName := request.PathParameter("storageclass")
|
||||
claims, err := models.GetPvcListBySc(scName)
|
||||
if err != nil {
|
||||
response.WriteError(http.StatusInternalServerError, err)
|
||||
}
|
||||
result := models.PvcListBySc{scName, claims}
|
||||
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
|
||||
// Get StorageClass item
|
||||
// Extended API URL: "GET /api/v1alpha1/storage/storageclasses/{storageclass}/metrics"
|
||||
func GetScMetrics(request *restful.Request, response *restful.Response) {
|
||||
scName := request.PathParameter("storageclass")
|
||||
result, err := models.GetScItemMetrics(scName)
|
||||
if err != nil {
|
||||
response.WriteError(http.StatusInternalServerError, err)
|
||||
}
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
|
||||
// Get StorageClass item list
|
||||
// Extended API URL: "GET /api/v1alpha1/storage/storageclasses/metrics"
|
||||
func GetScMetricsList(request *restful.Request, response *restful.Response) {
|
||||
result, err := models.GetScItemMetricsList()
|
||||
if err != nil {
|
||||
response.WriteError(http.StatusInternalServerError, err)
|
||||
}
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright 2018 The Kubesphere 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.
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"net/http"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
ws.Route(ws.GET(subPath).To(handleExecShell))
|
||||
|
||||
}
|
||||
|
||||
func handleExecShell(req *restful.Request, resp *restful.Response) {
|
||||
res, err := models.HandleExecShell(req)
|
||||
if err != nil {
|
||||
resp.WriteError(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
resp.WriteEntity(res)
|
||||
|
||||
}
|
||||
|
||||
func RegisterWebSocketHandler(container *restful.Container, path string) {
|
||||
handler := models.CreateTerminalHandler(path[0 : len(path)-1])
|
||||
container.Handle(path, handler)
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
"kubesphere.io/kubesphere/pkg/models/kubectl"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
tags := []string{"users"}
|
||||
|
||||
ws.Route(ws.POST(subPath).Doc("create user").Param(ws.PathParameter("user",
|
||||
"the username to be created").DataType("string")).Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
To(createUser).Consumes("*/*").Produces(restful.MIME_JSON))
|
||||
ws.Route(ws.DELETE(subPath).Doc("delete user").Param(ws.PathParameter("user",
|
||||
"the username to be deleted").DataType("string")).Metadata(restfulspec.KeyOpenAPITags, tags).To(delUser).Produces(restful.MIME_JSON))
|
||||
ws.Route(ws.GET(subPath+"/kubectl").Doc("get user's kubectl pod").Param(ws.PathParameter("user",
|
||||
"username").DataType("string")).Metadata(restfulspec.KeyOpenAPITags, tags).To(getKubectl).Produces(restful.MIME_JSON))
|
||||
ws.Route(ws.GET(subPath+"/kubeconfig").Doc("get users' kubeconfig").Param(ws.PathParameter("user",
|
||||
"username").DataType("string")).Metadata(restfulspec.KeyOpenAPITags, tags).To(getKubeconfig).Produces(restful.MIME_JSON))
|
||||
|
||||
}
|
||||
|
||||
func createUser(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
user := req.PathParameter("user")
|
||||
|
||||
err := models.CreateKubeConfig(user)
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(constants.MessageResponse{Message: "successfully created"})
|
||||
}
|
||||
|
||||
func delUser(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
user := req.PathParameter("user")
|
||||
|
||||
err := kubectl.DelKubectlDeploy(user)
|
||||
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = models.DelKubeConfig(user)
|
||||
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(constants.MessageResponse{Message: "successfully deleted"})
|
||||
}
|
||||
|
||||
func getKubectl(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
user := req.PathParameter("user")
|
||||
|
||||
kubectlPod, err := kubectl.GetKubectlPod(user)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(kubectlPod)
|
||||
}
|
||||
|
||||
func getKubeconfig(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
user := req.PathParameter("user")
|
||||
|
||||
kubectlConfig, err := models.GetKubeConfig(user)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(kubectlConfig)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package volumes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/filter/route"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
ws.Route(ws.GET(subPath+"/namespaces/{namespace}/persistentvolumeclaims/{pvc}/pods").
|
||||
To(GetPodListByPvc).Filter(route.RouteLogging)).
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON)
|
||||
}
|
||||
|
||||
// List all pods of a specific PVC
|
||||
// Extended API URL: "GET /api/v1alpha1/volumes/namespaces/{namespace}/persistentvolumeclaims/{name}/pods"
|
||||
func GetPodListByPvc(request *restful.Request, response *restful.Response) {
|
||||
pvcName := request.PathParameter("pvc")
|
||||
nsName := request.PathParameter("namespace")
|
||||
pods, err := models.GetPodListByPvc(pvcName, nsName)
|
||||
if err != nil {
|
||||
response.WriteError(http.StatusInternalServerError, err)
|
||||
}
|
||||
result := models.PodListByPvc{Name: pvcName, Namespace: nsName, Pods: pods}
|
||||
response.WriteAsJson(result)
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package workloadstatus
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
tags := []string{"workloadStatus"}
|
||||
|
||||
ws.Route(ws.GET(subPath).Doc("get abnormal workloads' count of whole cluster").Metadata(restfulspec.KeyOpenAPITags, tags).To(getClusterStatus).Produces(restful.MIME_JSON))
|
||||
ws.Route(ws.GET(subPath+"/namespaces/{namespace}").Doc("get abnormal workloads' count of specified namespace").Param(ws.PathParameter("namespace",
|
||||
"the name of namespace").DataType("string")).Metadata(restfulspec.KeyOpenAPITags, tags).To(getNamespaceStatus).Produces(restful.MIME_JSON))
|
||||
|
||||
}
|
||||
|
||||
func getClusterStatus(req *restful.Request, resp *restful.Response) {
|
||||
res, err := models.GetClusterResourceStatus()
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
}
|
||||
resp.WriteEntity(res)
|
||||
}
|
||||
|
||||
func getNamespaceStatus(req *restful.Request, resp *restful.Response) {
|
||||
res, err := models.GetNamespacesResourceStatus(req.PathParameter("namespace"))
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
}
|
||||
resp.WriteEntity(res)
|
||||
}
|
||||
@@ -1,536 +0,0 @@
|
||||
package workspaces
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"k8s.io/api/core/v1"
|
||||
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
|
||||
"strconv"
|
||||
|
||||
"regexp"
|
||||
|
||||
"sort"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam"
|
||||
"kubesphere.io/kubesphere/pkg/models/metrics"
|
||||
"kubesphere.io/kubesphere/pkg/models/workspaces"
|
||||
)
|
||||
|
||||
const UserNameHeader = "X-Token-Username"
|
||||
|
||||
func Register(ws *restful.WebService, subPath string) {
|
||||
|
||||
ws.Route(ws.GET(subPath).To(UserWorkspaceListHandler))
|
||||
ws.Route(ws.POST(subPath).To(WorkspaceCreateHandler))
|
||||
ws.Route(ws.DELETE(subPath + "/{name}").To(DeleteWorkspaceHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}").To(WorkspaceDetailHandler))
|
||||
ws.Route(ws.PUT(subPath + "/{name}").To(WorkspaceEditHandler))
|
||||
ws.Route(ws.GET(subPath + "/{workspace}/namespaces").To(UserNamespaceListHandler))
|
||||
ws.Route(ws.GET(subPath + "/{workspace}/members/{username}/namespaces").To(UserNamespaceListHandler))
|
||||
ws.Route(ws.POST(subPath + "/{name}/namespaces").To(NamespaceCreateHandler))
|
||||
ws.Route(ws.DELETE(subPath + "/{name}/namespaces/{namespace}").To(NamespaceDeleteHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}/namespaces/{namespace}").To(NamespaceCheckHandler))
|
||||
ws.Route(ws.GET("/namespaces/{namespace}").To(NamespaceCheckHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}/devops").To(DevOpsProjectHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}/members/{username}/devops").To(DevOpsProjectHandler))
|
||||
ws.Route(ws.POST(subPath + "/{name}/devops").To(DevOpsProjectCreateHandler))
|
||||
ws.Route(ws.DELETE(subPath + "/{name}/devops/{id}").To(DevOpsProjectDeleteHandler))
|
||||
|
||||
ws.Route(ws.GET(subPath + "/{name}/members").To(MembersHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}/members/{member}").To(MemberHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}/roles").To(RolesHandler))
|
||||
ws.Route(ws.GET(subPath + "/{name}/roles/{role}").To(RoleHandler))
|
||||
ws.Route(ws.POST(subPath + "/{name}/members").To(MembersInviteHandler))
|
||||
ws.Route(ws.DELETE(subPath + "/{name}/members").To(MembersRemoveHandler))
|
||||
}
|
||||
|
||||
func RoleHandler(req *restful.Request, resp *restful.Response) {
|
||||
workspaceName := req.PathParameter("name")
|
||||
roleName := req.PathParameter("role")
|
||||
|
||||
if !slice.ContainsString(constants.WorkSpaceRoles, roleName, nil) {
|
||||
resp.WriteHeaderAndEntity(http.StatusNotFound, constants.MessageResponse{Message: fmt.Sprintf("role %s not found", roleName)})
|
||||
return
|
||||
}
|
||||
|
||||
role, rules, err := iam.WorkspaceRoleRules(workspaceName, roleName)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
users, err := iam.WorkspaceRoleUsers(workspaceName, roleName)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(map[string]interface{}{"role": role, "rules": rules, "users": users})
|
||||
}
|
||||
|
||||
func RolesHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
name := req.PathParameter("name")
|
||||
|
||||
workspace, err := workspaces.Detail(name)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
roles, err := workspaces.Roles(workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(roles)
|
||||
}
|
||||
|
||||
func MembersHandler(req *restful.Request, resp *restful.Response) {
|
||||
workspace := req.PathParameter("name")
|
||||
keyword := req.QueryParameter("keyword")
|
||||
|
||||
users, err := workspaces.GetWorkspaceMembers(workspace, keyword)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(users)
|
||||
}
|
||||
|
||||
func MemberHandler(req *restful.Request, resp *restful.Response) {
|
||||
workspace := req.PathParameter("name")
|
||||
username := req.PathParameter("member")
|
||||
|
||||
user, err := iam.GetUser(username)
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
namespaces, err := workspaces.Namespaces(workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
user.WorkspaceRole = user.WorkspaceRoles[workspace]
|
||||
|
||||
roles := make(map[string]string)
|
||||
|
||||
for _, namespace := range namespaces {
|
||||
if role := user.Roles[namespace.Name]; role != "" {
|
||||
roles[namespace.Name] = role
|
||||
}
|
||||
}
|
||||
|
||||
user.Roles = roles
|
||||
user.Rules = nil
|
||||
user.WorkspaceRules = nil
|
||||
user.WorkspaceRoles = nil
|
||||
user.ClusterRules = nil
|
||||
resp.WriteEntity(user)
|
||||
}
|
||||
|
||||
func MembersInviteHandler(req *restful.Request, resp *restful.Response) {
|
||||
var users []workspaces.UserInvite
|
||||
workspace := req.PathParameter("name")
|
||||
err := req.ReadEntity(&users)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = workspaces.Invite(workspace, users)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeaderAndEntity(http.StatusOK, constants.MessageResponse{Message: "success"})
|
||||
}
|
||||
|
||||
func MembersRemoveHandler(req *restful.Request, resp *restful.Response) {
|
||||
query := req.QueryParameter("name")
|
||||
workspace := req.PathParameter("name")
|
||||
|
||||
names := strings.Split(query, ",")
|
||||
|
||||
err := workspaces.RemoveMembers(workspace, names)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeaderAndEntity(http.StatusOK, constants.MessageResponse{Message: "success"})
|
||||
}
|
||||
|
||||
func NamespaceCheckHandler(req *restful.Request, resp *restful.Response) {
|
||||
namespace := req.PathParameter("namespace")
|
||||
|
||||
exist, err := workspaces.NamespaceExistCheck(namespace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(map[string]bool{"exist": exist})
|
||||
}
|
||||
|
||||
func NamespaceDeleteHandler(req *restful.Request, resp *restful.Response) {
|
||||
namespace := req.PathParameter("namespace")
|
||||
workspace := req.PathParameter("name")
|
||||
|
||||
err := workspaces.DeleteNamespace(workspace, namespace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeaderAndEntity(http.StatusOK, constants.MessageResponse{Message: "success"})
|
||||
}
|
||||
|
||||
func DevOpsProjectDeleteHandler(req *restful.Request, resp *restful.Response) {
|
||||
devops := req.PathParameter("id")
|
||||
workspace := req.PathParameter("name")
|
||||
force := req.QueryParameter("force")
|
||||
username := req.HeaderParameter(UserNameHeader)
|
||||
|
||||
err := workspaces.UnBindDevopsProject(workspace, devops)
|
||||
|
||||
if err != nil && force != "true" {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = workspaces.DeleteDevopsProject(username, devops)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(constants.MessageResponse{Message: "success"})
|
||||
}
|
||||
|
||||
func DevOpsProjectCreateHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
workspace := req.PathParameter("name")
|
||||
username := req.HeaderParameter(UserNameHeader)
|
||||
|
||||
var devops workspaces.DevopsProject
|
||||
|
||||
err := req.ReadEntity(&devops)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
project, err := workspaces.CreateDevopsProject(username, workspace, devops)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(project)
|
||||
|
||||
}
|
||||
|
||||
func NamespaceCreateHandler(req *restful.Request, resp *restful.Response) {
|
||||
workspace := req.PathParameter("name")
|
||||
username := req.HeaderParameter(UserNameHeader)
|
||||
|
||||
namespace := &v1.Namespace{}
|
||||
|
||||
err := req.ReadEntity(namespace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if namespace.Annotations == nil {
|
||||
namespace.Annotations = make(map[string]string, 0)
|
||||
}
|
||||
|
||||
namespace.Annotations["creator"] = username
|
||||
namespace.Annotations["workspace"] = workspace
|
||||
|
||||
if namespace.Labels == nil {
|
||||
namespace.Labels = make(map[string]string, 0)
|
||||
}
|
||||
|
||||
namespace.Labels["kubesphere.io/workspace"] = workspace
|
||||
|
||||
namespace, err = workspaces.CreateNamespace(namespace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(namespace)
|
||||
}
|
||||
|
||||
func DevOpsProjectHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
workspace := req.PathParameter("name")
|
||||
username := req.PathParameter("username")
|
||||
keyword := req.QueryParameter("keyword")
|
||||
|
||||
if username == "" {
|
||||
username = req.HeaderParameter(UserNameHeader)
|
||||
}
|
||||
|
||||
limit := 65535
|
||||
offset := 0
|
||||
orderBy := "createTime"
|
||||
reverse := true
|
||||
|
||||
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(req.QueryParameter("paging")); len(groups) == 3 {
|
||||
limit, _ = strconv.Atoi(groups[1])
|
||||
page, _ := strconv.Atoi(groups[2])
|
||||
offset = (page - 1) * limit
|
||||
}
|
||||
|
||||
if groups := regexp.MustCompile(`^(createTime|name)$`).FindStringSubmatch(req.QueryParameter("order")); len(groups) == 2 {
|
||||
orderBy = groups[1]
|
||||
reverse = false
|
||||
}
|
||||
|
||||
if q := req.QueryParameter("reverse"); q != "" {
|
||||
b, err := strconv.ParseBool(q)
|
||||
if err == nil {
|
||||
reverse = b
|
||||
}
|
||||
}
|
||||
|
||||
total, devOpsProjects, err := workspaces.ListDevopsProjectsByUser(username, workspace, keyword, orderBy, reverse, limit, offset)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
result := constants.PageableResponse{}
|
||||
result.TotalCount = total
|
||||
result.Items = make([]interface{}, 0)
|
||||
for _, n := range devOpsProjects {
|
||||
result.Items = append(result.Items, n)
|
||||
}
|
||||
resp.WriteEntity(result)
|
||||
}
|
||||
|
||||
func WorkspaceCreateHandler(req *restful.Request, resp *restful.Response) {
|
||||
var workspace workspaces.Workspace
|
||||
username := req.HeaderParameter(UserNameHeader)
|
||||
err := req.ReadEntity(&workspace)
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
if workspace.Name == "" || strings.Contains(workspace.Name, ":") {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: "invalid workspace name"})
|
||||
return
|
||||
}
|
||||
|
||||
workspace.Path = workspace.Name
|
||||
workspace.Members = nil
|
||||
|
||||
if workspace.Admin != "" {
|
||||
workspace.Creator = workspace.Admin
|
||||
} else {
|
||||
workspace.Creator = username
|
||||
}
|
||||
|
||||
created, err := workspaces.Create(&workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(created)
|
||||
|
||||
}
|
||||
|
||||
func DeleteWorkspaceHandler(req *restful.Request, resp *restful.Response) {
|
||||
name := req.PathParameter("name")
|
||||
|
||||
if name == "" || strings.Contains(name, ":") {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: "invalid workspace name"})
|
||||
return
|
||||
}
|
||||
|
||||
workspace, err := workspaces.Detail(name)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = workspaces.Delete(workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(constants.MessageResponse{Message: "success"})
|
||||
}
|
||||
func WorkspaceEditHandler(req *restful.Request, resp *restful.Response) {
|
||||
var workspace workspaces.Workspace
|
||||
name := req.PathParameter("name")
|
||||
err := req.ReadEntity(&workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if name != workspace.Name {
|
||||
resp.WriteError(http.StatusBadRequest, fmt.Errorf("the name of workspace (%s) does not match the name on the URL (%s)", workspace.Name, name))
|
||||
return
|
||||
}
|
||||
|
||||
if workspace.Name == "" || strings.Contains(workspace.Name, ":") {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: "invalid workspace name"})
|
||||
return
|
||||
}
|
||||
|
||||
workspace.Path = workspace.Name
|
||||
|
||||
workspace.Members = nil
|
||||
|
||||
edited, err := workspaces.Edit(&workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(edited)
|
||||
}
|
||||
func WorkspaceDetailHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
name := req.PathParameter("name")
|
||||
|
||||
workspace, err := workspaces.Detail(name)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(workspace)
|
||||
}
|
||||
|
||||
// List all workspaces for the current user
|
||||
func UserWorkspaceListHandler(req *restful.Request, resp *restful.Response) {
|
||||
keyword := req.QueryParameter("keyword")
|
||||
username := req.HeaderParameter(UserNameHeader)
|
||||
|
||||
ws, err := workspaces.ListWorkspaceByUser(username, keyword)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
sort.Slice(ws, func(i, j int) bool {
|
||||
t1, err := ws[i].GetCreateTime()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
t2, err := ws[j].GetCreateTime()
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return t1.After(t2)
|
||||
})
|
||||
|
||||
resp.WriteEntity(ws)
|
||||
}
|
||||
|
||||
func UserNamespaceListHandler(req *restful.Request, resp *restful.Response) {
|
||||
withMetrics, err := strconv.ParseBool(req.QueryParameter("metrics"))
|
||||
|
||||
if err != nil {
|
||||
withMetrics = false
|
||||
}
|
||||
|
||||
username := req.PathParameter("username")
|
||||
keyword := req.QueryParameter("keyword")
|
||||
if username == "" {
|
||||
username = req.HeaderParameter(UserNameHeader)
|
||||
}
|
||||
limit := 65535
|
||||
offset := 0
|
||||
orderBy := "createTime"
|
||||
reverse := true
|
||||
|
||||
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(req.QueryParameter("paging")); len(groups) == 3 {
|
||||
limit, _ = strconv.Atoi(groups[1])
|
||||
page, _ := strconv.Atoi(groups[2])
|
||||
if page < 0 {
|
||||
page = 1
|
||||
}
|
||||
offset = (page - 1) * limit
|
||||
}
|
||||
|
||||
if groups := regexp.MustCompile(`^(createTime|name)$`).FindStringSubmatch(req.QueryParameter("order")); len(groups) == 2 {
|
||||
orderBy = groups[1]
|
||||
reverse = false
|
||||
}
|
||||
|
||||
if q := req.QueryParameter("reverse"); q != "" {
|
||||
b, err := strconv.ParseBool(q)
|
||||
if err == nil {
|
||||
reverse = b
|
||||
}
|
||||
}
|
||||
|
||||
workspaceName := req.PathParameter("workspace")
|
||||
|
||||
total, namespaces, err := workspaces.ListNamespaceByUser(workspaceName, username, keyword, orderBy, reverse, limit, offset)
|
||||
|
||||
if withMetrics {
|
||||
namespaces = metrics.GetNamespacesWithMetrics(namespaces)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
result := constants.PageableResponse{}
|
||||
result.TotalCount = total
|
||||
result.Items = make([]interface{}, 0)
|
||||
for _, n := range namespaces {
|
||||
result.Items = append(result.Items, n)
|
||||
}
|
||||
|
||||
resp.WriteEntity(result)
|
||||
}
|
||||
189
pkg/app/app.go
189
pkg/app/app.go
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/golang/glog"
|
||||
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
"github.com/go-openapi/spec"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
|
||||
_ "kubesphere.io/kubesphere/pkg/apis/v1alpha"
|
||||
"kubesphere.io/kubesphere/pkg/client"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
"kubesphere.io/kubesphere/pkg/models/controllers"
|
||||
"kubesphere.io/kubesphere/pkg/models/kubectl"
|
||||
"kubesphere.io/kubesphere/pkg/models/workspaces"
|
||||
"kubesphere.io/kubesphere/pkg/options"
|
||||
)
|
||||
|
||||
type kubeSphereServer struct {
|
||||
insecureBindAddress net.IP
|
||||
bindAddress net.IP
|
||||
insecurePort int
|
||||
port int
|
||||
certFile string
|
||||
keyFile string
|
||||
container *restful.Container
|
||||
}
|
||||
|
||||
func newKubeSphereServer(options *options.ServerRunOptions) *kubeSphereServer {
|
||||
|
||||
s := kubeSphereServer{
|
||||
insecureBindAddress: options.GetInsecureBindAddress(),
|
||||
bindAddress: options.GetBindAddress(),
|
||||
insecurePort: options.GetInsecurePort(),
|
||||
port: options.GetPort(),
|
||||
certFile: options.GetCertFile(),
|
||||
keyFile: options.GetKeyFile(),
|
||||
}
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
func preCheck() error {
|
||||
k8sClient := client.NewK8sClient()
|
||||
_, err := k8sClient.CoreV1().Namespaces().Get(constants.KubeSphereControlNamespace, metaV1.GetOptions{})
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
_, err = k8sClient.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metaV1.ObjectMeta{Name: constants.KubeSphereControlNamespace}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = k8sClient.AppsV1().Deployments(constants.KubeSphereControlNamespace).Get(constants.AdminUserName, metaV1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
if err = models.CreateKubeConfig(constants.AdminUserName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = kubectl.CreateKubectlDeploy(constants.AdminUserName); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
db := client.NewSharedDBClient()
|
||||
defer db.Close()
|
||||
|
||||
if !db.HasTable(&workspaces.WorkspaceDPBinding{}) {
|
||||
if err := db.CreateTable(&workspaces.WorkspaceDPBinding{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerSwagger() {
|
||||
config := restfulspec.Config{
|
||||
WebServices: restful.RegisteredWebServices(), // you control what services are visible
|
||||
APIPath: "/swagger-ui/api.json",
|
||||
PostBuildSwaggerObjectHandler: enrichSwaggerObject}
|
||||
restful.DefaultContainer.Add(restfulspec.NewOpenAPIService(config))
|
||||
http.Handle("/swagger-ui/", http.StripPrefix("/swagger-ui/", http.FileServer(http.Dir("/usr/lib/kubesphere/swagger-ui"))))
|
||||
}
|
||||
|
||||
func enrichSwaggerObject(swo *spec.Swagger) {
|
||||
swo.Info = &spec.Info{
|
||||
InfoProps: spec.InfoProps{
|
||||
Title: "KubeSphere",
|
||||
Description: "The extend apis of kubesphere",
|
||||
Version: "v1.0-alpha",
|
||||
},
|
||||
}
|
||||
swo.Tags = []spec.Tag{spec.Tag{TagProps: spec.TagProps{
|
||||
Name: "extend apis"}}}
|
||||
}
|
||||
|
||||
func (server *kubeSphereServer) run() {
|
||||
err := preCheck()
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
stopChan := make(chan struct{})
|
||||
wg.Add(1)
|
||||
go controllers.Run(stopChan, &wg)
|
||||
|
||||
registerSwagger()
|
||||
|
||||
if len(server.certFile) > 0 && len(server.keyFile) > 0 {
|
||||
servingCert, err := tls.LoadX509KeyPair(server.certFile, server.keyFile)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
secureAddr := fmt.Sprintf("%s:%d", server.bindAddress, server.port)
|
||||
glog.Infof("Serving securely on addr: %s", secureAddr)
|
||||
|
||||
httpServer := &http.Server{
|
||||
Addr: secureAddr,
|
||||
Handler: restful.DefaultContainer,
|
||||
TLSConfig: &tls.Config{Certificates: []tls.Certificate{servingCert}},
|
||||
}
|
||||
|
||||
go func() { glog.Fatal(httpServer.ListenAndServeTLS("", "")) }()
|
||||
|
||||
} else {
|
||||
insecureAddr := fmt.Sprintf("%s:%d", server.insecureBindAddress, server.insecurePort)
|
||||
glog.Infof("Serving insecurely on addr: %s", insecureAddr)
|
||||
|
||||
go func() { glog.Fatal(http.ListenAndServe(insecureAddr, nil)) }()
|
||||
}
|
||||
|
||||
sigs := make(chan os.Signal)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
<-sigs
|
||||
close(stopChan)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func Run() {
|
||||
server := newKubeSphereServer(options.ServerOptions)
|
||||
|
||||
server.run()
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/golang/glog"
|
||||
"github.com/jinzhu/gorm"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/logs"
|
||||
"kubesphere.io/kubesphere/pkg/options"
|
||||
)
|
||||
|
||||
var dbClient *gorm.DB
|
||||
|
||||
const database = "kubesphere"
|
||||
|
||||
func NewDBClient() *gorm.DB {
|
||||
user := options.ServerOptions.GetMysqlUser()
|
||||
passwd := options.ServerOptions.GetMysqlPassword()
|
||||
addr := options.ServerOptions.GetMysqlAddr()
|
||||
conn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", user, passwd, addr, database)
|
||||
|
||||
db, err := gorm.Open("mysql", conn)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
db.SetLogger(log.New(logs.GlogWriter{}, " ", 0))
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func NewSharedDBClient() *gorm.DB {
|
||||
|
||||
if dbClient != nil {
|
||||
err := dbClient.DB().Ping()
|
||||
if err == nil {
|
||||
return dbClient
|
||||
} else {
|
||||
glog.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return NewDBClient()
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/options"
|
||||
)
|
||||
|
||||
type EtcdClient struct {
|
||||
cli *clientv3.Client
|
||||
}
|
||||
|
||||
func (cli EtcdClient) Put(key, value string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(10)*time.Second)
|
||||
_, err := cli.cli.Put(ctx, key, value)
|
||||
cancel()
|
||||
if err != nil {
|
||||
switch err {
|
||||
case context.Canceled:
|
||||
glog.Errorf("ctx is canceled by another routine: %v\n", err)
|
||||
case context.DeadlineExceeded:
|
||||
glog.Errorf("ctx is attached with a deadline is exceeded: %v\n", err)
|
||||
case rpctypes.ErrEmptyKey:
|
||||
glog.Errorf("client-side error: %v\n", err)
|
||||
default:
|
||||
glog.Errorf("bad cluster endpoints, which are not etcd servers: %v\n", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli EtcdClient) Get(key string) ([]byte, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(10)*time.Second)
|
||||
resp, err := cli.cli.Get(ctx, key)
|
||||
cancel()
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(resp.Kvs) == 0 {
|
||||
return nil, fmt.Errorf("empty value of key: %s", key)
|
||||
}
|
||||
return resp.Kvs[0].Value, nil
|
||||
}
|
||||
|
||||
func (cli EtcdClient) Close() {
|
||||
|
||||
cli.cli.Close()
|
||||
}
|
||||
|
||||
func newEtcdClientWithHttps(certFile, keyFile, caFile string, endpoints []string, dialTimeout int) (*clientv3.Client, error) {
|
||||
tlsInfo := transport.TLSInfo{
|
||||
CertFile: certFile,
|
||||
KeyFile: keyFile,
|
||||
TrustedCAFile: caFile,
|
||||
}
|
||||
tlsConfig, err := tlsInfo.ClientConfig()
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cli, err := clientv3.New(clientv3.Config{
|
||||
Endpoints: endpoints,
|
||||
DialTimeout: time.Duration(dialTimeout) * time.Second,
|
||||
TLS: tlsConfig,
|
||||
})
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
return cli, nil // make sure to close the client
|
||||
}
|
||||
|
||||
func newEtcdClient(endpoints []string, dialTimeout int) (*clientv3.Client, error) {
|
||||
cli, err := clientv3.New(clientv3.Config{
|
||||
Endpoints: endpoints,
|
||||
DialTimeout: time.Duration(dialTimeout) * time.Second,
|
||||
})
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
return cli, nil // make sure to close the client
|
||||
}
|
||||
|
||||
func NewEtcdClient() (*EtcdClient, error) {
|
||||
var cli *clientv3.Client
|
||||
var err error
|
||||
cert := options.ServerOptions.GetEtcdCertFile()
|
||||
key := options.ServerOptions.GetEtcdKeyFile()
|
||||
ca := options.ServerOptions.GetEtcdCaFile()
|
||||
endpoints := options.ServerOptions.GetEtcdEndPoints()
|
||||
if len(cert) > 0 && len(key) > 0 && len(ca) > 0 {
|
||||
cli, err = newEtcdClientWithHttps(cert, key, ca, endpoints, 20)
|
||||
} else {
|
||||
cli, err = newEtcdClient(endpoints, 20)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EtcdClient{cli}, nil
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/antonholmquist/jason"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultHeapsterScheme = "http"
|
||||
DefaultHeapsterService = "heapster.kube-system.svc.cluster.local" //"heapster"
|
||||
DefaultHeapsterPort = "80" // use the first exposed port on the service
|
||||
HeapsterApiPath = "/api/v1/model"
|
||||
HeapsterEndpointUrl = DefaultHeapsterScheme + "://" + DefaultHeapsterService + ":" + DefaultHeapsterPort + HeapsterApiPath
|
||||
)
|
||||
|
||||
var httpClient = &http.Client{Timeout: 30 * time.Second}
|
||||
|
||||
// Get heapster response in python-like dictionary
|
||||
func GetHeapsterMetricsJson(url string) *jason.Object {
|
||||
|
||||
response, err := httpClient.Get(HeapsterEndpointUrl + url)
|
||||
|
||||
var data *jason.Object
|
||||
if err != nil {
|
||||
glog.Error(url, err)
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
data, err = jason.NewObjectFromReader(response.Body)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(url, err)
|
||||
}
|
||||
}
|
||||
|
||||
// return empty json in case of error response from es-node
|
||||
if data == nil {
|
||||
emptyJSON := `{}`
|
||||
data, _ = jason.NewObjectFromBytes([]byte(emptyJSON))
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func GetHeapsterMetrics(url string) string {
|
||||
response, err := httpClient.Get(HeapsterEndpointUrl + url)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
contents, err := ioutil.ReadAll(response.Body)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
||||
return string(contents)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
func GetCAdvisorMetrics(nodeAddr string) string {
|
||||
|
||||
response, err := httpClient.Get("http://" + nodeAddr + ":10255/stats/summary")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
contents, err := ioutil.ReadAll(response.Body)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
||||
return string(contents)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/options"
|
||||
)
|
||||
|
||||
var k8sClient *kubernetes.Clientset
|
||||
|
||||
func NewK8sClient() *kubernetes.Clientset {
|
||||
if k8sClient != nil {
|
||||
return k8sClient
|
||||
}
|
||||
|
||||
kubeConfig, err := options.ServerOptions.GetKubeConfig()
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
k8sClient, err = kubernetes.NewForConfig(kubeConfig)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
return k8sClient
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
package client
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"os"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultScheme = "http"
|
||||
DefaultPrometheusPort = "9090"
|
||||
PrometheusApiPath = "/api/v1/"
|
||||
DefaultQueryStep = "10m"
|
||||
DefaultQueryTimeout = "10s"
|
||||
RangeQueryType = "query_range?"
|
||||
DefaultQueryType = "query?"
|
||||
PrometheusAPIServerEnv = "PROMETHEUS_API_SERVER"
|
||||
)
|
||||
|
||||
var PrometheusAPIServer = "prometheus-k8s.kubesphere-monitoring-system.svc"
|
||||
var PrometheusEndpointUrl string
|
||||
|
||||
func init() {
|
||||
if env := os.Getenv(PrometheusAPIServerEnv); env != "" {
|
||||
PrometheusAPIServer = env
|
||||
}
|
||||
PrometheusEndpointUrl = DefaultScheme + "://" + PrometheusAPIServer + ":" + DefaultPrometheusPort + PrometheusApiPath
|
||||
}
|
||||
|
||||
type MonitoringRequestParams struct {
|
||||
Params url.Values
|
||||
QueryType string
|
||||
SortMetricName string
|
||||
SortType string
|
||||
PageNum string
|
||||
LimitNum string
|
||||
Tp string
|
||||
MetricsFilter string
|
||||
NodesFilter string
|
||||
WsFilter string
|
||||
NsFilter string
|
||||
PodsFilter string
|
||||
ContainersFilter string
|
||||
MetricsName string
|
||||
WorkloadName string
|
||||
WlFilter string
|
||||
NodeId string
|
||||
WsName string
|
||||
NsName string
|
||||
PodName string
|
||||
ContainerName string
|
||||
WorkloadKind string
|
||||
}
|
||||
|
||||
var client = &http.Client{}
|
||||
|
||||
func SendMonitoringRequest(queryType string, params string) string {
|
||||
epurl := PrometheusEndpointUrl + queryType + params
|
||||
response, err := client.Get(epurl)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
contents, err := ioutil.ReadAll(response.Body)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
return string(contents)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ParseMonitoringRequestParams(request *restful.Request) *MonitoringRequestParams {
|
||||
instantTime := strings.Trim(request.QueryParameter("time"), " ")
|
||||
start := strings.Trim(request.QueryParameter("start"), " ")
|
||||
end := strings.Trim(request.QueryParameter("end"), " ")
|
||||
step := strings.Trim(request.QueryParameter("step"), " ")
|
||||
timeout := strings.Trim(request.QueryParameter("timeout"), " ")
|
||||
|
||||
sortMetricName := strings.Trim(request.QueryParameter("sort_metric"), " ")
|
||||
sortType := strings.Trim(request.QueryParameter("sort_type"), " ")
|
||||
pageNum := strings.Trim(request.QueryParameter("page"), " ")
|
||||
limitNum := strings.Trim(request.QueryParameter("limit"), " ")
|
||||
tp := strings.Trim(request.QueryParameter("type"), " ")
|
||||
|
||||
metricsFilter := strings.Trim(request.QueryParameter("metrics_filter"), " ")
|
||||
nodesFilter := strings.Trim(request.QueryParameter("nodes_filter"), " ")
|
||||
wsFilter := strings.Trim(request.QueryParameter("workspaces_filter"), " ")
|
||||
nsFilter := strings.Trim(request.QueryParameter("namespaces_filter"), " ")
|
||||
wlFilter := strings.Trim(request.QueryParameter("workloads_filter"), " ")
|
||||
podsFilter := strings.Trim(request.QueryParameter("pods_filter"), " ")
|
||||
containersFilter := strings.Trim(request.QueryParameter("containers_filter"), " ")
|
||||
|
||||
metricsName := strings.Trim(request.QueryParameter("metrics_name"), " ")
|
||||
workloadName := strings.Trim(request.QueryParameter("workload_name"), " ")
|
||||
|
||||
nodeId := strings.Trim(request.PathParameter("node_id"), " ")
|
||||
wsName := strings.Trim(request.PathParameter("workspace_name"), " ")
|
||||
nsName := strings.Trim(request.PathParameter("ns_name"), " ")
|
||||
podName := strings.Trim(request.PathParameter("pod_name"), " ")
|
||||
containerName := strings.Trim(request.PathParameter("container_name"), " ")
|
||||
workloadKind := strings.Trim(request.PathParameter("workload_kind"), " ")
|
||||
|
||||
var requestParams = MonitoringRequestParams{
|
||||
SortMetricName: sortMetricName,
|
||||
SortType: sortType,
|
||||
PageNum: pageNum,
|
||||
LimitNum: limitNum,
|
||||
Tp: tp,
|
||||
MetricsFilter: metricsFilter,
|
||||
NodesFilter: nodesFilter,
|
||||
WsFilter: wsFilter,
|
||||
NsFilter: nsFilter,
|
||||
PodsFilter: podsFilter,
|
||||
ContainersFilter: containersFilter,
|
||||
MetricsName: metricsName,
|
||||
WorkloadName: workloadName,
|
||||
WlFilter: wlFilter,
|
||||
NodeId: nodeId,
|
||||
WsName: wsName,
|
||||
NsName: nsName,
|
||||
PodName: podName,
|
||||
ContainerName: containerName,
|
||||
WorkloadKind: workloadKind,
|
||||
}
|
||||
|
||||
if timeout == "" {
|
||||
timeout = DefaultQueryTimeout
|
||||
}
|
||||
if step == "" {
|
||||
step = DefaultQueryStep
|
||||
}
|
||||
// Whether query or query_range request
|
||||
u := url.Values{}
|
||||
|
||||
if start != "" && end != "" {
|
||||
u.Set("start", convertTimeGranularity(start))
|
||||
u.Set("end", convertTimeGranularity(end))
|
||||
u.Set("step", step)
|
||||
u.Set("timeout", timeout)
|
||||
requestParams.QueryType = RangeQueryType
|
||||
requestParams.Params = u
|
||||
return &requestParams
|
||||
}
|
||||
if instantTime != "" {
|
||||
u.Set("time", instantTime)
|
||||
u.Set("timeout", timeout)
|
||||
requestParams.QueryType = DefaultQueryType
|
||||
requestParams.Params = u
|
||||
return &requestParams
|
||||
} else {
|
||||
//u.Set("time", strconv.FormatInt(int64(time.Now().Unix()), 10))
|
||||
u.Set("timeout", timeout)
|
||||
requestParams.QueryType = DefaultQueryType
|
||||
requestParams.Params = u
|
||||
return &requestParams
|
||||
}
|
||||
|
||||
glog.Errorln("Parse request %s failed", u)
|
||||
requestParams.QueryType = DefaultQueryType
|
||||
requestParams.Params = u
|
||||
|
||||
return &requestParams
|
||||
}
|
||||
|
||||
func convertTimeGranularity(ts string) string {
|
||||
timeFloat, err := strconv.ParseFloat(ts, 64)
|
||||
if err != nil {
|
||||
glog.Errorf("convert second timestamp %s to minute timestamp failed", ts)
|
||||
return strconv.FormatInt(int64(time.Now().Unix()), 10)
|
||||
}
|
||||
timeInt := int64(timeFloat)
|
||||
// convert second timestamp to minute timestamp
|
||||
secondTime := time.Unix(timeInt, 0).Truncate(time.Minute).Unix()
|
||||
return strconv.FormatInt(secondTime, 10)
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package constants
|
||||
|
||||
import "os"
|
||||
|
||||
type MessageResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type PageableResponse struct {
|
||||
Items []interface{} `json:"items"`
|
||||
TotalCount int `json:"total_count"`
|
||||
}
|
||||
|
||||
const (
|
||||
APIVersion = "v1alpha1"
|
||||
|
||||
KubeSystemNamespace = "kube-system"
|
||||
OpenPitrixNamespace = "openpitrix-system"
|
||||
IstioNamespace = "istio-system"
|
||||
KubeSphereNamespace = "kubesphere-system"
|
||||
KubeSphereControlNamespace = "kubesphere-controls-system"
|
||||
IngressControllerNamespace = KubeSphereControlNamespace
|
||||
AdminUserName = "admin"
|
||||
DataHome = "/etc/kubesphere"
|
||||
IngressControllerFolder = DataHome + "/ingress-controller"
|
||||
IngressControllerPrefix = "kubesphere-router-"
|
||||
DevopsAPIServerEnv = "DEVOPS_API_SERVER"
|
||||
AccountAPIServerEnv = "ACCOUNT_API_SERVER"
|
||||
DevopsProxyTokenEnv = "DEVOPS_PROXY_TOKEN"
|
||||
OpenPitrixProxyTokenEnv = "OPENPITRIX_PROXY_TOKEN"
|
||||
WorkspaceLabelKey = "kubesphere.io/workspace"
|
||||
WorkspaceAdmin = "workspace-admin"
|
||||
ClusterAdmin = "cluster-admin"
|
||||
WorkspaceRegular = "workspace-regular"
|
||||
WorkspaceViewer = "workspace-viewer"
|
||||
DevopsOwner = "owner"
|
||||
DevopsReporter = "reporter"
|
||||
)
|
||||
|
||||
var (
|
||||
DevopsAPIServer = "ks-devops-apiserver.kubesphere-system.svc"
|
||||
AccountAPIServer = "ks-account.kubesphere-system.svc"
|
||||
DevopsProxyToken = ""
|
||||
OpenPitrixProxyToken = ""
|
||||
WorkSpaceRoles = []string{WorkspaceAdmin, WorkspaceRegular, WorkspaceViewer}
|
||||
)
|
||||
|
||||
func init() {
|
||||
if env := os.Getenv(DevopsAPIServerEnv); env != "" {
|
||||
DevopsAPIServer = env
|
||||
}
|
||||
if env := os.Getenv(AccountAPIServerEnv); env != "" {
|
||||
AccountAPIServer = env
|
||||
}
|
||||
if env := os.Getenv(DevopsProxyTokenEnv); env != "" {
|
||||
DevopsProxyToken = env
|
||||
}
|
||||
if env := os.Getenv(OpenPitrixProxyTokenEnv); env != "" {
|
||||
OpenPitrixProxyToken = env
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package container
|
||||
|
||||
import "github.com/emicklei/go-restful"
|
||||
|
||||
// add filter to default container
|
||||
func init() {
|
||||
|
||||
restful.Filter(logFilter())
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package container
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
func logFilter() restful.FilterFunction {
|
||||
return func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
|
||||
start := time.Now()
|
||||
chain.ProcessFilter(req, resp)
|
||||
glog.Infof("%s - \"%s %s %s\" %d %d in %dms",
|
||||
strings.Split(req.Request.RemoteAddr, ":")[0],
|
||||
req.Request.Method,
|
||||
req.Request.URL.RequestURI(),
|
||||
req.Request.Proto,
|
||||
resp.StatusCode(),
|
||||
resp.ContentLength(),
|
||||
time.Since(start)/time.Millisecond,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package route
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Route Filter (defines FilterFunction)
|
||||
func RouteLogging(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
|
||||
|
||||
start := time.Now()
|
||||
chain.ProcessFilter(req, resp)
|
||||
glog.Infof("%s - \"%s %s %s\" %d %dms",
|
||||
strings.Split(req.Request.RemoteAddr, ":")[0],
|
||||
req.Request.Method,
|
||||
req.Request.URL.RequestURI(),
|
||||
req.Request.Proto,
|
||||
resp.StatusCode(),
|
||||
time.Now().Sub(start)/1000000,
|
||||
)
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package logs
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
flag.Set("logtostderr", "true")
|
||||
}
|
||||
|
||||
// GlogWriter serves as a bridge between the standard log package and the glog package.
|
||||
type GlogWriter struct{}
|
||||
|
||||
// Write implements the io.Writer interface.
|
||||
func (writer GlogWriter) Write(data []byte) (n int, err error) {
|
||||
|
||||
glog.Info(string(data))
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// InitLogs initializes logs the way we want for kubeSphere.
|
||||
func InitLogs() {
|
||||
|
||||
log.SetOutput(GlogWriter{})
|
||||
log.SetFlags(0)
|
||||
}
|
||||
|
||||
// FlushLogs flushes logs immediately.
|
||||
func FlushLogs() {
|
||||
|
||||
glog.Flush()
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
v13 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/client"
|
||||
|
||||
v12 "k8s.io/client-go/listers/core/v1"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/models/controllers"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Namespaces need to watch
|
||||
var SYSTEM_NAMESPACES = [...]string{"kubesphere-system", "openpitrix-system", "kube-system"}
|
||||
|
||||
type Component struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
SelfLink string `json:"selfLink"`
|
||||
Label interface{} `json:"label"`
|
||||
StartedAt time.Time `json:"startedAt"`
|
||||
TotalBackends int `json:"totalBackends"`
|
||||
HealthyBackends int `json:"healthyBackends"`
|
||||
}
|
||||
|
||||
func GetComponentStatus(namespace string, componentName string) (interface{}, error) {
|
||||
lister, err := controllers.GetLister(controllers.Services)
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serviceLister := lister.(v12.ServiceLister)
|
||||
service, err := serviceLister.Services(namespace).Get(componentName)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lister, err = controllers.GetLister(controllers.Pods)
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podLister := lister.(v12.PodLister)
|
||||
|
||||
set := labels.Set(service.Spec.Selector)
|
||||
|
||||
pods, err := podLister.Pods(namespace).List(set.AsSelector())
|
||||
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
return nil, err
|
||||
} else {
|
||||
component := Component{
|
||||
Name: service.Name,
|
||||
Namespace: service.Namespace,
|
||||
SelfLink: service.SelfLink,
|
||||
Label: service.Spec.Selector,
|
||||
StartedAt: service.CreationTimestamp.Time,
|
||||
HealthyBackends: 0,
|
||||
TotalBackends: 0,
|
||||
}
|
||||
|
||||
for _, v := range pods {
|
||||
component.TotalBackends++
|
||||
component.HealthyBackends++
|
||||
for _, c := range v.Status.ContainerStatuses {
|
||||
if !c.Ready {
|
||||
component.HealthyBackends--
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return component, nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func GetSystemHealthStatus() (map[string]interface{}, error) {
|
||||
|
||||
status := make(map[string]interface{})
|
||||
|
||||
k8sClient := client.NewK8sClient()
|
||||
|
||||
csList, err := k8sClient.Core().ComponentStatuses().List(v1.ListOptions{})
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, cs := range csList.Items {
|
||||
status[cs.Name] = cs.Conditions[0]
|
||||
}
|
||||
|
||||
// get kubesphere-system components
|
||||
|
||||
systemComponentStatus, err := GetAllComponentsStatus()
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
|
||||
for k, v := range systemComponentStatus {
|
||||
status[k] = v
|
||||
}
|
||||
// get node status
|
||||
|
||||
lister, err := controllers.GetLister(controllers.Nodes)
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
return status, nil
|
||||
}
|
||||
|
||||
nodeLister := lister.(v12.NodeLister)
|
||||
|
||||
nodes, err := nodeLister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
return status, nil
|
||||
}
|
||||
|
||||
nodeStatus := make(map[string]int)
|
||||
totalNodes := 0
|
||||
healthyNodes := 0
|
||||
for _, nodes := range nodes {
|
||||
totalNodes++
|
||||
for _, condition := range nodes.Status.Conditions {
|
||||
if condition.Type == v13.NodeReady && condition.Status == v13.ConditionTrue {
|
||||
healthyNodes++
|
||||
}
|
||||
}
|
||||
}
|
||||
nodeStatus["totalNodes"] = totalNodes
|
||||
nodeStatus["healthyNodes"] = healthyNodes
|
||||
|
||||
status["nodes"] = nodeStatus
|
||||
|
||||
return status, nil
|
||||
|
||||
}
|
||||
|
||||
func GetAllComponentsStatus() (map[string]interface{}, error) {
|
||||
|
||||
status := make(map[string]interface{})
|
||||
var err error
|
||||
|
||||
lister, err := controllers.GetLister(controllers.Services)
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serviceLister := lister.(v12.ServiceLister)
|
||||
|
||||
lister, err = controllers.GetLister(controllers.Pods)
|
||||
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podLister := lister.(v12.PodLister)
|
||||
|
||||
for _, ns := range SYSTEM_NAMESPACES {
|
||||
|
||||
nsStatus := make(map[string]interface{})
|
||||
|
||||
services, err := serviceLister.Services(ns).List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, service := range services {
|
||||
|
||||
set := labels.Set(service.Spec.Selector)
|
||||
|
||||
if len(set) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
component := Component{
|
||||
Name: service.Name,
|
||||
Namespace: service.Namespace,
|
||||
SelfLink: service.SelfLink,
|
||||
Label: service.Spec.Selector,
|
||||
StartedAt: service.CreationTimestamp.Time,
|
||||
HealthyBackends: 0,
|
||||
TotalBackends: 0,
|
||||
}
|
||||
|
||||
pods, err := podLister.Pods(ns).List(set.AsSelector())
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, pod := range pods {
|
||||
component.TotalBackends++
|
||||
component.HealthyBackends++
|
||||
for _, c := range pod.Status.ContainerStatuses {
|
||||
if !c.Ready {
|
||||
component.HealthyBackends--
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsStatus[service.Name] = component
|
||||
}
|
||||
|
||||
if len(nsStatus) > 0 {
|
||||
status[ns] = nsStatus
|
||||
}
|
||||
}
|
||||
|
||||
return status, err
|
||||
}
|
||||
@@ -1,490 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/client"
|
||||
)
|
||||
|
||||
const (
|
||||
unknown = "-"
|
||||
deploySurffix = "-Deployment"
|
||||
daemonSurffix = "-DaemonSet"
|
||||
stateSurffix = "-StatefulSet"
|
||||
)
|
||||
|
||||
type ApplicationCtl struct {
|
||||
OpenpitrixAddr string
|
||||
}
|
||||
|
||||
type Application struct {
|
||||
Name string `json:"name"`
|
||||
RepoName string `json:"repoName"`
|
||||
Runtime string `json:"namespace"`
|
||||
RuntimeId string `json:"runtime_id"`
|
||||
Version string `json:"version"`
|
||||
VersionId string `json:"version_id"`
|
||||
Status string `json:"status"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
App string `json:"app"`
|
||||
AppId string `json:"app_id"`
|
||||
Description string `json:"description,omitempty"`
|
||||
WorkLoads *workLoads `json:"workloads,omitempty"`
|
||||
Services *[]Service `json:"services,omitempty"`
|
||||
Ingresses *[]ing `json:"ingresses,omitempty"`
|
||||
ClusterID string `json:"cluster_id"`
|
||||
}
|
||||
|
||||
type ing struct {
|
||||
Name string `json:"name"`
|
||||
Rules []ingressRule `json:"rules"`
|
||||
}
|
||||
|
||||
type clusterRole struct {
|
||||
ClusterID string `json:"cluster_id"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
type cluster struct {
|
||||
ClusterID string `json:"cluster_id"`
|
||||
Name string `json:"name"`
|
||||
AppID string `json:"app_id"`
|
||||
VersionID string `json:"version_id"`
|
||||
Status string `json:"status"`
|
||||
UpdateTime time.Time `json:"status_time"`
|
||||
CreateTime time.Time `json:"create_time"`
|
||||
RunTimeId string `json:"runtime_id"`
|
||||
Description string `json:"description"`
|
||||
ClusterRoleSets []clusterRole `json:"cluster_role_set"`
|
||||
}
|
||||
|
||||
type clusters struct {
|
||||
Total int `json:"total_count"`
|
||||
Clusters []cluster `json:"cluster_set"`
|
||||
}
|
||||
|
||||
type versionList struct {
|
||||
Total int `json:"total_count"`
|
||||
Versions []version `json:"app_version_set"`
|
||||
}
|
||||
|
||||
type version struct {
|
||||
Name string `json:"name"`
|
||||
VersionID string `json:"version_id"`
|
||||
}
|
||||
|
||||
type runtime struct {
|
||||
RuntimeID string `json:"runtime_id"`
|
||||
Zone string `json:"zone"`
|
||||
}
|
||||
|
||||
type runtimeList struct {
|
||||
Total int `json:"total_count"`
|
||||
Runtimes []runtime `json:"runtime_set"`
|
||||
}
|
||||
|
||||
type app struct {
|
||||
AppId string `json:"app_id"`
|
||||
Name string `json:"name"`
|
||||
ChartName string `json:"chart_name"`
|
||||
RepoId string `json:"repo_id"`
|
||||
}
|
||||
|
||||
type repo struct {
|
||||
RepoId string `json:"repo_id"`
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
type workLoads struct {
|
||||
Deployments []Deployment `json:"deployments,omitempty"`
|
||||
Statefulsets []Statefulset `json:"statefulsets,omitempty"`
|
||||
Daemonsets []Daemonset `json:"daemonsets,omitempty"`
|
||||
}
|
||||
|
||||
//type description struct {
|
||||
// Creator string `json:"creator"`
|
||||
//}
|
||||
|
||||
type appList struct {
|
||||
Total int `json:"total_count"`
|
||||
Apps []app `json:"app_set"`
|
||||
}
|
||||
|
||||
type repoList struct {
|
||||
Total int `json:"total_count"`
|
||||
Repos []repo `json:"repo_set"`
|
||||
}
|
||||
|
||||
func (ctl *ApplicationCtl) GetAppInfo(appId string) (string, string, string, error) {
|
||||
url := fmt.Sprintf("%s/v1/apps?app_id=%s", ctl.OpenpitrixAddr, appId)
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, unknown, unknown, err
|
||||
}
|
||||
|
||||
var apps appList
|
||||
err = json.Unmarshal(resp, &apps)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, unknown, unknown, err
|
||||
}
|
||||
|
||||
if len(apps.Apps) == 0 {
|
||||
return unknown, unknown, unknown, err
|
||||
}
|
||||
|
||||
return apps.Apps[0].ChartName, apps.Apps[0].RepoId, apps.Apps[0].AppId, nil
|
||||
}
|
||||
|
||||
func (ctl *ApplicationCtl) GetRepo(repoId string) (string, error) {
|
||||
url := fmt.Sprintf("%s/v1/repos?repo_id=%s", ctl.OpenpitrixAddr, repoId)
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
var repos repoList
|
||||
err = json.Unmarshal(resp, &repos)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
if len(repos.Repos) == 0 {
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
return repos.Repos[0].Name, nil
|
||||
}
|
||||
|
||||
func (ctl *ApplicationCtl) GetVersion(versionId string) (string, error) {
|
||||
versionUrl := fmt.Sprintf("%s/v1/app_versions?version_id=%s", ctl.OpenpitrixAddr, versionId)
|
||||
resp, err := makeHttpRequest("GET", versionUrl, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
var versions versionList
|
||||
err = json.Unmarshal(resp, &versions)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
if len(versions.Versions) == 0 {
|
||||
return unknown, nil
|
||||
}
|
||||
return versions.Versions[0].Name, nil
|
||||
}
|
||||
|
||||
func (ctl *ApplicationCtl) GetRuntime(runtimeId string) (string, error) {
|
||||
|
||||
versionUrl := fmt.Sprintf("%s/v1/runtimes?runtime_id=%s", ctl.OpenpitrixAddr, runtimeId)
|
||||
resp, err := makeHttpRequest("GET", versionUrl, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
var runtimes runtimeList
|
||||
err = json.Unmarshal(resp, &runtimes)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
if len(runtimes.Runtimes) == 0 {
|
||||
return unknown, nil
|
||||
}
|
||||
|
||||
return runtimes.Runtimes[0].Zone, nil
|
||||
}
|
||||
|
||||
func (ctl *ApplicationCtl) GetWorkLoads(namespace string, clusterRoles []clusterRole) *workLoads {
|
||||
|
||||
var works workLoads
|
||||
for _, clusterRole := range clusterRoles {
|
||||
workLoadName := clusterRole.Role
|
||||
if len(workLoadName) > 0 {
|
||||
if strings.HasSuffix(workLoadName, deploySurffix) {
|
||||
name := strings.Split(workLoadName, deploySurffix)[0]
|
||||
ctl := ResourceControllers.Controllers[Deployments]
|
||||
_, items, _ := ctl.ListWithConditions(fmt.Sprintf("namespace='%s' and name = '%s'", namespace, name), nil, "")
|
||||
works.Deployments = append(works.Deployments, items.([]Deployment)...)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(workLoadName, daemonSurffix) {
|
||||
name := strings.Split(workLoadName, daemonSurffix)[0]
|
||||
ctl := ResourceControllers.Controllers[Daemonsets]
|
||||
_, items, _ := ctl.ListWithConditions(fmt.Sprintf("namespace='%s' and name = '%s'", namespace, name), nil, "")
|
||||
works.Daemonsets = append(works.Daemonsets, items.([]Daemonset)...)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(workLoadName, stateSurffix) {
|
||||
name := strings.Split(workLoadName, stateSurffix)[0]
|
||||
ctl := ResourceControllers.Controllers[Statefulsets]
|
||||
_, items, _ := ctl.ListWithConditions(fmt.Sprintf("namespace='%s' and name = '%s'", namespace, name), nil, "")
|
||||
works.Statefulsets = append(works.Statefulsets, items.([]Statefulset)...)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return &works
|
||||
}
|
||||
|
||||
func (ctl *ApplicationCtl) getLabels(namespace string, workloads *workLoads) *[]map[string]string {
|
||||
k8sClient := client.NewK8sClient()
|
||||
|
||||
var workloadLables []map[string]string
|
||||
if workloads == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, workload := range workloads.Deployments {
|
||||
deploy, err := k8sClient.AppsV1().Deployments(namespace).Get(workload.Name, metaV1.GetOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
workloadLables = append(workloadLables, deploy.Labels)
|
||||
}
|
||||
|
||||
for _, workload := range workloads.Daemonsets {
|
||||
daemonset, err := k8sClient.AppsV1().DaemonSets(namespace).Get(workload.Name, metaV1.GetOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
workloadLables = append(workloadLables, daemonset.Labels)
|
||||
}
|
||||
|
||||
for _, workload := range workloads.Statefulsets {
|
||||
statefulset, err := k8sClient.AppsV1().StatefulSets(namespace).Get(workload.Name, metaV1.GetOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
workloadLables = append(workloadLables, statefulset.Labels)
|
||||
}
|
||||
|
||||
return &workloadLables
|
||||
}
|
||||
|
||||
func isExist(svcs []Service, svc v1.Service) bool {
|
||||
for _, item := range svcs {
|
||||
if item.Name == svc.Name && item.Namespace == svc.Namespace {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ctl *ApplicationCtl) getSvcs(namespace string, workLoadLabels *[]map[string]string) *[]Service {
|
||||
if len(*workLoadLabels) == 0 {
|
||||
return nil
|
||||
}
|
||||
k8sClient := client.NewK8sClient()
|
||||
var services []Service
|
||||
for _, label := range *workLoadLabels {
|
||||
labelSelector := labels.Set(label).AsSelector().String()
|
||||
svcs, err := k8sClient.CoreV1().Services(namespace).List(metaV1.ListOptions{LabelSelector: labelSelector})
|
||||
if err != nil {
|
||||
glog.Errorf("get app's svc failed, reason: %v", err)
|
||||
}
|
||||
for _, item := range svcs.Items {
|
||||
if !isExist(services, item) {
|
||||
services = append(services, *generateSvcObject(item))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &services
|
||||
}
|
||||
|
||||
func (ctl *ApplicationCtl) getIng(namespace string, services *[]Service) *[]ing {
|
||||
if services == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ingCtl := ResourceControllers.Controllers[Ingresses]
|
||||
var ings []ing
|
||||
for _, svc := range *services {
|
||||
_, items, err := ingCtl.ListWithConditions(fmt.Sprintf("namespace = '%s' and rules like '%%%s%%' ", namespace, svc.Name), nil, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
glog.Error(items)
|
||||
for _, ingress := range items.([]Ingress) {
|
||||
var rules []ingressRule
|
||||
err := json.Unmarshal([]byte(ingress.Rules), &rules)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
exist := false
|
||||
var tmpRules []ingressRule
|
||||
for _, rule := range rules {
|
||||
if rule.Service == svc.Name {
|
||||
exist = true
|
||||
tmpRules = append(tmpRules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
if exist {
|
||||
ings = append(ings, ing{Name: ingress.Name, Rules: tmpRules})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &ings
|
||||
}
|
||||
|
||||
func (ctl *ApplicationCtl) ListApplication(runtimeId string, match, fuzzy map[string]string, paging *Paging) (int, interface{}, error) {
|
||||
limit := paging.Limit
|
||||
offset := paging.Offset
|
||||
if strings.HasSuffix(ctl.OpenpitrixAddr, "/") {
|
||||
ctl.OpenpitrixAddr = strings.TrimSuffix(ctl.OpenpitrixAddr, "/")
|
||||
}
|
||||
|
||||
defaultStatus := "status=active&status=stopped&status=pending&status=ceased"
|
||||
|
||||
url := fmt.Sprintf("%s/v1/clusters?limit=%s&offset=%s", ctl.OpenpitrixAddr, strconv.Itoa(limit), strconv.Itoa(offset))
|
||||
|
||||
if len(fuzzy["name"]) > 0 {
|
||||
url = fmt.Sprintf("%s&search_word=%s", url, fuzzy["name"])
|
||||
}
|
||||
|
||||
if len(match["status"]) > 0 {
|
||||
url = fmt.Sprintf("%s&status=%s", url, match["status"])
|
||||
} else {
|
||||
url = fmt.Sprintf("%s&%s", url, defaultStatus)
|
||||
}
|
||||
|
||||
if len(runtimeId) > 0 {
|
||||
url = fmt.Sprintf("%s&runtime_id=%s", url, runtimeId)
|
||||
}
|
||||
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Errorf("request %s failed, reason: %s", url, err)
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
var clusterList clusters
|
||||
err = json.Unmarshal(resp, &clusterList)
|
||||
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
var apps []Application
|
||||
|
||||
for _, item := range clusterList.Clusters {
|
||||
var app Application
|
||||
|
||||
app.Name = item.Name
|
||||
app.ClusterID = item.ClusterID
|
||||
app.UpdateTime = item.UpdateTime
|
||||
app.Status = item.Status
|
||||
versionInfo, _ := ctl.GetVersion(item.VersionID)
|
||||
app.Version = versionInfo
|
||||
app.VersionId = item.VersionID
|
||||
runtimeInfo, _ := ctl.GetRuntime(item.RunTimeId)
|
||||
app.Runtime = runtimeInfo
|
||||
app.RuntimeId = item.RunTimeId
|
||||
appInfo, _, appId, _ := ctl.GetAppInfo(item.AppID)
|
||||
app.App = appInfo
|
||||
app.AppId = appId
|
||||
app.Description = item.Description
|
||||
|
||||
apps = append(apps, app)
|
||||
}
|
||||
|
||||
return clusterList.Total, apps, nil
|
||||
}
|
||||
|
||||
func (ctl *ApplicationCtl) GetApp(clusterId string) (*Application, error) {
|
||||
if strings.HasSuffix(ctl.OpenpitrixAddr, "/") {
|
||||
ctl.OpenpitrixAddr = strings.TrimSuffix(ctl.OpenpitrixAddr, "/")
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/v1/clusters?cluster_id=%s", ctl.OpenpitrixAddr, clusterId)
|
||||
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var clusterList clusters
|
||||
err = json.Unmarshal(resp, &clusterList)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(clusterList.Clusters) == 0 {
|
||||
return nil, fmt.Errorf("NotFound, clusterId:%s", clusterId)
|
||||
}
|
||||
|
||||
item := clusterList.Clusters[0]
|
||||
var app Application
|
||||
|
||||
app.Name = item.Name
|
||||
app.ClusterID = item.ClusterID
|
||||
app.UpdateTime = item.UpdateTime
|
||||
app.CreateTime = item.CreateTime
|
||||
app.Status = item.Status
|
||||
versionInfo, _ := ctl.GetVersion(item.VersionID)
|
||||
app.Version = versionInfo
|
||||
app.VersionId = item.VersionID
|
||||
|
||||
runtimeInfo, _ := ctl.GetRuntime(item.RunTimeId)
|
||||
app.Runtime = runtimeInfo
|
||||
app.RuntimeId = item.RunTimeId
|
||||
appInfo, repoId, appId, _ := ctl.GetAppInfo(item.AppID)
|
||||
app.App = appInfo
|
||||
app.AppId = appId
|
||||
app.Description = item.Description
|
||||
|
||||
app.RepoName, _ = ctl.GetRepo(repoId)
|
||||
app.WorkLoads = ctl.GetWorkLoads(app.Runtime, item.ClusterRoleSets)
|
||||
workloadLabels := ctl.getLabels(app.Runtime, app.WorkLoads)
|
||||
app.Services = ctl.getSvcs(app.Runtime, workloadLabels)
|
||||
app.Ingresses = ctl.getIng(app.Runtime, app.Services)
|
||||
|
||||
return &app, nil
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/pkg/errors"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models/kubectl"
|
||||
)
|
||||
|
||||
func (ctl *ClusterRoleBindingCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleBindingCtl) sync(stopChan chan struct{}) {
|
||||
ctl.initListerAndInformer()
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleBindingCtl) total() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleBindingCtl) handleWorkspaceRoleChange(clusterRole *rbac.ClusterRoleBinding) {
|
||||
if groups := regexp.MustCompile(fmt.Sprintf(`^system:(\S+):(%s)$`, strings.Join(constants.WorkSpaceRoles, "|"))).FindStringSubmatch(clusterRole.Name); len(groups) == 3 {
|
||||
workspace := groups[1]
|
||||
go ctl.restNamespaceRoleBinding(workspace)
|
||||
}
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleBindingCtl) restNamespaceRoleBinding(workspace string) {
|
||||
selector := labels.SelectorFromSet(labels.Set{"kubesphere.io/workspace": workspace})
|
||||
namespaces, err := ctl.K8sClient.CoreV1().Namespaces().List(meta_v1.ListOptions{LabelSelector: selector.String()})
|
||||
|
||||
if err != nil {
|
||||
glog.Warning("workspace roles sync failed", workspace, err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, namespace := range namespaces.Items {
|
||||
pathJson := fmt.Sprintf(`{"metadata":{"annotations":{"%s":"%s"}}}`, initTimeAnnotateKey, "")
|
||||
_, err := ctl.K8sClient.CoreV1().Namespaces().Patch(namespace.Name, "application/strategic-merge-patch+json", []byte(pathJson))
|
||||
if err != nil {
|
||||
glog.Warning("workspace roles sync failed", workspace, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleBindingCtl) initListerAndInformer() {
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
ctl.lister = informerFactory.Rbac().V1().ClusterRoleBindings().Lister()
|
||||
ctl.informer = informerFactory.Rbac().V1().ClusterRoleBindings().Informer()
|
||||
ctl.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
|
||||
ctl.handleTerminalCreate(clusterRoleBinding)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
oldValue := old.(*rbac.ClusterRoleBinding)
|
||||
newValue := new.(*rbac.ClusterRoleBinding)
|
||||
if !subjectsCompile(oldValue.Subjects, newValue.Subjects) {
|
||||
ctl.handleWorkspaceRoleChange(newValue)
|
||||
ctl.handleTerminalUpdate(oldValue, newValue)
|
||||
}
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
|
||||
ctl.handleTerminalDelete(clusterRoleBinding)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleBindingCtl) handleTerminalCreate(clusterRoleBinding *rbac.ClusterRoleBinding) {
|
||||
if clusterRoleBinding.RoleRef.Name == constants.ClusterAdmin {
|
||||
for _, subject := range clusterRoleBinding.Subjects {
|
||||
if subject.Kind == rbac.UserKind {
|
||||
err := kubectl.CreateKubectlDeploy(subject.Name)
|
||||
if err != nil {
|
||||
glog.Error(fmt.Sprintf("create %s's terminal pod failed:%s", subject.Name, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func (ctl *ClusterRoleBindingCtl) handleTerminalUpdate(old *rbac.ClusterRoleBinding, new *rbac.ClusterRoleBinding) {
|
||||
if new.RoleRef.Name == constants.ClusterAdmin {
|
||||
for _, newSubject := range new.Subjects {
|
||||
isAdded := true
|
||||
for _, oldSubject := range old.Subjects {
|
||||
if oldSubject == newSubject {
|
||||
isAdded = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if isAdded && newSubject.Kind == rbac.UserKind {
|
||||
err := kubectl.CreateKubectlDeploy(newSubject.Name)
|
||||
if err != nil {
|
||||
glog.Error(fmt.Sprintf("create %s's terminal pod failed:%s", newSubject.Name, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, oldSubject := range old.Subjects {
|
||||
isDeleted := true
|
||||
for _, newSubject := range new.Subjects {
|
||||
if newSubject == oldSubject {
|
||||
isDeleted = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if isDeleted && oldSubject.Kind == rbac.UserKind {
|
||||
err := kubectl.DelKubectlDeploy(oldSubject.Name)
|
||||
if err != nil {
|
||||
glog.Error(fmt.Sprintf("delete %s's terminal pod failed:%s", oldSubject.Name, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleBindingCtl) handleTerminalDelete(clusterRoleBinding *rbac.ClusterRoleBinding) {
|
||||
if clusterRoleBinding.RoleRef.Name == constants.ClusterAdmin {
|
||||
for _, subject := range clusterRoleBinding.Subjects {
|
||||
if subject.Kind == rbac.UserKind {
|
||||
err := kubectl.DelKubectlDeploy(subject.Name)
|
||||
if err != nil {
|
||||
glog.Error(fmt.Sprintf("delete %s's terminal pod failed:%s", subject.Name, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func subjectsCompile(s1 []rbac.Subject, s2 []rbac.Subject) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, v := range s1 {
|
||||
if v.Name != s2[i].Name || v.Kind != s2[i].Kind {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleBindingCtl) CountWithConditions(conditions string) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleBindingCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
return 0, nil, errors.New("not implement")
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleBindingCtl) Lister() interface{} {
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
const systemPrefix = "system:"
|
||||
|
||||
func (ctl *ClusterRoleCtl) generateObject(item v1.ClusterRole) *ClusterRole {
|
||||
var displayName string
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
|
||||
name := item.Name
|
||||
if strings.HasPrefix(name, systemPrefix) || item.Annotations == nil || len(item.Annotations[creator]) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
createTime := item.CreationTimestamp.Time
|
||||
if createTime.IsZero() {
|
||||
createTime = time.Now()
|
||||
}
|
||||
|
||||
object := &ClusterRole{Name: name, CreateTime: createTime, Annotation: MapString{item.Annotations}, DisplayName: displayName}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&ClusterRole{}) {
|
||||
db.DropTable(&ClusterRole{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&ClusterRole{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
if obj != nil {
|
||||
if err := db.Create(obj).Error; err != nil {
|
||||
glog.Error("cluster roles sync error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s failed, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
|
||||
count := 0
|
||||
for _, item := range list {
|
||||
if !strings.HasPrefix(item.Name, systemPrefix) && item.Annotations != nil && len(item.Annotations[creator]) > 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
ctl.lister = informerFactory.Rbac().V1().ClusterRoles().Lister()
|
||||
|
||||
informer := informerFactory.Rbac().V1().ClusterRoles().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1.ClusterRole)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
if mysqlObject != nil {
|
||||
if err := db.Create(mysqlObject).Error; err != nil {
|
||||
glog.Error("cluster roles sync error", err)
|
||||
}
|
||||
}
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.ClusterRole)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
if mysqlObject != nil {
|
||||
if err := db.Save(mysqlObject).Error; err != nil {
|
||||
glog.Error("cluster roles update error", err)
|
||||
}
|
||||
}
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1.ClusterRole)
|
||||
|
||||
if err := db.Delete(ClusterRole{}, "name=?", object.Name).Error; err != nil {
|
||||
glog.Error("cluster roles delete error", err)
|
||||
}
|
||||
},
|
||||
})
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleCtl) CountWithConditions(conditions string) int {
|
||||
var object ClusterRole
|
||||
|
||||
if strings.Contains(conditions, "namespace") {
|
||||
conditions = ""
|
||||
}
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var object ClusterRole
|
||||
var list []ClusterRole
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
db := ctl.DB
|
||||
|
||||
listWithConditions(db, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *ClusterRoleCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
)
|
||||
|
||||
const (
|
||||
checkPeriod = 30 * time.Minute
|
||||
sleepPeriod = 15 * time.Second
|
||||
)
|
||||
|
||||
func listWithConditions(db *gorm.DB, total *int, object, list interface{}, conditions string, paging *Paging, order string) {
|
||||
if len(conditions) == 0 {
|
||||
db.Model(object).Count(total)
|
||||
} else {
|
||||
db.Model(object).Where(conditions).Count(total)
|
||||
}
|
||||
|
||||
if paging != nil {
|
||||
if len(conditions) > 0 {
|
||||
db.Where(conditions).Order(order).Limit(paging.Limit).Offset(paging.Offset).Find(list)
|
||||
} else {
|
||||
db.Order(order).Limit(paging.Limit).Offset(paging.Offset).Find(list)
|
||||
}
|
||||
|
||||
} else {
|
||||
if len(conditions) > 0 {
|
||||
db.Where(conditions).Order(order).Find(list)
|
||||
} else {
|
||||
db.Order(order).Find(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func countWithConditions(db *gorm.DB, conditions string, object interface{}) int {
|
||||
var count int
|
||||
if len(conditions) == 0 {
|
||||
db.Model(object).Count(&count)
|
||||
} else {
|
||||
db.Model(object).Where(conditions).Count(&count)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func makeHttpRequest(method, url, data string) ([]byte, error) {
|
||||
var req *http.Request
|
||||
|
||||
var err error
|
||||
if method == "GET" {
|
||||
req, err = http.NewRequest(method, url, nil)
|
||||
} else {
|
||||
req, err = http.NewRequest(method, url, strings.NewReader(data))
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", constants.OpenPitrixProxyToken)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
httpClient := &http.Client{}
|
||||
resp, err := httpClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Request to %s failed, method: %s, reason: %s ", url, method, err)
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
err = errors.New(string(body))
|
||||
}
|
||||
return body, err
|
||||
}
|
||||
|
||||
func handleCrash(ctl Controller) {
|
||||
close(ctl.chanAlive())
|
||||
if err := recover(); err != nil {
|
||||
glog.Errorf("panic occur in %s controller's listAndWatch function, reason: %s", ctl.Name(), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func hasSynced(ctl Controller) bool {
|
||||
totalInDb := ctl.CountWithConditions("")
|
||||
totalInK8s := ctl.total()
|
||||
|
||||
if totalInDb == totalInK8s {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkAndResync(ctl Controller, stopChan chan struct{}) {
|
||||
defer close(stopChan)
|
||||
|
||||
lastTime := time.Now()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctl.chanStop():
|
||||
return
|
||||
default:
|
||||
if time.Now().Sub(lastTime) < checkPeriod {
|
||||
time.Sleep(sleepPeriod)
|
||||
break
|
||||
}
|
||||
|
||||
lastTime = time.Now()
|
||||
if !hasSynced(ctl) {
|
||||
glog.Errorf("the data in db and kubernetes is inconsistent, resync %s controller", ctl.Name())
|
||||
close(stopChan)
|
||||
stopChan = make(chan struct{})
|
||||
go ctl.sync(stopChan)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listAndWatch(ctl Controller, wg *sync.WaitGroup) {
|
||||
defer handleCrash(ctl)
|
||||
defer ctl.CloseDB()
|
||||
defer wg.Done()
|
||||
stopChan := make(chan struct{})
|
||||
|
||||
go ctl.sync(stopChan)
|
||||
|
||||
checkAndResync(ctl, stopChan)
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
func (ctl *ConfigMapCtl) generateObject(item v1.ConfigMap) *ConfigMap {
|
||||
var displayName string
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
|
||||
createTime := item.CreationTimestamp.Time
|
||||
if createTime.IsZero() {
|
||||
createTime = time.Now()
|
||||
}
|
||||
|
||||
var entries []string
|
||||
|
||||
for entry := range item.Data {
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
object := &ConfigMap{
|
||||
Name: item.Name,
|
||||
Namespace: item.Namespace,
|
||||
CreateTime: createTime,
|
||||
Annotation: MapString{item.Annotations},
|
||||
DisplayName: displayName,
|
||||
Entries: strings.Join(entries, ","),
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *ConfigMapCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *ConfigMapCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&ConfigMap{}) {
|
||||
db.DropTable(&ConfigMap{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&ConfigMap{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
if obj != nil {
|
||||
db.Create(obj)
|
||||
}
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *ConfigMapCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *ConfigMapCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
ctl.lister = informerFactory.Core().V1().ConfigMaps().Lister()
|
||||
|
||||
informer := informerFactory.Core().V1().ConfigMaps().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1.ConfigMap)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
if mysqlObject != nil {
|
||||
db.Create(mysqlObject)
|
||||
}
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.ConfigMap)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
if mysqlObject != nil {
|
||||
db.Save(mysqlObject)
|
||||
}
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item ConfigMap
|
||||
object := obj.(*v1.ConfigMap)
|
||||
db.Where("name=?", object.Name).Find(&item)
|
||||
db.Delete(item)
|
||||
|
||||
},
|
||||
})
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *ConfigMapCtl) CountWithConditions(conditions string) int {
|
||||
var object ConfigMap
|
||||
|
||||
if strings.Contains(conditions, "namespace") {
|
||||
conditions = ""
|
||||
}
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *ConfigMapCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var object ConfigMap
|
||||
var list []ConfigMap
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
db := ctl.DB
|
||||
|
||||
listWithConditions(db, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *ConfigMapCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/informers"
|
||||
)
|
||||
|
||||
func (ctl *ControllerRevisionCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *ControllerRevisionCtl) sync(stopChan chan struct{}) {
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *ControllerRevisionCtl) total() int {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ctl *ControllerRevisionCtl) initListerAndInformer() {
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
|
||||
ctl.lister = informerFactory.Apps().V1().ControllerRevisions().Lister()
|
||||
|
||||
informer := informerFactory.Apps().V1().ControllerRevisions().Informer()
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *ControllerRevisionCtl) CountWithConditions(conditions string) int {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ctl *ControllerRevisionCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
func (ctl *ControllerRevisionCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/batch/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
func (ctl *CronJobCtl) generateObject(item v1beta1.CronJob) *CronJob {
|
||||
var status, displayName string
|
||||
var lastScheduleTime *time.Time
|
||||
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
|
||||
name := item.Name
|
||||
namespace := item.Namespace
|
||||
|
||||
status = Running
|
||||
if *item.Spec.Suspend {
|
||||
status = Pause
|
||||
}
|
||||
|
||||
schedule := item.Spec.Schedule
|
||||
if item.Status.LastScheduleTime != nil {
|
||||
lastScheduleTime = &item.Status.LastScheduleTime.Time
|
||||
}
|
||||
|
||||
active := len(item.Status.Active)
|
||||
|
||||
object := &CronJob{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
LastScheduleTime: lastScheduleTime,
|
||||
Active: active,
|
||||
Schedule: schedule,
|
||||
Status: status,
|
||||
Annotation: MapString{item.Annotations},
|
||||
Labels: MapString{item.ObjectMeta.Labels},
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *CronJobCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *CronJobCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&CronJob{}) {
|
||||
db.DropTable(&CronJob{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&CronJob{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
db.Create(obj)
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *CronJobCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *CronJobCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
ctl.lister = informerFactory.Batch().V1beta1().CronJobs().Lister()
|
||||
|
||||
informer := informerFactory.Batch().V1beta1().CronJobs().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1beta1.CronJob)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Create(mysqlObject)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1beta1.CronJob)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Save(mysqlObject)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item CronJob
|
||||
object := obj.(*v1beta1.CronJob)
|
||||
db.Where("name=? And namespace=?", object.Name, object.Namespace).Find(&item)
|
||||
db.Delete(item)
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *CronJobCtl) CountWithConditions(conditions string) int {
|
||||
var object CronJob
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *CronJobCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []CronJob
|
||||
var object CronJob
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "lastScheduleTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *CronJobCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
func (ctl *DaemonsetCtl) generateObject(item v1.DaemonSet) *Daemonset {
|
||||
var app, status, displayName string
|
||||
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
name := item.Name
|
||||
namespace := item.Namespace
|
||||
availablePodNum := item.Status.NumberAvailable
|
||||
desirePodNum := item.Status.DesiredNumberScheduled
|
||||
createTime := item.CreationTimestamp.Time
|
||||
release := item.ObjectMeta.Labels["release"]
|
||||
nodeSelector := item.Spec.Template.Spec.NodeSelector
|
||||
nodeSelectorStr, _ := json.Marshal(nodeSelector)
|
||||
|
||||
chart := item.ObjectMeta.Labels["chart"]
|
||||
|
||||
if len(release) > 0 && len(chart) > 0 {
|
||||
app = release + "/" + chart
|
||||
}
|
||||
|
||||
if createTime.IsZero() {
|
||||
createTime = time.Now()
|
||||
}
|
||||
|
||||
if availablePodNum >= desirePodNum {
|
||||
status = Running
|
||||
} else {
|
||||
status = Updating
|
||||
}
|
||||
|
||||
object := &Daemonset{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
Available: availablePodNum,
|
||||
Desire: desirePodNum,
|
||||
App: app,
|
||||
CreateTime: createTime,
|
||||
Status: status,
|
||||
NodeSelector: string(nodeSelectorStr),
|
||||
Annotation: MapString{item.Annotations},
|
||||
Labels: MapString{item.Spec.Selector.MatchLabels},
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *DaemonsetCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *DaemonsetCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&Daemonset{}) {
|
||||
db.DropTable(&Daemonset{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&Daemonset{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
db.Create(obj)
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *DaemonsetCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *DaemonsetCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
ctl.lister = informerFactory.Apps().V1().DaemonSets().Lister()
|
||||
|
||||
informer := informerFactory.Apps().V1().DaemonSets().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1.DaemonSet)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Create(mysqlObject)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.DaemonSet)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Save(mysqlObject)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item Daemonset
|
||||
object := obj.(*v1.DaemonSet)
|
||||
db.Where("name=? And namespace=?", object.Name, object.Namespace).Find(&item)
|
||||
db.Delete(item)
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *DaemonsetCtl) CountWithConditions(conditions string) int {
|
||||
var object Daemonset
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *DaemonsetCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []Daemonset
|
||||
var object Daemonset
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *DaemonsetCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
func (ctl *DeploymentCtl) generateObject(item v1.Deployment) *Deployment {
|
||||
var app, status, displayName string
|
||||
var updateTime time.Time
|
||||
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
|
||||
name := item.Name
|
||||
namespace := item.Namespace
|
||||
availablePodNum := item.Status.AvailableReplicas
|
||||
desirePodNum := *item.Spec.Replicas
|
||||
release := item.ObjectMeta.Labels["release"]
|
||||
chart := item.ObjectMeta.Labels["chart"]
|
||||
|
||||
if len(release) > 0 && len(chart) > 0 {
|
||||
app = release + "/" + chart
|
||||
}
|
||||
|
||||
for _, condition := range item.Status.Conditions {
|
||||
if updateTime.IsZero() {
|
||||
updateTime = condition.LastUpdateTime.Time
|
||||
} else {
|
||||
if updateTime.Before(condition.LastUpdateTime.Time) {
|
||||
updateTime = condition.LastUpdateTime.Time
|
||||
}
|
||||
}
|
||||
}
|
||||
if updateTime.IsZero() {
|
||||
updateTime = time.Now()
|
||||
}
|
||||
|
||||
if item.Annotations["state"] == "stop" {
|
||||
status = Stopped
|
||||
} else {
|
||||
if availablePodNum >= desirePodNum {
|
||||
status = Running
|
||||
} else {
|
||||
status = Updating
|
||||
}
|
||||
}
|
||||
|
||||
return &Deployment{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
Available: availablePodNum,
|
||||
Desire: desirePodNum,
|
||||
App: app,
|
||||
UpdateTime: updateTime,
|
||||
Status: status,
|
||||
Annotation: MapString{item.Annotations},
|
||||
Labels: MapString{item.Spec.Selector.MatchLabels},
|
||||
DisplayName: displayName,
|
||||
}
|
||||
}
|
||||
|
||||
func (ctl *DeploymentCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *DeploymentCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
if db.HasTable(&Deployment{}) {
|
||||
db.DropTable(&Deployment{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&Deployment{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
db.Create(obj)
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *DeploymentCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", ctl.Name(), err)
|
||||
return 0
|
||||
}
|
||||
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *DeploymentCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
|
||||
ctl.lister = informerFactory.Apps().V1().Deployments().Lister()
|
||||
|
||||
informer := informerFactory.Apps().V1().Deployments().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1.Deployment)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Create(mysqlObject)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.Deployment)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Save(mysqlObject)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var deploy Deployment
|
||||
object := obj.(*v1.Deployment)
|
||||
db.Where("name=? And namespace=?", object.Name, object.Namespace).Find(&deploy)
|
||||
db.Delete(deploy)
|
||||
|
||||
},
|
||||
})
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *DeploymentCtl) CountWithConditions(conditions string) int {
|
||||
var object Deployment
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *DeploymentCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []Deployment
|
||||
var object Deployment
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "updateTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *DeploymentCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/extensions/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
func (ctl *IngressCtl) generateObject(item v1beta1.Ingress) *Ingress {
|
||||
|
||||
var ip, tls, displayName string
|
||||
|
||||
name := item.Name
|
||||
namespace := item.Namespace
|
||||
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
|
||||
createTime := item.CreationTimestamp.Time
|
||||
if createTime.IsZero() {
|
||||
createTime = time.Now()
|
||||
}
|
||||
|
||||
var ipList []string
|
||||
for _, lb := range item.Status.LoadBalancer.Ingress {
|
||||
if len(lb.IP) > 0 {
|
||||
ipList = append(ipList, lb.IP)
|
||||
}
|
||||
}
|
||||
if len(ipList) > 0 {
|
||||
ip = strings.Join(ipList, ",")
|
||||
}
|
||||
|
||||
var ingRules []ingressRule
|
||||
for _, rule := range item.Spec.Rules {
|
||||
host := rule.Host
|
||||
for _, path := range rule.HTTP.Paths {
|
||||
var ingRule ingressRule
|
||||
ingRule.Host = host
|
||||
ingRule.Service = path.Backend.ServiceName
|
||||
ingRule.Port = path.Backend.ServicePort.IntVal
|
||||
ingRule.Path = path.Path
|
||||
ingRules = append(ingRules, ingRule)
|
||||
}
|
||||
}
|
||||
|
||||
ruleStr, _ := json.Marshal(ingRules)
|
||||
|
||||
object := &Ingress{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
TlsTermination: tls,
|
||||
Ip: ip,
|
||||
CreateTime: createTime,
|
||||
Annotation: MapString{item.Annotations},
|
||||
Rules: string(ruleStr),
|
||||
Labels: MapString{item.Labels},
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *IngressCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *IngressCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&Ingress{}) {
|
||||
db.DropTable(&Ingress{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&Ingress{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
db.Create(obj)
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *IngressCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *IngressCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
|
||||
ctl.lister = informerFactory.Extensions().V1beta1().Ingresses().Lister()
|
||||
|
||||
informer := informerFactory.Extensions().V1beta1().Ingresses().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1beta1.Ingress)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Create(mysqlObject)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1beta1.Ingress)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Save(mysqlObject)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item Ingress
|
||||
object := obj.(*v1beta1.Ingress)
|
||||
db.Where("name=? And namespace=?", object.Name, object.Namespace).Find(&item)
|
||||
db.Delete(item)
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *IngressCtl) CountWithConditions(conditions string) int {
|
||||
var object Ingress
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *IngressCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []Ingress
|
||||
var object Ingress
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *IngressCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/batch/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/client"
|
||||
)
|
||||
|
||||
var k8sClient *kubernetes.Clientset
|
||||
|
||||
const retryTimes = 3
|
||||
|
||||
func (ctl *JobCtl) generateObject(item v1.Job) *Job {
|
||||
var status, displayName string
|
||||
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
|
||||
name := item.Name
|
||||
namespace := item.Namespace
|
||||
succeedPodNum := item.Status.Succeeded
|
||||
desirePodNum := *item.Spec.Completions
|
||||
createTime := item.CreationTimestamp.Time
|
||||
updteTime := createTime
|
||||
for _, condition := range item.Status.Conditions {
|
||||
if condition.Type == "Failed" && condition.Status == "True" {
|
||||
status = Failed
|
||||
}
|
||||
|
||||
if condition.Type == "Complete" && condition.Status == "True" {
|
||||
status = Completed
|
||||
}
|
||||
|
||||
if updteTime.Before(condition.LastProbeTime.Time) {
|
||||
updteTime = condition.LastProbeTime.Time
|
||||
}
|
||||
|
||||
if updteTime.Before(condition.LastTransitionTime.Time) {
|
||||
updteTime = condition.LastTransitionTime.Time
|
||||
}
|
||||
}
|
||||
|
||||
if desirePodNum > succeedPodNum && len(status) == 0 {
|
||||
status = Running
|
||||
}
|
||||
|
||||
object := &Job{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
Desire: desirePodNum,
|
||||
Completed: succeedPodNum,
|
||||
UpdateTime: updteTime,
|
||||
CreateTime: createTime,
|
||||
Status: status,
|
||||
Annotation: MapString{item.Annotations},
|
||||
Labels: MapString{item.Labels},
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *JobCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *JobCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&Job{}) {
|
||||
db.DropTable(&Job{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&Job{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
db.Create(obj)
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *JobCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *JobCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
ctl.lister = informerFactory.Batch().V1().Jobs().Lister()
|
||||
|
||||
informer := informerFactory.Batch().V1().Jobs().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1.Job)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
ctl.makeRevision(object)
|
||||
db.Create(mysqlObject)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.Job)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
ctl.makeRevision(object)
|
||||
db.Save(mysqlObject)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item Job
|
||||
object := obj.(*v1.Job)
|
||||
db.Where("name=? And namespace=?", object.Name, object.Namespace).Find(&item)
|
||||
db.Delete(item)
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *JobCtl) CountWithConditions(conditions string) int {
|
||||
var object Job
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *JobCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []Job
|
||||
var object Job
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "updateTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *JobCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
|
||||
func getRevisions(job v1.Job) (JobRevisions, error) {
|
||||
revisions := make(JobRevisions)
|
||||
|
||||
if _, exist := job.Annotations["revisions"]; exist {
|
||||
revisionsStr := job.Annotations["revisions"]
|
||||
|
||||
err := json.Unmarshal([]byte(revisionsStr), &revisions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get job %s's revisions, reason: %s", job.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return revisions, nil
|
||||
}
|
||||
|
||||
func getCurrentRevision(item *v1.Job) JobRevision {
|
||||
var revision JobRevision
|
||||
for _, condition := range item.Status.Conditions {
|
||||
if condition.Type == "Failed" && condition.Status == "True" {
|
||||
revision.Status = Failed
|
||||
revision.Reasons = append(revision.Reasons, condition.Reason)
|
||||
revision.Messages = append(revision.Messages, condition.Message)
|
||||
}
|
||||
|
||||
if condition.Type == "Complete" && condition.Status == "True" {
|
||||
revision.Status = Completed
|
||||
}
|
||||
}
|
||||
|
||||
if len(revision.Status) == 0 {
|
||||
revision.Status = Running
|
||||
}
|
||||
|
||||
revision.DesirePodNum = *item.Spec.Completions
|
||||
revision.Succeed = item.Status.Succeeded
|
||||
revision.Failed = item.Status.Failed
|
||||
revision.StartTime = item.CreationTimestamp.Time
|
||||
revision.Uid = string(item.UID)
|
||||
if item.Status.CompletionTime != nil {
|
||||
revision.CompletionTime = item.Status.CompletionTime.Time
|
||||
}
|
||||
|
||||
return revision
|
||||
}
|
||||
|
||||
func deleteJob(namespace, job string) error {
|
||||
deletePolicy := metav1.DeletePropagationBackground
|
||||
err := k8sClient.BatchV1().Jobs(namespace).Delete(job, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy})
|
||||
return err
|
||||
}
|
||||
|
||||
func (ctl *JobCtl) makeRevision(job *v1.Job) {
|
||||
revisionIndex := -1
|
||||
revisions, err := getRevisions(*job)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
uid := job.UID
|
||||
for index, revision := range revisions {
|
||||
if revision.Uid == string(uid) {
|
||||
currentRevision := getCurrentRevision(job)
|
||||
if reflect.DeepEqual(currentRevision, revision) {
|
||||
return
|
||||
} else {
|
||||
revisionIndex = index
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if revisionIndex == -1 {
|
||||
revisionIndex = len(revisions) + 1
|
||||
}
|
||||
|
||||
revisions[revisionIndex] = getCurrentRevision(job)
|
||||
|
||||
revisionsByte, err := json.Marshal(revisions)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
||||
if job.Annotations == nil {
|
||||
job.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
job.Annotations["revisions"] = string(revisionsByte)
|
||||
ctl.K8sClient.BatchV1().Jobs(job.Namespace).Update(job)
|
||||
|
||||
}
|
||||
|
||||
func JobReRun(namespace, jobName string) (string, error) {
|
||||
k8sClient = client.NewK8sClient()
|
||||
job, err := k8sClient.BatchV1().Jobs(namespace).Get(jobName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
newJob := *job
|
||||
newJob.ResourceVersion = ""
|
||||
newJob.Status = v1.JobStatus{}
|
||||
newJob.ObjectMeta.UID = ""
|
||||
newJob.Annotations["revisions"] = strings.Replace(job.Annotations["revisions"], Running, Unfinished, -1)
|
||||
|
||||
delete(newJob.Spec.Selector.MatchLabels, "controller-uid")
|
||||
delete(newJob.Spec.Template.ObjectMeta.Labels, "controller-uid")
|
||||
|
||||
err = deleteJob(namespace, jobName)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to rerun job %s, reason: %s", jobName, err)
|
||||
return "", fmt.Errorf("failed to rerun job %s", jobName)
|
||||
}
|
||||
|
||||
for i := 0; i < retryTimes; i++ {
|
||||
_, err = k8sClient.BatchV1().Jobs(namespace).Create(&newJob)
|
||||
if err != nil {
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
glog.Errorf("failed to rerun job %s, reason: %s", jobName, err)
|
||||
return "", fmt.Errorf("failed to rerun job %s", jobName)
|
||||
}
|
||||
|
||||
return "succeed", nil
|
||||
}
|
||||
@@ -1,535 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
|
||||
"k8s.io/client-go/informers"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/client"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/options"
|
||||
)
|
||||
|
||||
const (
|
||||
provider = "kubernetes"
|
||||
admin = "admin"
|
||||
operator = "operator"
|
||||
viewer = "viewer"
|
||||
kubectlNamespace = constants.KubeSphereControlNamespace
|
||||
kubectlConfigKey = "config"
|
||||
openPitrixRuntimeAnnotateKey = "openpitrix_runtime"
|
||||
creatorAnnotateKey = "creator"
|
||||
initTimeAnnotateKey = "kubesphere.io/init-time"
|
||||
workspaceLabelKey = "kubesphere.io/workspace"
|
||||
)
|
||||
|
||||
var adminRules = []rbac.PolicyRule{{Verbs: []string{"*"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}
|
||||
var editorRules = []rbac.PolicyRule{{Verbs: []string{"*"}, APIGroups: []string{"", "apps", "extensions", "batch", "kubesphere.io", "account.kubesphere.io"}, Resources: []string{"*"}}}
|
||||
var viewerRules = []rbac.PolicyRule{{Verbs: []string{"list", "get", "watch"}, APIGroups: []string{"", "apps", "extensions", "batch", "kubesphere.io", "account.kubesphere.io"}, Resources: []string{"*"}}}
|
||||
|
||||
type runTime struct {
|
||||
RuntimeId string `json:"runtime_id"`
|
||||
RuntimeUrl string `json:"runtime_url"`
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
Zone string `json:"zone"`
|
||||
RuntimeCredential string `json:"runtime_credential"`
|
||||
}
|
||||
|
||||
type DeleteRunTime struct {
|
||||
RuntimeId []string `json:"runtime_id"`
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) getKubeConfig(user string) (string, error) {
|
||||
k8sClient := client.NewK8sClient()
|
||||
configmap, err := k8sClient.CoreV1().ConfigMaps(kubectlNamespace).Get(user, metaV1.GetOptions{})
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
return "", err
|
||||
}
|
||||
return configmap.Data[kubectlConfigKey], nil
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) deleteOpRuntime(item v1.Namespace) {
|
||||
|
||||
var runtimeId string
|
||||
if item.Annotations == nil {
|
||||
runtimeId = ""
|
||||
} else {
|
||||
runtimeId = item.Annotations[openPitrixRuntimeAnnotateKey]
|
||||
}
|
||||
|
||||
if len(runtimeId) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
url := options.ServerOptions.GetOpAddress() + "/v1/runtimes"
|
||||
|
||||
var deleteRuntime = DeleteRunTime{RuntimeId: []string{runtimeId}}
|
||||
|
||||
body, err := json.Marshal(deleteRuntime)
|
||||
if err != nil {
|
||||
glog.Error("runtime release failed:", item.Name, runtimeId, err)
|
||||
return
|
||||
}
|
||||
|
||||
glog.Info("runtime release succeeded:", item.Name, runtimeId)
|
||||
|
||||
// todo: if delete failed, what's to be done?
|
||||
makeHttpRequest("DELETE", url, string(body))
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) createOpRuntime(namespace string) ([]byte, error) {
|
||||
zone := namespace
|
||||
name := namespace
|
||||
kubeConfig, err := ctl.getKubeConfig("admin")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := options.ServerOptions.GetOpAddress() + "/v1/runtimes"
|
||||
|
||||
option := runTime{Name: name, Provider: provider, RuntimeCredential: kubeConfig, Zone: zone}
|
||||
body, err := json.Marshal(option)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return makeHttpRequest("POST", url, string(body))
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) updateSystemRoleBindings(namespace *v1.Namespace) error {
|
||||
|
||||
workspace := ""
|
||||
|
||||
if namespace.Labels != nil {
|
||||
workspace = namespace.Labels[workspaceLabelKey]
|
||||
}
|
||||
|
||||
adminBinding, err := ctl.K8sClient.RbacV1().RoleBindings(namespace.Name).Get(admin, metaV1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
adminBinding = new(rbac.RoleBinding)
|
||||
adminBinding.Name = admin
|
||||
adminBinding.Namespace = namespace.Name
|
||||
adminBinding.RoleRef = rbac.RoleRef{Kind: "Role", Name: admin}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
adminBinding.Subjects = make([]rbac.Subject, 0)
|
||||
|
||||
if workspace != "" {
|
||||
workspaceAdmin, err := ctl.K8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:%s", workspace, constants.WorkspaceAdmin), metaV1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
adminBinding.Subjects = append(adminBinding.Subjects, workspaceAdmin.Subjects...)
|
||||
}
|
||||
|
||||
if adminBinding.ResourceVersion == "" {
|
||||
_, err = ctl.K8sClient.RbacV1().RoleBindings(namespace.Name).Create(adminBinding)
|
||||
} else {
|
||||
_, err = ctl.K8sClient.RbacV1().RoleBindings(namespace.Name).Update(adminBinding)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
viewerBinding, err := ctl.K8sClient.RbacV1().RoleBindings(namespace.Name).Get(viewer, metaV1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
viewerBinding = new(rbac.RoleBinding)
|
||||
viewerBinding.Name = viewer
|
||||
viewerBinding.Namespace = namespace.Name
|
||||
viewerBinding.RoleRef = rbac.RoleRef{Kind: "Role", Name: viewer}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
viewerBinding.Subjects = make([]rbac.Subject, 0)
|
||||
|
||||
if workspace != "" {
|
||||
workspaceViewer, err := ctl.K8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:%s", workspace, constants.WorkspaceViewer), metaV1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
viewerBinding.Subjects = append(viewerBinding.Subjects, workspaceViewer.Subjects...)
|
||||
}
|
||||
|
||||
if viewerBinding.ResourceVersion == "" {
|
||||
_, err = ctl.K8sClient.RbacV1().RoleBindings(namespace.Name).Create(viewerBinding)
|
||||
} else {
|
||||
_, err = ctl.K8sClient.RbacV1().RoleBindings(namespace.Name).Update(viewerBinding)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) createDefaultRoleBinding(namespace *v1.Namespace) error {
|
||||
creator := ""
|
||||
if namespace.Annotations != nil {
|
||||
creator = namespace.Annotations[creatorAnnotateKey]
|
||||
}
|
||||
// create once
|
||||
if creator != "" {
|
||||
creatorBindingName := fmt.Sprintf("%s-admin", creator)
|
||||
creatorBinding, err := ctl.K8sClient.RbacV1().RoleBindings(namespace.Name).Get(creatorBindingName, metaV1.GetOptions{})
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
creatorBinding = new(rbac.RoleBinding)
|
||||
creatorBinding.Name = creatorBindingName
|
||||
creatorBinding.Namespace = namespace.Name
|
||||
creatorBinding.RoleRef = rbac.RoleRef{Kind: "Role", Name: admin}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
creatorBinding.Subjects = []rbac.Subject{{Kind: rbac.UserKind, Name: creator}}
|
||||
|
||||
if creatorBinding.ResourceVersion == "" {
|
||||
_, err = ctl.K8sClient.RbacV1().RoleBindings(namespace.Name).Create(creatorBinding)
|
||||
} else {
|
||||
_, err = ctl.K8sClient.RbacV1().RoleBindings(namespace.Name).Update(creatorBinding)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) CreateDefaultRoleAndRoleBinding(namespace *v1.Namespace) error {
|
||||
adminRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: admin, Namespace: namespace.Name, Annotations: map[string]string{creatorAnnotateKey: "system"}}, Rules: adminRules}
|
||||
operatorRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: operator, Namespace: namespace.Name, Annotations: map[string]string{creatorAnnotateKey: "system"}}, Rules: editorRules}
|
||||
viewerRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: viewer, Namespace: namespace.Name, Annotations: map[string]string{creatorAnnotateKey: "system"}}, Rules: viewerRules}
|
||||
|
||||
_, err := ctl.K8sClient.RbacV1().Roles(namespace.Name).Create(adminRole)
|
||||
|
||||
if err != nil && !errors.IsAlreadyExists(err) {
|
||||
return err
|
||||
} else if err == nil {
|
||||
if err := ctl.createDefaultRoleBinding(namespace); err != nil {
|
||||
glog.Warning("default role binding create failed", namespace.Name)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = ctl.K8sClient.RbacV1().Roles(namespace.Name).Create(operatorRole)
|
||||
|
||||
if err != nil && !errors.IsAlreadyExists(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = ctl.K8sClient.RbacV1().Roles(namespace.Name).Create(viewerRole)
|
||||
|
||||
if err != nil && !errors.IsAlreadyExists(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) createRoleAndRuntime(namespace *v1.Namespace) {
|
||||
runtime := ""
|
||||
initTime := ""
|
||||
if namespace.Annotations != nil {
|
||||
runtime = namespace.Annotations[openPitrixRuntimeAnnotateKey]
|
||||
initTime = namespace.Annotations[initTimeAnnotateKey]
|
||||
}
|
||||
|
||||
componentsNamespaces := []string{constants.KubeSystemNamespace, constants.OpenPitrixNamespace, constants.IstioNamespace, constants.KubeSphereNamespace}
|
||||
|
||||
if runtime == "" && !slice.ContainsString(componentsNamespaces, namespace.Name, nil) {
|
||||
_, runtimeCreateError := ctl.createOpRuntime(namespace.Name)
|
||||
if runtimeCreateError != nil {
|
||||
glog.Error("runtime create error:", runtimeCreateError)
|
||||
}
|
||||
}
|
||||
|
||||
if initTime == "" {
|
||||
err := ctl.CreateDefaultRoleAndRoleBinding(namespace)
|
||||
if err == nil {
|
||||
err = ctl.updateSystemRoleBindings(namespace)
|
||||
if err != nil {
|
||||
glog.Error("role binding update error:", err)
|
||||
}
|
||||
} else {
|
||||
glog.Error("default role create error:", err)
|
||||
}
|
||||
if err == nil {
|
||||
pathJson := fmt.Sprintf(`{"metadata":{"annotations":{"%s":"%s"}}}`, initTimeAnnotateKey, time.Now().UTC().Format("2006-01-02T15:04:05Z"))
|
||||
_, err = ctl.K8sClient.CoreV1().Namespaces().Patch(namespace.Name, "application/strategic-merge-patch+json", []byte(pathJson))
|
||||
if err != nil {
|
||||
glog.Error("annotations patch error init failed:", namespace.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) createCephSecretAfterNewNs(item v1.Namespace) {
|
||||
// Kubernetes version must < 1.11.0
|
||||
verInfo, err := ctl.K8sClient.ServerVersion()
|
||||
if err != nil {
|
||||
glog.Error("consult k8s server error: ", err)
|
||||
return
|
||||
}
|
||||
if !utilversion.MustParseSemantic(verInfo.String()).LessThan(utilversion.MustParseSemantic("v1.11.0")) {
|
||||
glog.Infof("disable Ceph secret controller due to k8s version %s >= v1.11.0", verInfo.String())
|
||||
return
|
||||
}
|
||||
|
||||
// Create Ceph secret in the new namespace
|
||||
newNsName := item.Name
|
||||
scList, _ := ctl.K8sClient.StorageV1().StorageClasses().List(metaV1.ListOptions{})
|
||||
if scList == nil {
|
||||
return
|
||||
}
|
||||
for _, sc := range scList.Items {
|
||||
if sc.Provisioner == rbdPluginName {
|
||||
glog.Infof("would create Ceph user secret in storage class %s at namespace %s", sc.GetName(), newNsName)
|
||||
if secretName, ok := sc.Parameters[rbdUserSecretNameKey]; ok {
|
||||
secret, err := ctl.K8sClient.CoreV1().Secrets(core.NamespaceSystem).Get(secretName, metaV1.GetOptions{})
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
glog.Errorf("cannot find secret in namespace %s, error: %s", core.NamespaceSystem, err.Error())
|
||||
continue
|
||||
}
|
||||
glog.Errorf("failed to find secret in namespace %s, error: %s", core.NamespaceSystem, err.Error())
|
||||
continue
|
||||
}
|
||||
glog.Infof("succeed to find secret %s in namespace %s", secret.GetName(), secret.GetNamespace())
|
||||
|
||||
newSecret := &v1.Secret{
|
||||
TypeMeta: metaV1.TypeMeta{
|
||||
Kind: secret.Kind,
|
||||
APIVersion: secret.APIVersion,
|
||||
},
|
||||
ObjectMeta: metaV1.ObjectMeta{
|
||||
Name: secret.GetName(),
|
||||
Namespace: newNsName,
|
||||
Labels: secret.GetLabels(),
|
||||
Annotations: secret.GetAnnotations(),
|
||||
DeletionGracePeriodSeconds: secret.GetDeletionGracePeriodSeconds(),
|
||||
ClusterName: secret.GetClusterName(),
|
||||
},
|
||||
Data: secret.Data,
|
||||
StringData: secret.StringData,
|
||||
Type: secret.Type,
|
||||
}
|
||||
glog.Infof("creating secret %s in namespace %s...", newSecret.GetName(), newSecret.GetNamespace())
|
||||
_, err = ctl.K8sClient.CoreV1().Secrets(newSecret.GetNamespace()).Create(newSecret)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to create secret in namespace %s, error: %v", newSecret.GetNamespace(), err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
glog.Errorf("failed to find user secret name in storage class %s", sc.GetName())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) generateObject(item *v1.Namespace) *Namespace {
|
||||
var displayName string
|
||||
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
|
||||
name := item.Name
|
||||
createTime := item.CreationTimestamp.Time
|
||||
status := fmt.Sprintf("%v", item.Status.Phase)
|
||||
|
||||
if createTime.IsZero() {
|
||||
createTime = time.Now()
|
||||
}
|
||||
|
||||
object := &Namespace{
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
CreateTime: createTime,
|
||||
Status: status,
|
||||
Annotation: MapString{item.Annotations},
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&Namespace{}) {
|
||||
db.DropTable(&Namespace{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&Namespace{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
//list, err := ctl.lister.List(labels.Everything())
|
||||
//if err != nil {
|
||||
// glog.Error(err)
|
||||
// return
|
||||
//}
|
||||
|
||||
//for _, item := range list {
|
||||
// obj := ctl.generateObject(item)
|
||||
// db.Create(obj)
|
||||
// ctl.createRoleAndRuntime(item)
|
||||
//}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
|
||||
ctl.lister = informerFactory.Core().V1().Namespaces().Lister()
|
||||
|
||||
informer := informerFactory.Core().V1().Namespaces().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1.Namespace)
|
||||
mysqlObject := ctl.generateObject(object)
|
||||
db.Create(mysqlObject)
|
||||
ctl.createRoleAndRuntime(object)
|
||||
ctl.createCephSecretAfterNewNs(*object)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.Namespace)
|
||||
mysqlObject := ctl.generateObject(object)
|
||||
db.Save(mysqlObject)
|
||||
ctl.createRoleAndRuntime(object)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item Namespace
|
||||
object := obj.(*v1.Namespace)
|
||||
db.Where("name=?", object.Name).Find(&item)
|
||||
db.Delete(item)
|
||||
ctl.deleteOpRuntime(*object)
|
||||
},
|
||||
})
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) CountWithConditions(conditions string) int {
|
||||
var object Namespace
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []Namespace
|
||||
var object Namespace
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
if paging != nil {
|
||||
for index := range list {
|
||||
usage, err := ctl.GetNamespaceQuota(list[index].Name)
|
||||
if err == nil {
|
||||
list[index].Usage = usage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func getUsage(namespace, resource string) int {
|
||||
ctl := ResourceControllers.Controllers[resource]
|
||||
return ctl.CountWithConditions(fmt.Sprintf("namespace = '%s' ", namespace))
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) GetNamespaceQuota(namespace string) (v1.ResourceList, error) {
|
||||
|
||||
usage := make(v1.ResourceList)
|
||||
|
||||
resourceList := []string{Daemonsets, Deployments, Ingresses, Roles, Services, Statefulsets, PersistentVolumeClaim, Pods, Jobs, Cronjobs}
|
||||
for _, resourceName := range resourceList {
|
||||
used := getUsage(namespace, resourceName)
|
||||
var quantity resource.Quantity
|
||||
quantity.Set(int64(used))
|
||||
usage[v1.ResourceName(resourceName)] = quantity
|
||||
}
|
||||
|
||||
podCtl := ResourceControllers.Controllers[Pods]
|
||||
var quantity resource.Quantity
|
||||
used := podCtl.CountWithConditions(fmt.Sprintf("status=\"%s\" And namespace=\"%s\"", "Running", namespace))
|
||||
quantity.Set(int64(used))
|
||||
usage["runningPods"] = quantity
|
||||
return usage, nil
|
||||
}
|
||||
|
||||
func (ctl *NamespaceCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
const NodeRoleLabel = "node-role.kubernetes.io/"
|
||||
|
||||
func (ctl *NodeCtl) generateObject(item v1.Node) *Node {
|
||||
var status, ip, displayName, msgStr string
|
||||
var msg, role []string
|
||||
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
|
||||
name := item.Name
|
||||
createTime := item.ObjectMeta.CreationTimestamp.Time
|
||||
annotation := item.Annotations
|
||||
|
||||
// in case of multiple roles
|
||||
for label, _ := range item.Labels {
|
||||
if strings.HasPrefix(label, NodeRoleLabel) {
|
||||
if parts := strings.Split(label, "/"); len(parts) == 2 {
|
||||
role = append(role, parts[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, condition := range item.Status.Conditions {
|
||||
if condition.Type == "Ready" {
|
||||
if condition.Status == "True" {
|
||||
status = Running
|
||||
} else {
|
||||
status = Error
|
||||
}
|
||||
|
||||
} else {
|
||||
if condition.Status == "True" {
|
||||
msg = append(msg, condition.Reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(msg) > 0 {
|
||||
msgStr = strings.Join(msg, ",")
|
||||
if status == Running {
|
||||
status = Warning
|
||||
}
|
||||
}
|
||||
|
||||
for _, address := range item.Status.Addresses {
|
||||
if address.Type == "InternalIP" {
|
||||
ip = address.Address
|
||||
}
|
||||
}
|
||||
|
||||
object := &Node{
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
Ip: ip,
|
||||
Status: status,
|
||||
CreateTime: createTime,
|
||||
Annotation: MapString{annotation},
|
||||
Taints: Taints{item.Spec.Taints},
|
||||
Msg: msgStr,
|
||||
Role: strings.Join(role, ","),
|
||||
Labels: MapString{item.Labels}}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *NodeCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *NodeCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&Node{}) {
|
||||
db.DropTable(&Node{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&Node{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
db.Create(obj)
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *NodeCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s failed, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *NodeCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
|
||||
ctl.lister = informerFactory.Core().V1().Nodes().Lister()
|
||||
|
||||
informer := informerFactory.Core().V1().Nodes().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
object := obj.(*v1.Node)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Create(mysqlObject)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.Node)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Save(mysqlObject)
|
||||
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item Node
|
||||
object := obj.(*v1.Node)
|
||||
db.Where("name=? ", object.Name, object.Namespace).Find(&item)
|
||||
db.Delete(item)
|
||||
},
|
||||
})
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *NodeCtl) CountWithConditions(conditions string) int {
|
||||
var object Node
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *NodeCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []Node
|
||||
var object Node
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *NodeCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
const inUse = "kubesphere.io/in_use_pods"
|
||||
|
||||
func (ctl *PodCtl) addAnnotationToPvc(item v1.Pod) {
|
||||
volumes := item.Spec.Volumes
|
||||
for _, volume := range volumes {
|
||||
pvc := volume.PersistentVolumeClaim
|
||||
if pvc != nil {
|
||||
name := pvc.ClaimName
|
||||
|
||||
Pvc, _ := ctl.K8sClient.CoreV1().PersistentVolumeClaims(item.Namespace).Get(name, metaV1.GetOptions{})
|
||||
if Pvc.Annotations == nil {
|
||||
Pvc.Annotations = make(map[string]string)
|
||||
}
|
||||
annotation := Pvc.Annotations
|
||||
|
||||
if len(annotation[inUse]) == 0 {
|
||||
pods := []string{item.Name}
|
||||
str, _ := json.Marshal(pods)
|
||||
annotation[inUse] = string(str)
|
||||
} else {
|
||||
var pods []string
|
||||
json.Unmarshal([]byte(annotation[inUse]), &pods)
|
||||
for _, pod := range pods {
|
||||
if pod == item.Name {
|
||||
return
|
||||
}
|
||||
}
|
||||
pods = append(pods, item.Name)
|
||||
str, _ := json.Marshal(pods)
|
||||
annotation[inUse] = string(str)
|
||||
}
|
||||
Pvc.Annotations = annotation
|
||||
ctl.K8sClient.CoreV1().PersistentVolumeClaims(item.Namespace).Update(Pvc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ctl *PodCtl) delAnnotationFromPvc(item v1.Pod) {
|
||||
volumes := item.Spec.Volumes
|
||||
for _, volume := range volumes {
|
||||
pvc := volume.PersistentVolumeClaim
|
||||
if pvc != nil {
|
||||
name := pvc.ClaimName
|
||||
Pvc, _ := ctl.K8sClient.CoreV1().PersistentVolumeClaims(item.Namespace).Get(name, metaV1.GetOptions{})
|
||||
if Pvc.Annotations == nil {
|
||||
Pvc.Annotations = make(map[string]string)
|
||||
}
|
||||
annotation := Pvc.Annotations
|
||||
var pods []string
|
||||
json.Unmarshal([]byte(annotation[inUse]), pods)
|
||||
|
||||
for index, pod := range pods {
|
||||
if pod == item.Name {
|
||||
pods = append(pods[:index], pods[index+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
str, _ := json.Marshal(pods)
|
||||
annotation[inUse] = string(str)
|
||||
ctl.K8sClient.CoreV1().PersistentVolumeClaims(item.Namespace).Update(Pvc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getStatusAndRestartCount(pod v1.Pod) (string, int) {
|
||||
status := string(pod.Status.Phase)
|
||||
restarts := 0
|
||||
if pod.Status.Reason != "" {
|
||||
status = pod.Status.Reason
|
||||
}
|
||||
|
||||
initializing := false
|
||||
for i := range pod.Status.InitContainerStatuses {
|
||||
container := pod.Status.InitContainerStatuses[i]
|
||||
restarts += int(container.RestartCount)
|
||||
switch {
|
||||
case container.State.Terminated != nil && container.State.Terminated.ExitCode == 0:
|
||||
continue
|
||||
case container.State.Terminated != nil:
|
||||
// initialization is failed
|
||||
if len(container.State.Terminated.Reason) == 0 {
|
||||
if container.State.Terminated.Signal != 0 {
|
||||
status = fmt.Sprintf("Init:Signal:%d", container.State.Terminated.Signal)
|
||||
} else {
|
||||
status = fmt.Sprintf("Init:ExitCode:%d", container.State.Terminated.ExitCode)
|
||||
}
|
||||
} else {
|
||||
status = "Init:" + container.State.Terminated.Reason
|
||||
}
|
||||
initializing = true
|
||||
case container.State.Waiting != nil && len(container.State.Waiting.Reason) > 0 && container.State.Waiting.Reason != "PodInitializing":
|
||||
status = "Init:" + container.State.Waiting.Reason
|
||||
initializing = true
|
||||
default:
|
||||
status = fmt.Sprintf("Init:%d/%d", i, len(pod.Spec.InitContainers))
|
||||
initializing = true
|
||||
}
|
||||
break
|
||||
}
|
||||
if !initializing {
|
||||
restarts = 0
|
||||
hasRunning := false
|
||||
for i := len(pod.Status.ContainerStatuses) - 1; i >= 0; i-- {
|
||||
container := pod.Status.ContainerStatuses[i]
|
||||
|
||||
restarts += int(container.RestartCount)
|
||||
if container.State.Waiting != nil && container.State.Waiting.Reason != "" {
|
||||
status = container.State.Waiting.Reason
|
||||
} else if container.State.Terminated != nil && container.State.Terminated.Reason != "" {
|
||||
status = container.State.Terminated.Reason
|
||||
} else if container.State.Terminated != nil && container.State.Terminated.Reason == "" {
|
||||
if container.State.Terminated.Signal != 0 {
|
||||
status = fmt.Sprintf("Signal:%d", container.State.Terminated.Signal)
|
||||
} else {
|
||||
status = fmt.Sprintf("ExitCode:%d", container.State.Terminated.ExitCode)
|
||||
}
|
||||
} else if container.Ready && container.State.Running != nil {
|
||||
hasRunning = true
|
||||
}
|
||||
}
|
||||
|
||||
// change pod status back to "Running" if there is at least one container still reporting as "Running" status
|
||||
if status == "Completed" && hasRunning {
|
||||
status = "Running"
|
||||
}
|
||||
}
|
||||
|
||||
if pod.DeletionTimestamp != nil && pod.Status.Reason == "NodeLost" {
|
||||
status = "Unknown"
|
||||
} else if pod.DeletionTimestamp != nil {
|
||||
status = "Terminating"
|
||||
}
|
||||
|
||||
return status, restarts
|
||||
}
|
||||
|
||||
func (ctl *PodCtl) generateObject(item v1.Pod) *Pod {
|
||||
|
||||
var ownerKind, ownerName string
|
||||
|
||||
// For ReplicaSet,ReplicaController,DaemonSet,StatefulSet,Job,CronJob, k8s will automatically
|
||||
// set ownerReference for pods, in case of setting ownerReference manually.
|
||||
if item.OwnerReferences != nil && len(item.OwnerReferences) > 0 {
|
||||
ownerKind = item.OwnerReferences[0].Kind
|
||||
ownerName = item.OwnerReferences[0].Name
|
||||
}
|
||||
|
||||
object := &Pod{
|
||||
Namespace: item.Namespace,
|
||||
Name: item.Name,
|
||||
Node: item.Spec.NodeName,
|
||||
Status: item.Status,
|
||||
CreateTime: item.CreationTimestamp.Time,
|
||||
OwnerKind: ownerKind,
|
||||
OwnerName: ownerName,
|
||||
Spec: item.Spec,
|
||||
Metadata: item.ObjectMeta,
|
||||
Kind: item.TypeMeta.Kind,
|
||||
APIVersion: item.TypeMeta.APIVersion,
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *PodCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *PodCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&Pod{}) {
|
||||
db.DropTable(&Pod{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&Pod{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
db.Create(obj)
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *PodCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *PodCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
|
||||
ctl.lister = informerFactory.Core().V1().Pods().Lister()
|
||||
|
||||
informer := informerFactory.Core().V1().Pods().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
object := obj.(*v1.Pod)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Create(mysqlObject)
|
||||
|
||||
ctl.addAnnotationToPvc(*object)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.Pod)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
ctl.addAnnotationToPvc(*object)
|
||||
db.Save(mysqlObject)
|
||||
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item Pod
|
||||
object := obj.(*v1.Pod)
|
||||
db.Where("name=? And namespace=?", object.Name, object.Namespace).Find(&item)
|
||||
ctl.delAnnotationFromPvc(*object)
|
||||
db.Delete(item)
|
||||
},
|
||||
})
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *PodCtl) CountWithConditions(conditions string) int {
|
||||
var object Pod
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *PodCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []Pod
|
||||
var object Pod
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *PodCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
func (ctl *PvcCtl) generateObject(item *v1.PersistentVolumeClaim) *Pvc {
|
||||
var displayName string
|
||||
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
|
||||
name := item.Name
|
||||
namespace := item.Namespace
|
||||
createTime := item.CreationTimestamp.Time
|
||||
status := fmt.Sprintf("%s", item.Status.Phase)
|
||||
if item.DeletionTimestamp != nil {
|
||||
status = "Terminating"
|
||||
}
|
||||
|
||||
var capacity, storageClass, accessModeStr string
|
||||
|
||||
if createTime.IsZero() {
|
||||
createTime = time.Now()
|
||||
}
|
||||
|
||||
if storage, exist := item.Status.Capacity["storage"]; exist {
|
||||
capacity = storage.String()
|
||||
}
|
||||
|
||||
if len(item.Annotations["volume.beta.kubernetes.io/storage-class"]) > 0 {
|
||||
storageClass = item.Annotations["volume.beta.kubernetes.io/storage-class"]
|
||||
}
|
||||
if item.Spec.StorageClassName != nil {
|
||||
storageClass = *item.Spec.StorageClassName
|
||||
}
|
||||
|
||||
var accessModeList []string
|
||||
for _, accessMode := range item.Status.AccessModes {
|
||||
accessModeList = append(accessModeList, string(accessMode))
|
||||
}
|
||||
|
||||
accessModeStr = strings.Join(accessModeList, ",")
|
||||
|
||||
object := &Pvc{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
Status: status,
|
||||
Capacity: capacity,
|
||||
AccessMode: accessModeStr,
|
||||
StorageClassName: storageClass,
|
||||
CreateTime: createTime,
|
||||
Annotation: MapString{item.Annotations},
|
||||
Labels: MapString{item.Labels},
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *PvcCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *PvcCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&Pvc{}) {
|
||||
db.DropTable(&Pvc{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&Pvc{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(item)
|
||||
db.Create(obj)
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *PvcCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *PvcCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
|
||||
ctl.lister = informerFactory.Core().V1().PersistentVolumeClaims().Lister()
|
||||
|
||||
informer := informerFactory.Core().V1().PersistentVolumeClaims().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1.PersistentVolumeClaim)
|
||||
mysqlObject := ctl.generateObject(object)
|
||||
db.Create(mysqlObject)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.PersistentVolumeClaim)
|
||||
mysqlObject := ctl.generateObject(object)
|
||||
db.Save(mysqlObject)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item Pvc
|
||||
object := obj.(*v1.PersistentVolumeClaim)
|
||||
db.Where("name=? And namespace=?", object.Name, object.Namespace).Find(&item)
|
||||
db.Delete(item)
|
||||
},
|
||||
})
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *PvcCtl) CountWithConditions(conditions string) int {
|
||||
var object Pvc
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *PvcCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []Pvc
|
||||
var object Pvc
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
for index := range list {
|
||||
inUsePods := list[index].Annotation.Values[inUse]
|
||||
var pods []string
|
||||
|
||||
json.Unmarshal([]byte(inUsePods), &pods)
|
||||
|
||||
if len(pods) > 0 {
|
||||
list[index].InUse = true
|
||||
} else {
|
||||
list[index].InUse = false
|
||||
}
|
||||
}
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *PvcCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/informers"
|
||||
)
|
||||
|
||||
func (ctl *ReplicaSetCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *ReplicaSetCtl) sync(stopChan chan struct{}) {
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *ReplicaSetCtl) total() int {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ctl *ReplicaSetCtl) initListerAndInformer() {
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
|
||||
ctl.lister = informerFactory.Apps().V1().ReplicaSets().Lister()
|
||||
|
||||
informer := informerFactory.Apps().V1().ReplicaSets().Informer()
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *ReplicaSetCtl) CountWithConditions(conditions string) int {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ctl *ReplicaSetCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
func (ctl *ReplicaSetCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/client-go/informers"
|
||||
)
|
||||
|
||||
func (ctl *RoleBindingCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *RoleBindingCtl) sync(stopChan chan struct{}) {
|
||||
ctl.initListerAndInformer()
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *RoleBindingCtl) total() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ctl *RoleBindingCtl) initListerAndInformer() {
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
|
||||
ctl.lister = informerFactory.Rbac().V1().RoleBindings().Lister()
|
||||
ctl.informer = informerFactory.Rbac().V1().RoleBindings().Informer()
|
||||
}
|
||||
|
||||
func (ctl *RoleBindingCtl) CountWithConditions(conditions string) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ctl *RoleBindingCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
return 0, nil, errors.New("not implement")
|
||||
}
|
||||
|
||||
func (ctl *RoleBindingCtl) Lister() interface{} {
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
func (ctl *RoleCtl) generateObject(item v1.Role) *Role {
|
||||
var displayName string
|
||||
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) == 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
|
||||
name := item.Name
|
||||
if strings.HasPrefix(name, systemPrefix) || item.Annotations == nil || len(item.Annotations[creator]) == 0 {
|
||||
return nil
|
||||
}
|
||||
namespace := item.Namespace
|
||||
createTime := item.CreationTimestamp.Time
|
||||
if createTime.IsZero() {
|
||||
createTime = time.Now()
|
||||
}
|
||||
|
||||
object := &Role{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
CreateTime: createTime,
|
||||
Annotation: MapString{item.Annotations},
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *RoleCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *RoleCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&Role{}) {
|
||||
db.DropTable(&Role{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&Role{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
if obj != nil {
|
||||
db.Create(obj)
|
||||
}
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *RoleCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s failed, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
|
||||
count := 0
|
||||
for _, item := range list {
|
||||
if !strings.HasPrefix(item.Name, systemPrefix) && item.Annotations != nil && len(item.Annotations[creator]) > 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func (ctl *RoleCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
|
||||
ctl.lister = informerFactory.Rbac().V1().Roles().Lister()
|
||||
|
||||
informer := informerFactory.Rbac().V1().Roles().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1.Role)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
if mysqlObject != nil {
|
||||
db.Create(mysqlObject)
|
||||
}
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.Role)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
if mysqlObject != nil {
|
||||
db.Save(mysqlObject)
|
||||
}
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item Role
|
||||
object := obj.(*v1.Role)
|
||||
db.Where("name=? And namespace=?", object.Name, object.Namespace).Find(&item)
|
||||
db.Delete(item)
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *RoleCtl) CountWithConditions(conditions string) int {
|
||||
var object Role
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *RoleCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []Role
|
||||
var object Role
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *RoleCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/jinzhu/gorm"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/client"
|
||||
)
|
||||
|
||||
type resourceControllers struct {
|
||||
Controllers map[string]Controller
|
||||
k8sClient *kubernetes.Clientset
|
||||
}
|
||||
|
||||
var ResourceControllers resourceControllers
|
||||
|
||||
func (rec *resourceControllers) runController(name string, stopChan chan struct{}, wg *sync.WaitGroup) {
|
||||
var ctl Controller
|
||||
attr := CommonAttribute{DB: client.NewDBClient(), K8sClient: rec.k8sClient, stopChan: stopChan,
|
||||
aliveChan: make(chan struct{}), Name: name}
|
||||
switch name {
|
||||
case Deployments:
|
||||
ctl = &DeploymentCtl{CommonAttribute: attr}
|
||||
case Statefulsets:
|
||||
ctl = &StatefulsetCtl{CommonAttribute: attr}
|
||||
case Daemonsets:
|
||||
ctl = &DaemonsetCtl{CommonAttribute: attr}
|
||||
case Ingresses:
|
||||
ctl = &IngressCtl{CommonAttribute: attr}
|
||||
case PersistentVolumeClaim:
|
||||
ctl = &PvcCtl{CommonAttribute: attr}
|
||||
case Roles:
|
||||
ctl = &RoleCtl{CommonAttribute: attr}
|
||||
case ClusterRoles:
|
||||
ctl = &ClusterRoleCtl{CommonAttribute: attr}
|
||||
case Services:
|
||||
ctl = &ServiceCtl{CommonAttribute: attr}
|
||||
case Pods:
|
||||
ctl = &PodCtl{CommonAttribute: attr}
|
||||
case Namespaces:
|
||||
ctl = &NamespaceCtl{CommonAttribute: attr}
|
||||
case StorageClasses:
|
||||
ctl = &StorageClassCtl{CommonAttribute: attr}
|
||||
case Jobs:
|
||||
ctl = &JobCtl{CommonAttribute: attr}
|
||||
case Cronjobs:
|
||||
ctl = &CronJobCtl{CommonAttribute: attr}
|
||||
case Nodes:
|
||||
ctl = &NodeCtl{CommonAttribute: attr}
|
||||
case Replicasets:
|
||||
ctl = &ReplicaSetCtl{CommonAttribute: attr}
|
||||
case ControllerRevisions:
|
||||
ctl = &ControllerRevisionCtl{CommonAttribute: attr}
|
||||
case ConfigMaps:
|
||||
ctl = &ConfigMapCtl{CommonAttribute: attr}
|
||||
case Secrets:
|
||||
ctl = &SecretCtl{CommonAttribute: attr}
|
||||
case ClusterRoleBindings:
|
||||
ctl = &ClusterRoleBindingCtl{CommonAttribute: attr}
|
||||
case RoleBindings:
|
||||
ctl = &RoleBindingCtl{CommonAttribute: attr}
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
rec.Controllers[name] = ctl
|
||||
wg.Add(1)
|
||||
go listAndWatch(ctl, wg)
|
||||
|
||||
}
|
||||
|
||||
func dbHealthCheck(db *gorm.DB) {
|
||||
defer db.Close()
|
||||
|
||||
for {
|
||||
count := 0
|
||||
var err error
|
||||
for k := 0; k < 5; k++ {
|
||||
err = db.DB().Ping()
|
||||
if err != nil {
|
||||
count++
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
if count > 3 {
|
||||
syscall.Kill(os.Getpid(), syscall.SIGTERM)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Run(stopChan chan struct{}, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
k8sClient := client.NewK8sClient()
|
||||
ResourceControllers = resourceControllers{k8sClient: k8sClient, Controllers: make(map[string]Controller)}
|
||||
|
||||
for _, item := range []string{Deployments, Statefulsets, Daemonsets, PersistentVolumeClaim, Pods, Services,
|
||||
Ingresses, Roles, RoleBindings, ClusterRoles, ClusterRoleBindings, Namespaces, StorageClasses, Jobs, Cronjobs, Nodes, Replicasets,
|
||||
ControllerRevisions, ConfigMaps, Secrets} {
|
||||
ResourceControllers.runController(item, stopChan, wg)
|
||||
}
|
||||
|
||||
go dbHealthCheck(client.NewDBClient())
|
||||
|
||||
for {
|
||||
for ctlName, controller := range ResourceControllers.Controllers {
|
||||
select {
|
||||
case <-stopChan:
|
||||
return
|
||||
case _, isClose := <-controller.chanAlive():
|
||||
if !isClose {
|
||||
glog.Errorf("controller %s have stopped, restart it", ctlName)
|
||||
ResourceControllers.runController(ctlName, stopChan, wg)
|
||||
}
|
||||
default:
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetLister(controller string) (interface{}, error) {
|
||||
if ctl, ok := ResourceControllers.Controllers[controller]; ok {
|
||||
if ctl.Lister() != nil {
|
||||
return ctl.Lister(), nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New(fmt.Sprintf("lister of %s not alive", controller))
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
func (ctl *SecretCtl) generateObject(item v1.Secret) *Secret {
|
||||
var displayName string
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
|
||||
createTime := item.CreationTimestamp.Time
|
||||
if createTime.IsZero() {
|
||||
createTime = time.Now()
|
||||
}
|
||||
|
||||
object := &Secret{
|
||||
Name: item.Name,
|
||||
Namespace: item.Namespace,
|
||||
CreateTime: createTime,
|
||||
Annotation: MapString{item.Annotations},
|
||||
DisplayName: displayName,
|
||||
Entries: len(item.Data),
|
||||
Type: string(item.Type),
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *SecretCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *SecretCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&Secret{}) {
|
||||
db.DropTable(&Secret{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&Secret{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
if obj != nil {
|
||||
db.Create(obj)
|
||||
}
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *SecretCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *SecretCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
ctl.lister = informerFactory.Core().V1().Secrets().Lister()
|
||||
|
||||
informer := informerFactory.Core().V1().Secrets().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1.Secret)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
if mysqlObject != nil {
|
||||
db.Create(mysqlObject)
|
||||
}
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.Secret)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
if mysqlObject != nil {
|
||||
db.Save(mysqlObject)
|
||||
}
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item Secret
|
||||
object := obj.(*v1.Secret)
|
||||
db.Where("name=?", object.Name).Find(&item)
|
||||
db.Delete(item)
|
||||
|
||||
},
|
||||
})
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *SecretCtl) CountWithConditions(conditions string) int {
|
||||
var object Secret
|
||||
|
||||
if strings.Contains(conditions, "namespace") {
|
||||
conditions = ""
|
||||
}
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *SecretCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var object Secret
|
||||
var list []Secret
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
db := ctl.DB
|
||||
|
||||
listWithConditions(db, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *SecretCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
func loadBalancerStatusStringer(item v1.Service) string {
|
||||
ingress := item.Status.LoadBalancer.Ingress
|
||||
result := sets.NewString()
|
||||
for i := range ingress {
|
||||
if ingress[i].IP != "" {
|
||||
result.Insert(ingress[i].IP)
|
||||
} else if ingress[i].Hostname != "" {
|
||||
result.Insert(ingress[i].Hostname)
|
||||
}
|
||||
}
|
||||
|
||||
r := strings.Join(result.List(), ",")
|
||||
return r
|
||||
}
|
||||
|
||||
func getExternalIp(item v1.Service) string {
|
||||
switch item.Spec.Type {
|
||||
case "ClusterIP", "NodePort":
|
||||
if len(item.Spec.ExternalIPs) > 0 {
|
||||
return strings.Join(item.Spec.ExternalIPs, ",")
|
||||
}
|
||||
case "ExternalName":
|
||||
return item.Spec.ExternalName
|
||||
|
||||
case "LoadBalancer":
|
||||
lbIps := loadBalancerStatusStringer(item)
|
||||
if len(item.Spec.ExternalIPs) > 0 {
|
||||
results := []string{}
|
||||
if len(lbIps) > 0 {
|
||||
results = append(results, strings.Split(lbIps, ",")...)
|
||||
}
|
||||
results = append(results, item.Spec.ExternalIPs...)
|
||||
return strings.Join(results, ",")
|
||||
}
|
||||
if len(lbIps) > 0 {
|
||||
return lbIps
|
||||
}
|
||||
return "<pending>"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func generateSvcObject(item v1.Service) *Service {
|
||||
var app string
|
||||
var displayName string
|
||||
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
name := item.Name
|
||||
namespace := item.Namespace
|
||||
createTime := item.CreationTimestamp.Time
|
||||
externalIp := getExternalIp(item)
|
||||
serviceType := item.Spec.Type
|
||||
vip := item.Spec.ClusterIP
|
||||
|
||||
release := item.ObjectMeta.Labels["release"]
|
||||
chart := item.ObjectMeta.Labels["chart"]
|
||||
|
||||
if len(release) > 0 && len(chart) > 0 {
|
||||
app = release + "/" + chart
|
||||
}
|
||||
ports := ""
|
||||
var nodePorts []string
|
||||
|
||||
if createTime.IsZero() {
|
||||
createTime = time.Now()
|
||||
}
|
||||
|
||||
if len(item.Spec.ClusterIP) == 0 || item.Spec.ClusterIP == "None" {
|
||||
if len(item.Spec.Selector) != 0 {
|
||||
serviceType = "Headless(Selector)"
|
||||
}
|
||||
|
||||
if item.Spec.Type == v1.ServiceTypeExternalName {
|
||||
serviceType = "Headless(ExternalName)"
|
||||
}
|
||||
} else {
|
||||
serviceType = "Virtual IP"
|
||||
}
|
||||
|
||||
if len(item.Spec.ExternalIPs) > 0 {
|
||||
externalIp = strings.Join(item.Spec.ExternalIPs, ",")
|
||||
}
|
||||
|
||||
for _, portItem := range item.Spec.Ports {
|
||||
port := portItem.Port
|
||||
targetPort := portItem.TargetPort.String()
|
||||
protocol := portItem.Protocol
|
||||
ports += fmt.Sprintf("%d:%s/%s,", port, targetPort, protocol)
|
||||
|
||||
if portItem.NodePort != 0 {
|
||||
nodePorts = append(nodePorts, fmt.Sprintf("%d:%d/%s", port, portItem.NodePort, protocol))
|
||||
}
|
||||
}
|
||||
|
||||
if len(ports) == 0 {
|
||||
ports = "-"
|
||||
} else {
|
||||
ports = ports[0 : len(ports)-1]
|
||||
}
|
||||
|
||||
object := &Service{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
ServiceType: string(serviceType),
|
||||
ExternalIp: externalIp,
|
||||
VirtualIp: vip,
|
||||
CreateTime: createTime,
|
||||
Ports: ports,
|
||||
NodePorts: strings.Join(nodePorts, ","),
|
||||
Annotation: MapString{item.Annotations},
|
||||
Labels: MapString{item.Labels},
|
||||
App: app,
|
||||
}
|
||||
|
||||
return object
|
||||
|
||||
}
|
||||
|
||||
func (ctl *ServiceCtl) generateObject(item v1.Service) *Service {
|
||||
return generateSvcObject(item)
|
||||
}
|
||||
|
||||
func (ctl *ServiceCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *ServiceCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&Service{}) {
|
||||
db.DropTable(&Service{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&Service{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
db.Create(obj)
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *ServiceCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *ServiceCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
ctl.lister = informerFactory.Core().V1().Services().Lister()
|
||||
|
||||
informer := informerFactory.Core().V1().Services().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1.Service)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Create(mysqlObject)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.Service)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Save(mysqlObject)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item Service
|
||||
object := obj.(*v1.Service)
|
||||
db.Where("name=? And namespace=?", object.Name, object.Namespace).Find(&item)
|
||||
db.Delete(item)
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *ServiceCtl) CountWithConditions(conditions string) int {
|
||||
var object Service
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *ServiceCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []Service
|
||||
var object Service
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *ServiceCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
func (ctl *StatefulsetCtl) generateObject(item v1.StatefulSet) *Statefulset {
|
||||
var app string
|
||||
var status string
|
||||
var displayName string
|
||||
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
name := item.Name
|
||||
namespace := item.Namespace
|
||||
availablePodNum := item.Status.ReadyReplicas
|
||||
desirePodNum := *item.Spec.Replicas
|
||||
createTime := item.CreationTimestamp.Time
|
||||
release := item.ObjectMeta.Labels["release"]
|
||||
chart := item.ObjectMeta.Labels["chart"]
|
||||
|
||||
if createTime.IsZero() {
|
||||
createTime = time.Now()
|
||||
}
|
||||
|
||||
if len(release) > 0 && len(chart) > 0 {
|
||||
app = release + "/" + chart
|
||||
}
|
||||
|
||||
if item.Annotations["state"] == "stop" {
|
||||
status = Stopped
|
||||
} else {
|
||||
if availablePodNum >= desirePodNum {
|
||||
status = Running
|
||||
} else {
|
||||
status = Updating
|
||||
}
|
||||
}
|
||||
|
||||
statefulSetObject := &Statefulset{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
Available: availablePodNum,
|
||||
Desire: desirePodNum,
|
||||
App: app,
|
||||
CreateTime: createTime,
|
||||
Status: status,
|
||||
Annotation: MapString{item.Annotations},
|
||||
Labels: MapString{item.Spec.Selector.MatchLabels},
|
||||
}
|
||||
|
||||
return statefulSetObject
|
||||
}
|
||||
|
||||
func (ctl *StatefulsetCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *StatefulsetCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&Statefulset{}) {
|
||||
db.DropTable(&Statefulset{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&Statefulset{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
db.Create(obj)
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *StatefulsetCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *StatefulsetCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
|
||||
ctl.lister = informerFactory.Apps().V1().StatefulSets().Lister()
|
||||
|
||||
informer := informerFactory.Apps().V1().StatefulSets().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
|
||||
object := obj.(*v1.StatefulSet)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Create(mysqlObject)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.StatefulSet)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Save(mysqlObject)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item Statefulset
|
||||
object := obj.(*v1.StatefulSet)
|
||||
db.Where("name=? And namespace=?", object.Name, object.Namespace).Find(&item)
|
||||
db.Delete(item)
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *StatefulsetCtl) CountWithConditions(conditions string) int {
|
||||
var object Statefulset
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *StatefulsetCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []Statefulset
|
||||
var object Statefulset
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *StatefulsetCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
|
||||
"github.com/golang/glog"
|
||||
coreV1 "k8s.io/api/core/v1"
|
||||
"k8s.io/api/storage/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
const (
|
||||
rbdPluginName = "kubernetes.io/rbd"
|
||||
rbdUserSecretNameKey = "userSecretName"
|
||||
)
|
||||
|
||||
func (ctl *StorageClassCtl) generateObject(item v1.StorageClass) *StorageClass {
|
||||
|
||||
var displayName string
|
||||
|
||||
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
|
||||
displayName = item.Annotations[DisplayName]
|
||||
}
|
||||
|
||||
name := item.Name
|
||||
createTime := item.CreationTimestamp.Time
|
||||
isDefault := false
|
||||
provisioner := item.Provisioner
|
||||
if item.Annotations["storageclass.beta.kubernetes.io/is-default-class"] == "true" {
|
||||
isDefault = true
|
||||
}
|
||||
|
||||
if createTime.IsZero() {
|
||||
createTime = time.Now()
|
||||
}
|
||||
|
||||
object := &StorageClass{
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
CreateTime: createTime,
|
||||
IsDefault: isDefault,
|
||||
Annotation: MapString{item.Annotations},
|
||||
Provisioner: provisioner,
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (ctl *StorageClassCtl) Name() string {
|
||||
return ctl.CommonAttribute.Name
|
||||
}
|
||||
|
||||
func (ctl *StorageClassCtl) sync(stopChan chan struct{}) {
|
||||
db := ctl.DB
|
||||
|
||||
if db.HasTable(&StorageClass{}) {
|
||||
db.DropTable(&StorageClass{})
|
||||
}
|
||||
|
||||
db = db.CreateTable(&StorageClass{})
|
||||
|
||||
ctl.initListerAndInformer()
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
obj := ctl.generateObject(*item)
|
||||
db.Create(obj)
|
||||
}
|
||||
|
||||
ctl.informer.Run(stopChan)
|
||||
}
|
||||
|
||||
func (ctl *StorageClassCtl) total() int {
|
||||
list, err := ctl.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
|
||||
return 0
|
||||
}
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (ctl *StorageClassCtl) createCephSecretAfterNewSc(item v1.StorageClass) {
|
||||
// Kubernetes version must < 1.11.0
|
||||
verInfo, err := ctl.K8sClient.ServerVersion()
|
||||
if err != nil {
|
||||
glog.Error("consult k8s server error: ", err)
|
||||
return
|
||||
}
|
||||
if !utilversion.MustParseSemantic(verInfo.String()).LessThan(utilversion.MustParseSemantic("v1.11.0")) {
|
||||
glog.Infof("disable Ceph secret controller due to k8s version %s >= v1.11.0", verInfo.String())
|
||||
return
|
||||
}
|
||||
|
||||
// Find Ceph secret in the new storage class
|
||||
if item.Provisioner != rbdPluginName {
|
||||
return
|
||||
}
|
||||
var secret *coreV1.Secret
|
||||
if secretName, ok := item.Parameters[rbdUserSecretNameKey]; ok {
|
||||
secret, err = ctl.K8sClient.CoreV1().Secrets(core.NamespaceSystem).Get(secretName, metaV1.GetOptions{})
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
glog.Errorf("cannot find secret %s in namespace %s", secretName, core.NamespaceSystem)
|
||||
return
|
||||
}
|
||||
glog.Error("failed to find secret, error: ", err)
|
||||
return
|
||||
}
|
||||
glog.Infof("succeed to find secret %s in namespace %s", secret.GetName(), secret.GetNamespace())
|
||||
} else {
|
||||
glog.Errorf("failed to find user secret name in storage class %s", item.GetName())
|
||||
return
|
||||
}
|
||||
|
||||
// Create or update Ceph secret in each namespace
|
||||
nsList, err := ctl.K8sClient.CoreV1().Namespaces().List(metaV1.ListOptions{})
|
||||
if err != nil {
|
||||
glog.Error("failed to list namespace, error: ", err)
|
||||
return
|
||||
}
|
||||
for _, ns := range nsList.Items {
|
||||
if ns.GetName() == core.NamespaceSystem {
|
||||
glog.Infof("skip creating Ceph secret in namespace %s", core.NamespaceSystem)
|
||||
continue
|
||||
}
|
||||
newSecret := &coreV1.Secret{
|
||||
TypeMeta: metaV1.TypeMeta{
|
||||
Kind: secret.Kind,
|
||||
APIVersion: secret.APIVersion,
|
||||
},
|
||||
ObjectMeta: metaV1.ObjectMeta{
|
||||
Name: secret.GetName(),
|
||||
Namespace: ns.GetName(),
|
||||
Labels: secret.GetLabels(),
|
||||
Annotations: secret.GetAnnotations(),
|
||||
DeletionGracePeriodSeconds: secret.GetDeletionGracePeriodSeconds(),
|
||||
ClusterName: secret.GetClusterName(),
|
||||
},
|
||||
Data: secret.Data,
|
||||
StringData: secret.StringData,
|
||||
Type: secret.Type,
|
||||
}
|
||||
|
||||
_, err := ctl.K8sClient.CoreV1().Secrets(newSecret.GetNamespace()).Get(newSecret.GetName(), metaV1.GetOptions{})
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
// Create secret
|
||||
_, err := ctl.K8sClient.CoreV1().Secrets(newSecret.GetNamespace()).Create(newSecret)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to create secret in namespace %s, error: %v", newSecret.GetNamespace(), err)
|
||||
} else {
|
||||
glog.Infof("succeed to create secret %s in namespace %s", newSecret.GetName(),
|
||||
newSecret.GetNamespace())
|
||||
}
|
||||
} else {
|
||||
glog.Errorf("failed to find secret in namespace %s, error: %v", newSecret.GetNamespace(), err)
|
||||
}
|
||||
} else {
|
||||
// Update secret
|
||||
_, err = ctl.K8sClient.CoreV1().Secrets(newSecret.GetNamespace()).Update(newSecret)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to update secret in namespace %s, error: %v", newSecret.GetNamespace(), err)
|
||||
continue
|
||||
} else {
|
||||
glog.Infof("succeed to update secret %s in namespace %s", newSecret.GetName(), newSecret.GetNamespace())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ctl *StorageClassCtl) initListerAndInformer() {
|
||||
db := ctl.DB
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle)
|
||||
|
||||
ctl.lister = informerFactory.Storage().V1().StorageClasses().Lister()
|
||||
|
||||
informer := informerFactory.Storage().V1().StorageClasses().Informer()
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
object := obj.(*v1.StorageClass)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Create(mysqlObject)
|
||||
ctl.createCephSecretAfterNewSc(*object)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
object := new.(*v1.StorageClass)
|
||||
mysqlObject := ctl.generateObject(*object)
|
||||
db.Save(mysqlObject)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
var item StorageClass
|
||||
object := obj.(*v1.StorageClass)
|
||||
db.Where("name=?", object.Name).Find(&item)
|
||||
db.Delete(item)
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
ctl.informer = informer
|
||||
}
|
||||
|
||||
func (ctl *StorageClassCtl) CountWithConditions(conditions string) int {
|
||||
var object StorageClass
|
||||
|
||||
return countWithConditions(ctl.DB, conditions, &object)
|
||||
}
|
||||
|
||||
func (ctl *StorageClassCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) {
|
||||
var list []StorageClass
|
||||
var object StorageClass
|
||||
var total int
|
||||
|
||||
if len(order) == 0 {
|
||||
order = "createTime desc"
|
||||
}
|
||||
|
||||
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
|
||||
|
||||
for index, storageClass := range list {
|
||||
name := storageClass.Name
|
||||
pvcCtl := ResourceControllers.Controllers[PersistentVolumeClaim]
|
||||
|
||||
list[index].Count = pvcCtl.CountWithConditions(fmt.Sprintf("storage_class=\"%s\"", name))
|
||||
}
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func (ctl *StorageClassCtl) Lister() interface{} {
|
||||
|
||||
return ctl.lister
|
||||
}
|
||||
@@ -1,571 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The KubeSphere 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.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
appV1 "k8s.io/client-go/listers/apps/v1"
|
||||
batchv1 "k8s.io/client-go/listers/batch/v1"
|
||||
batchv1beta1 "k8s.io/client-go/listers/batch/v1beta1"
|
||||
coreV1 "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/listers/extensions/v1beta1"
|
||||
rbacV1 "k8s.io/client-go/listers/rbac/v1"
|
||||
storageV1 "k8s.io/client-go/listers/storage/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
resyncCircle = 600
|
||||
Stopped = "stopped"
|
||||
PvcPending = "pending"
|
||||
Running = "running"
|
||||
Updating = "updating"
|
||||
Failed = "failed"
|
||||
Unfinished = "unfinished"
|
||||
Completed = "completed"
|
||||
Pause = "pause"
|
||||
Warning = "warning"
|
||||
Error = "error"
|
||||
DisplayName = "displayName"
|
||||
creator = "creator"
|
||||
|
||||
Pods = "pods"
|
||||
Deployments = "deployments"
|
||||
Daemonsets = "daemonsets"
|
||||
Statefulsets = "statefulsets"
|
||||
Namespaces = "namespaces"
|
||||
Ingresses = "ingresses"
|
||||
PersistentVolumeClaim = "persistent-volume-claims"
|
||||
Roles = "roles"
|
||||
RoleBindings = "role-bindings"
|
||||
ClusterRoles = "cluster-roles"
|
||||
ClusterRoleBindings = "cluster-role-bindings"
|
||||
Services = "services"
|
||||
StorageClasses = "storage-classes"
|
||||
Applications = "applications"
|
||||
Jobs = "jobs"
|
||||
Cronjobs = "cronjobs"
|
||||
Nodes = "nodes"
|
||||
Replicasets = "replicasets"
|
||||
ControllerRevisions = "controllerrevisions"
|
||||
ConfigMaps = "configmaps"
|
||||
Secrets = "secrets"
|
||||
)
|
||||
|
||||
type MapString struct {
|
||||
Values map[string]string `json:"values" gorm:"type:TEXT"`
|
||||
}
|
||||
|
||||
func (annotation *MapString) Scan(val interface{}) error {
|
||||
switch val := val.(type) {
|
||||
case string:
|
||||
return json.Unmarshal([]byte(val), annotation)
|
||||
case []byte:
|
||||
return json.Unmarshal(val, annotation)
|
||||
default:
|
||||
return errors.New("not support")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (annotation MapString) Value() (driver.Value, error) {
|
||||
bytes, err := json.Marshal(annotation)
|
||||
return string(bytes), err
|
||||
}
|
||||
|
||||
type Taints struct {
|
||||
Values []v1.Taint `json:"values" gorm:"type:TEXT"`
|
||||
}
|
||||
|
||||
func (taints *Taints) Scan(val interface{}) error {
|
||||
switch val := val.(type) {
|
||||
case string:
|
||||
return json.Unmarshal([]byte(val), taints)
|
||||
case []byte:
|
||||
return json.Unmarshal(val, taints)
|
||||
default:
|
||||
return errors.New("not support")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (taints Taints) Value() (driver.Value, error) {
|
||||
bytes, err := json.Marshal(taints)
|
||||
return string(bytes), err
|
||||
}
|
||||
|
||||
type Deployment struct {
|
||||
Name string `gorm:"primary_key" json:"name"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Namespace string `gorm:"primary_key" json:"namespace"`
|
||||
App string `json:"app,omitempty"`
|
||||
|
||||
Available int32 `json:"available"`
|
||||
Desire int32 `json:"desire"`
|
||||
Status string `json:"status"`
|
||||
Labels MapString `json:"labels"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
UpdateTime time.Time `gorm:"column:updateTime" json:"updateTime,omitempty"`
|
||||
}
|
||||
|
||||
type Statefulset struct {
|
||||
Name string `gorm:"primary_key" json:"name,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Namespace string `gorm:"primary_key" json:"namespace,omitempty"`
|
||||
App string `json:"app,omitempty"`
|
||||
|
||||
Available int32 `json:"available"`
|
||||
Desire int32 `json:"desire"`
|
||||
Status string `json:"status"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
Labels MapString `json:"labels"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
}
|
||||
|
||||
type Daemonset struct {
|
||||
Name string `gorm:"primary_key" json:"name,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Namespace string `gorm:"primary_key" json:"namespace,omitempty"`
|
||||
App string `json:"app,omitempty"`
|
||||
|
||||
Available int32 `json:"available"`
|
||||
Desire int32 `json:"desire"`
|
||||
Status string `json:"status"`
|
||||
NodeSelector string `json:"nodeSelector, omitempty"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
Labels MapString `json:"labels"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
Name string `gorm:"primary_key" json:"name"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Namespace string `gorm:"primary_key" json:"namespace"`
|
||||
ServiceType string `gorm:"column:type" json:"type,omitempty"`
|
||||
|
||||
App string `json:"app,omitempty"`
|
||||
VirtualIp string `gorm:"column:virtualIp" json:"virtualIp,omitempty"`
|
||||
ExternalIp string `gorm:"column:externalIp" json:"externalIp,omitempty"`
|
||||
|
||||
Ports string `json:"ports,omitempty"`
|
||||
NodePorts string `gorm:"column:nodePorts" json:"nodePorts,omitempty"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
Labels MapString `json:"labels"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
}
|
||||
|
||||
type Pvc struct {
|
||||
Name string `gorm:"primary_key" json:"name"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Namespace string `gorm:"primary_key" json:"namespace"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Capacity string `json:"capacity,omitempty"`
|
||||
AccessMode string `gorm:"column:accessMode" json:"accessMode,omitempty"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
Labels MapString `json:"labels"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
StorageClassName string `gorm:"column:storage_class" json:"storage_class,omitempty"`
|
||||
InUse bool `gorm:"column:inUse" json:"inUse"`
|
||||
}
|
||||
|
||||
type ingressRule struct {
|
||||
Host string `json:"host"`
|
||||
Path string `json:"path"`
|
||||
Service string `json:"service"`
|
||||
Port int32 `json:"port"`
|
||||
}
|
||||
|
||||
type Ingress struct {
|
||||
Name string `gorm:"primary_key" json:"name"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Namespace string `gorm:"primary_key" json:"namespace"`
|
||||
Ip string `json:"ip,omitempty"`
|
||||
Rules string `gorm:"type:text" json:"rules, omitempty"`
|
||||
TlsTermination string `gorm:"column:tlsTermination" json:"tlsTermination,omitempty"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
Labels MapString `json:"labels"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
}
|
||||
|
||||
type Pod struct {
|
||||
// search and sort field, not seen in response
|
||||
Name string `gorm:"primary_key" json:"-"`
|
||||
Namespace string `gorm:"primary_key" json:"-"`
|
||||
Node string `json:"-"`
|
||||
OwnerKind string `gorm:"column:ownerKind" json:"-"`
|
||||
OwnerName string `gorm:"column:ownerName" json:"-"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"-"`
|
||||
|
||||
// Kubernetes Standard Pod Specification
|
||||
Kind string `json:"kind,omitempty"`
|
||||
APIVersion string `gorm:"column:apiVersion" json:"apiVersion,omitempty"`
|
||||
Spec v1.PodSpec `sql:"-" json:"spec,omitempty"`
|
||||
Metadata v12.ObjectMeta `sql:"-" json:"metadata,omitempty"`
|
||||
Status v1.PodStatus `sql:"-" json:"status,omitempty"`
|
||||
|
||||
// shadow field, used only for database
|
||||
MetadataString string `gorm:"column:metadata;type:text" json:"-"`
|
||||
SpecString string `gorm:"column:podSpec;type:text" json:"-"`
|
||||
StatusString string `gorm:"column:status;type:text" json:"-"`
|
||||
}
|
||||
|
||||
func (pod *Pod) AfterFind(scope *gorm.Scope) (err error) {
|
||||
|
||||
if err = json.Unmarshal([]byte(pod.SpecString), &pod.Spec); err != nil {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
|
||||
if err = json.Unmarshal([]byte(pod.MetadataString), &pod.Metadata); err != nil {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
|
||||
if err = json.Unmarshal([]byte(pod.StatusString), &pod.Status); err != nil {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pod *Pod) BeforeSave(scope *gorm.Scope) (err error) {
|
||||
|
||||
if bytes, err := json.Marshal(pod.Spec); err == nil {
|
||||
pod.SpecString = string(bytes)
|
||||
} else {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
|
||||
if bytes, err := json.Marshal(pod.Metadata); err == nil {
|
||||
pod.MetadataString = string(bytes)
|
||||
} else {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
|
||||
if bytes, err := json.Marshal(pod.Status); err == nil {
|
||||
pod.StatusString = string(bytes)
|
||||
} else {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Container struct {
|
||||
Name string `json:"name"`
|
||||
Ready bool `json:"ready,omitempty"`
|
||||
Image string `json:"image"`
|
||||
Resources v1.ResourceRequirements `json:"resources"`
|
||||
Ports []v1.ContainerPort `json:"ports"`
|
||||
}
|
||||
type Containers []Container
|
||||
|
||||
func (containers *Containers) Scan(val interface{}) error {
|
||||
switch val := val.(type) {
|
||||
case string:
|
||||
return json.Unmarshal([]byte(val), containers)
|
||||
case []byte:
|
||||
return json.Unmarshal(val, containers)
|
||||
default:
|
||||
return errors.New("not support")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (containers Containers) Value() (driver.Value, error) {
|
||||
bytes, err := json.Marshal(containers)
|
||||
return string(bytes), err
|
||||
}
|
||||
|
||||
type Role struct {
|
||||
Name string `gorm:"primary_key" json:"name"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Namespace string `gorm:"primary_key" json:"namespace"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
}
|
||||
|
||||
type ClusterRole struct {
|
||||
Name string `gorm:"primary_key" json:"name"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
}
|
||||
|
||||
type Namespace struct {
|
||||
Name string `gorm:"primary_key" json:"name"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Creator string `json:"creator,omitempty"`
|
||||
Status string `json:"status"`
|
||||
|
||||
Descrition string `json:"description,omitempty"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
Usage v1.ResourceList `gorm:"-" json:"usage,omitempty"`
|
||||
}
|
||||
|
||||
type StorageClass struct {
|
||||
Name string `gorm:"primary_key" json:"name"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Creator string `json:"creator,omitempty"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
IsDefault bool `json:"default"`
|
||||
Count int `json:"count"`
|
||||
Provisioner string `json:"provisioner"`
|
||||
}
|
||||
|
||||
type JobRevisions map[int]JobRevision
|
||||
|
||||
type JobRevision struct {
|
||||
Status string `json:"status"`
|
||||
Reasons []string `json:"reasons"`
|
||||
Messages []string `json:"messages"`
|
||||
Succeed int32 `json:"succeed"`
|
||||
DesirePodNum int32 `json:"desire"`
|
||||
Failed int32 `json:"failed"`
|
||||
Uid string `json:"uid"`
|
||||
StartTime time.Time `json:"start-time"`
|
||||
CompletionTime time.Time `json:"completion-time"`
|
||||
}
|
||||
|
||||
type Job struct {
|
||||
Name string `gorm:"primary_key" json:"name,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Namespace string `gorm:"primary_key" json:"namespace,omitempty"`
|
||||
|
||||
Completed int32 `json:"completed"`
|
||||
Desire int32 `json:"desire"`
|
||||
Status string `json:"status"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
Labels MapString `json:"labels"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
UpdateTime time.Time `gorm:"column:updateTime" json:"updateTime,omitempty"`
|
||||
}
|
||||
|
||||
type CronJob struct {
|
||||
Name string `gorm:"primary_key" json:"name,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Namespace string `gorm:"primary_key" json:"namespace,omitempty"`
|
||||
|
||||
Active int `json:"active"`
|
||||
Schedule string `json:"schedule"`
|
||||
Status string `json:"status"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
Labels MapString `json:"labels"`
|
||||
LastScheduleTime *time.Time `gorm:"column:lastScheduleTime" json:"lastScheduleTime,omitempty"`
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
Name string `gorm:"primary_key" json:"name,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
Ip string `json:"ip"`
|
||||
Status string `json:"status"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
Labels MapString `json:"labels"`
|
||||
Taints Taints `json:"taints"`
|
||||
Msg string `json:"msg"`
|
||||
Role string `json:"role"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigMap struct {
|
||||
Name string `gorm:"primary_key" json:"name"`
|
||||
Namespace string `gorm:"primary_key" json:"namespace"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
Entries string `gorm:"type:text" json:"entries"`
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
Name string `gorm:"primary_key" json:"name"`
|
||||
Namespace string `gorm:"primary_key" json:"namespace"`
|
||||
DisplayName string `json:"displayName,omitempty" gorm:"column:displayName"`
|
||||
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
|
||||
Annotation MapString `json:"annotations"`
|
||||
Entries int `json:"entries"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type Paging struct {
|
||||
Limit, Offset, Page int
|
||||
}
|
||||
|
||||
type Controller interface {
|
||||
chanStop() chan struct{}
|
||||
chanAlive() chan struct{}
|
||||
CountWithConditions(condition string) int
|
||||
total() int
|
||||
initListerAndInformer()
|
||||
sync(stopChan chan struct{})
|
||||
Name() string
|
||||
CloseDB()
|
||||
Lister() interface{}
|
||||
ListWithConditions(condition string, paging *Paging, order string) (int, interface{}, error)
|
||||
}
|
||||
|
||||
type CommonAttribute struct {
|
||||
K8sClient *kubernetes.Clientset
|
||||
Name string
|
||||
DB *gorm.DB
|
||||
stopChan chan struct{}
|
||||
aliveChan chan struct{}
|
||||
}
|
||||
|
||||
func (ca *CommonAttribute) chanStop() chan struct{} {
|
||||
|
||||
return ca.stopChan
|
||||
}
|
||||
|
||||
func (ca *CommonAttribute) chanAlive() chan struct{} {
|
||||
|
||||
return ca.aliveChan
|
||||
}
|
||||
|
||||
func (ca *CommonAttribute) CloseDB() {
|
||||
|
||||
ca.DB.Close()
|
||||
}
|
||||
|
||||
type DeploymentCtl struct {
|
||||
CommonAttribute
|
||||
lister appV1.DeploymentLister
|
||||
informer cache.SharedIndexInformer
|
||||
}
|
||||
|
||||
type StatefulsetCtl struct {
|
||||
CommonAttribute
|
||||
lister appV1.StatefulSetLister
|
||||
informer cache.SharedIndexInformer
|
||||
}
|
||||
|
||||
type DaemonsetCtl struct {
|
||||
CommonAttribute
|
||||
lister appV1.DaemonSetLister
|
||||
informer cache.SharedIndexInformer
|
||||
}
|
||||
|
||||
type ServiceCtl struct {
|
||||
CommonAttribute
|
||||
lister coreV1.ServiceLister
|
||||
informer cache.SharedIndexInformer
|
||||
}
|
||||
|
||||
type PvcCtl struct {
|
||||
CommonAttribute
|
||||
lister coreV1.PersistentVolumeClaimLister
|
||||
informer cache.SharedIndexInformer
|
||||
}
|
||||
|
||||
type PodCtl struct {
|
||||
CommonAttribute
|
||||
lister coreV1.PodLister
|
||||
informer cache.SharedIndexInformer
|
||||
}
|
||||
|
||||
type IngressCtl struct {
|
||||
lister v1beta1.IngressLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
|
||||
type NamespaceCtl struct {
|
||||
CommonAttribute
|
||||
lister coreV1.NamespaceLister
|
||||
informer cache.SharedIndexInformer
|
||||
}
|
||||
|
||||
type StorageClassCtl struct {
|
||||
lister storageV1.StorageClassLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
|
||||
type RoleCtl struct {
|
||||
lister rbacV1.RoleLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
|
||||
type ClusterRoleCtl struct {
|
||||
lister rbacV1.ClusterRoleLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
|
||||
type ClusterRoleBindingCtl struct {
|
||||
lister rbacV1.ClusterRoleBindingLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
type RoleBindingCtl struct {
|
||||
lister rbacV1.RoleBindingLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
|
||||
type JobCtl struct {
|
||||
lister batchv1.JobLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
|
||||
type CronJobCtl struct {
|
||||
lister batchv1beta1.CronJobLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
|
||||
type NodeCtl struct {
|
||||
lister coreV1.NodeLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
|
||||
type ReplicaSetCtl struct {
|
||||
lister appV1.ReplicaSetLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
|
||||
type ControllerRevisionCtl struct {
|
||||
lister appV1.ControllerRevisionLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
|
||||
type ConfigMapCtl struct {
|
||||
lister coreV1.ConfigMapLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
|
||||
type SecretCtl struct {
|
||||
lister coreV1.SecretLister
|
||||
informer cache.SharedIndexInformer
|
||||
CommonAttribute
|
||||
}
|
||||
@@ -1,741 +0,0 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/rbac/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
v12 "k8s.io/client-go/listers/rbac/v1"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models/controllers"
|
||||
ksErr "kubesphere.io/kubesphere/pkg/util/errors"
|
||||
)
|
||||
|
||||
const ClusterRoleKind = "ClusterRole"
|
||||
|
||||
// Get user list based on workspace role
|
||||
func WorkspaceRoleUsers(workspace string, roleName string) ([]User, error) {
|
||||
|
||||
lister, err := controllers.GetLister(controllers.ClusterRoleBindings)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRoleBindingLister := lister.(v12.ClusterRoleBindingLister)
|
||||
|
||||
workspaceRoleBinding, err := clusterRoleBindingLister.Get(fmt.Sprintf("system:%s:%s", workspace, roleName))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
names := make([]string, 0)
|
||||
|
||||
for _, subject := range workspaceRoleBinding.Subjects {
|
||||
if subject.Kind == v1.UserKind {
|
||||
names = append(names, subject.Name)
|
||||
}
|
||||
}
|
||||
|
||||
users, err := GetUsers(names)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(users); i++ {
|
||||
users[i].WorkspaceRole = roleName
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetUsers(names []string) ([]User, error) {
|
||||
var users []User
|
||||
|
||||
if names == nil || len(names) == 0 {
|
||||
return make([]User, 0), nil
|
||||
}
|
||||
|
||||
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users?name=%s", constants.AccountAPIServer, strings.Join(names, ",")))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer result.Body.Close()
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, ksErr.Wrap(data)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data, &users)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetUser(name string) (*User, error) {
|
||||
|
||||
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users/%s", constants.AccountAPIServer, name))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer result.Body.Close()
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, ksErr.Wrap(data)
|
||||
}
|
||||
|
||||
var user User
|
||||
|
||||
err = json.Unmarshal(data, &user)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// Get rules
|
||||
func WorkspaceRoleRules(workspace string, roleName string) (*v1.ClusterRole, []Rule, error) {
|
||||
|
||||
clusterRoleName := fmt.Sprintf("system:%s:%s", workspace, roleName)
|
||||
|
||||
workspaceRole, err := GetClusterRole(clusterRoleName)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(workspaceRole.Rules); i++ {
|
||||
workspaceRole.Rules[i].ResourceNames = nil
|
||||
}
|
||||
|
||||
rules := make([]Rule, 0)
|
||||
for i := 0; i < len(WorkspaceRoleRuleMapping); i++ {
|
||||
rule := Rule{Name: WorkspaceRoleRuleMapping[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < len(WorkspaceRoleRuleMapping[i].Actions); j++ {
|
||||
if rulesMatchesAction(workspaceRole.Rules, WorkspaceRoleRuleMapping[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, WorkspaceRoleRuleMapping[i].Actions[j])
|
||||
}
|
||||
}
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
workspaceRole.Name = roleName
|
||||
|
||||
return workspaceRole, rules, nil
|
||||
}
|
||||
|
||||
func GetUserNamespaces(username string, requiredRule v1.PolicyRule) (allNamespace bool, namespaces []string, err error) {
|
||||
|
||||
clusterRoles, err := GetClusterRoles(username)
|
||||
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
clusterRules := make([]v1.PolicyRule, 0)
|
||||
for _, role := range clusterRoles {
|
||||
clusterRules = append(clusterRules, role.Rules...)
|
||||
}
|
||||
|
||||
if requiredRule.Size() == 0 {
|
||||
if RulesMatchesRequired(clusterRules, v1.PolicyRule{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/namespaces"},
|
||||
}) {
|
||||
return true, nil, nil
|
||||
}
|
||||
} else {
|
||||
|
||||
if RulesMatchesRequired(clusterRules, requiredRule) {
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
roles, err := GetRoles("", username)
|
||||
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
rulesMapping := make(map[string][]v1.PolicyRule, 0)
|
||||
|
||||
for _, role := range roles {
|
||||
rules := rulesMapping[role.Namespace]
|
||||
if rules == nil {
|
||||
rules = make([]v1.PolicyRule, 0)
|
||||
}
|
||||
rules = append(rules, role.Rules...)
|
||||
rulesMapping[role.Namespace] = rules
|
||||
}
|
||||
|
||||
namespaces = make([]string, 0)
|
||||
|
||||
for namespace, rules := range rulesMapping {
|
||||
if requiredRule.Size() == 0 || RulesMatchesRequired(rules, requiredRule) {
|
||||
namespaces = append(namespaces, namespace)
|
||||
}
|
||||
}
|
||||
|
||||
return false, namespaces, nil
|
||||
}
|
||||
|
||||
func GetRole(namespace string, name string) (*v1.Role, error) {
|
||||
lister, err := controllers.GetLister(controllers.Roles)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roleLister := lister.(v12.RoleLister)
|
||||
role, err := roleLister.Roles(namespace).Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return role.DeepCopy(), nil
|
||||
}
|
||||
func GetWorkspaceUsers(workspace string, workspaceRole string) ([]string, error) {
|
||||
|
||||
lister, err := controllers.GetLister(controllers.ClusterRoleBindings)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRoleBindingLister := lister.(v12.ClusterRoleBindingLister)
|
||||
clusterRoleBinding, err := clusterRoleBindingLister.Get(fmt.Sprintf("system:%s:%s", workspace, workspaceRole))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users := make([]string, 0)
|
||||
|
||||
for _, s := range clusterRoleBinding.Subjects {
|
||||
if s.Kind == v1.UserKind && !slice.ContainsString(users, s.Name, nil) {
|
||||
users = append(users, s.Name)
|
||||
}
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetClusterRoleBindings(name string) ([]v1.ClusterRoleBinding, error) {
|
||||
lister, err := controllers.GetLister(controllers.ClusterRoleBindings)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRoleBindingLister := lister.(v12.ClusterRoleBindingLister)
|
||||
|
||||
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]v1.ClusterRoleBinding, 0)
|
||||
|
||||
for _, clusterRoleBinding := range clusterRoleBindings {
|
||||
if clusterRoleBinding.RoleRef.Name == name {
|
||||
items = append(items, *clusterRoleBinding)
|
||||
}
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func GetRoleBindings(namespace string, name string) ([]v1.RoleBinding, error) {
|
||||
lister, err := controllers.GetLister(controllers.RoleBindings)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roleBindingLister := lister.(v12.RoleBindingLister)
|
||||
|
||||
roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]v1.RoleBinding, 0)
|
||||
|
||||
for _, roleBinding := range roleBindings {
|
||||
if roleBinding.RoleRef.Name == name {
|
||||
items = append(items, *roleBinding)
|
||||
}
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func GetClusterRole(name string) (*v1.ClusterRole, error) {
|
||||
lister, err := controllers.GetLister(controllers.ClusterRoles)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRoleLister := lister.(v12.ClusterRoleLister)
|
||||
|
||||
role, err := clusterRoleLister.Get(name)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return role.DeepCopy(), nil
|
||||
}
|
||||
|
||||
func GetRoles(namespace string, username string) ([]v1.Role, error) {
|
||||
lister, err := controllers.GetLister(controllers.RoleBindings)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roleBindingLister := lister.(v12.RoleBindingLister)
|
||||
|
||||
lister, err = controllers.GetLister(controllers.Roles)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roleLister := lister.(v12.RoleLister)
|
||||
|
||||
lister, err = controllers.GetLister(controllers.ClusterRoles)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRoleLister := lister.(v12.ClusterRoleLister)
|
||||
|
||||
roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roles := make([]v1.Role, 0)
|
||||
|
||||
for _, roleBinding := range roleBindings {
|
||||
|
||||
for _, subject := range roleBinding.Subjects {
|
||||
if subject.Kind == v1.UserKind && subject.Name == username {
|
||||
if roleBinding.RoleRef.Kind == ClusterRoleKind {
|
||||
clusterRole, err := clusterRoleLister.Get(roleBinding.RoleRef.Name)
|
||||
if err == nil {
|
||||
var role = v1.Role{TypeMeta: (*clusterRole).TypeMeta, ObjectMeta: (*clusterRole).ObjectMeta, Rules: (*clusterRole).Rules}
|
||||
role.Namespace = roleBinding.Namespace
|
||||
roles = append(roles, role)
|
||||
break
|
||||
} else if apierrors.IsNotFound(err) {
|
||||
glog.Infoln(err.Error())
|
||||
break
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else {
|
||||
if subject.Kind == v1.UserKind && subject.Name == username {
|
||||
role, err := roleLister.Roles(roleBinding.Namespace).Get(roleBinding.RoleRef.Name)
|
||||
if err == nil {
|
||||
roles = append(roles, *role)
|
||||
break
|
||||
} else if apierrors.IsNotFound(err) {
|
||||
glog.Infoln(err.Error())
|
||||
break
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
// Get cluster roles by username
|
||||
func GetClusterRoles(username string) ([]v1.ClusterRole, error) {
|
||||
|
||||
lister, err := controllers.GetLister(controllers.ClusterRoleBindings)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRoleBindingLister := lister.(v12.ClusterRoleBindingLister)
|
||||
|
||||
lister, err = controllers.GetLister(controllers.ClusterRoles)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRoleLister := lister.(v12.ClusterRoleLister)
|
||||
|
||||
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roles := make([]v1.ClusterRole, 0)
|
||||
|
||||
for _, roleBinding := range clusterRoleBindings {
|
||||
for _, subject := range roleBinding.Subjects {
|
||||
if subject.Kind == v1.UserKind && subject.Name == username {
|
||||
if roleBinding.RoleRef.Kind == ClusterRoleKind {
|
||||
role, err := clusterRoleLister.Get(roleBinding.RoleRef.Name)
|
||||
if err == nil {
|
||||
role = role.DeepCopy()
|
||||
if role.Annotations == nil {
|
||||
role.Annotations = make(map[string]string, 0)
|
||||
}
|
||||
role.Annotations["rbac.authorization.k8s.io/clusterrolebinding"] = roleBinding.Name
|
||||
if roleBinding.Annotations != nil &&
|
||||
roleBinding.Annotations["rbac.authorization.k8s.io/clusterrole"] == roleBinding.RoleRef.Name {
|
||||
role.Annotations["rbac.authorization.k8s.io/clusterrole"] = "true"
|
||||
}
|
||||
roles = append(roles, *role)
|
||||
break
|
||||
} else if apierrors.IsNotFound(err) {
|
||||
glog.Warning(err)
|
||||
break
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
func GetUserRules(username string) (map[string][]Rule, error) {
|
||||
|
||||
items := make(map[string][]Rule, 0)
|
||||
userRoles, err := GetRoles("", username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rulesMapping := make(map[string][]v1.PolicyRule, 0)
|
||||
|
||||
for _, role := range userRoles {
|
||||
rules := rulesMapping[role.Namespace]
|
||||
if rules == nil {
|
||||
rules = make([]v1.PolicyRule, 0)
|
||||
}
|
||||
rules = append(rules, role.Rules...)
|
||||
rulesMapping[role.Namespace] = rules
|
||||
}
|
||||
|
||||
for namespace, policyRules := range rulesMapping {
|
||||
rules := convertToRules(policyRules)
|
||||
if len(rules) > 0 {
|
||||
items[namespace] = rules
|
||||
}
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func convertToRules(policyRules []v1.PolicyRule) []Rule {
|
||||
rules := make([]Rule, 0)
|
||||
|
||||
for i := 0; i < (len(RoleRuleMapping)); i++ {
|
||||
rule := Rule{Name: RoleRuleMapping[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < (len(RoleRuleMapping[i].Actions)); j++ {
|
||||
if rulesMatchesAction(policyRules, RoleRuleMapping[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, RoleRuleMapping[i].Actions[j])
|
||||
}
|
||||
}
|
||||
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules
|
||||
}
|
||||
|
||||
func GetUserClusterRules(username string) ([]Rule, error) {
|
||||
|
||||
rules := make([]Rule, 0)
|
||||
|
||||
clusterRoles, err := GetClusterRoles(username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRules := make([]v1.PolicyRule, 0)
|
||||
|
||||
for _, role := range clusterRoles {
|
||||
clusterRules = append(clusterRules, role.Rules...)
|
||||
}
|
||||
|
||||
for i := 0; i < (len(ClusterRoleRuleMapping)); i++ {
|
||||
rule := Rule{Name: ClusterRoleRuleMapping[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < (len(ClusterRoleRuleMapping[i].Actions)); j++ {
|
||||
if rulesMatchesAction(clusterRules, ClusterRoleRuleMapping[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, ClusterRoleRuleMapping[i].Actions[j])
|
||||
}
|
||||
}
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func GetClusterRoleRules(name string) ([]Rule, error) {
|
||||
|
||||
clusterRole, err := GetClusterRole(name)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules := make([]Rule, 0)
|
||||
|
||||
for i := 0; i < len(ClusterRoleRuleMapping); i++ {
|
||||
rule := Rule{Name: ClusterRoleRuleMapping[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < (len(ClusterRoleRuleMapping[i].Actions)); j++ {
|
||||
if rulesMatchesAction(clusterRole.Rules, ClusterRoleRuleMapping[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, ClusterRoleRuleMapping[i].Actions[j])
|
||||
}
|
||||
}
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func GetRoleRules(namespace string, name string) ([]Rule, error) {
|
||||
role, err := GetRole(namespace, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules := make([]Rule, 0)
|
||||
for i := 0; i < len(RoleRuleMapping); i++ {
|
||||
rule := Rule{Name: RoleRuleMapping[i].Name}
|
||||
rule.Actions = make([]Action, 0)
|
||||
for j := 0; j < len(RoleRuleMapping[i].Actions); j++ {
|
||||
if rulesMatchesAction(role.Rules, RoleRuleMapping[i].Actions[j]) {
|
||||
rule.Actions = append(rule.Actions, RoleRuleMapping[i].Actions[j])
|
||||
}
|
||||
}
|
||||
if len(rule.Actions) > 0 {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func rulesMatchesAction(rules []v1.PolicyRule, action Action) bool {
|
||||
|
||||
for _, rule := range action.Rules {
|
||||
if !RulesMatchesRequired(rules, rule) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func RulesMatchesRequired(rules []v1.PolicyRule, required v1.PolicyRule) bool {
|
||||
for _, rule := range rules {
|
||||
if ruleMatchesRequired(rule, required) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ruleMatchesRequired(rule v1.PolicyRule, required v1.PolicyRule) bool {
|
||||
|
||||
if len(required.NonResourceURLs) == 0 {
|
||||
for _, apiGroup := range required.APIGroups {
|
||||
for _, resource := range required.Resources {
|
||||
resources := strings.Split(resource, "/")
|
||||
resource = resources[0]
|
||||
var subsource string
|
||||
if len(resources) > 1 {
|
||||
subsource = resources[1]
|
||||
}
|
||||
|
||||
if len(required.ResourceNames) == 0 {
|
||||
for _, verb := range required.Verbs {
|
||||
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, "", verb) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, resourceName := range required.ResourceNames {
|
||||
for _, verb := range required.Verbs {
|
||||
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, resourceName, verb) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, apiGroup := range required.APIGroups {
|
||||
for _, nonResourceURL := range required.NonResourceURLs {
|
||||
for _, verb := range required.Verbs {
|
||||
if !ruleMatchesRequest(rule, apiGroup, nonResourceURL, "", "", "", verb) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func ruleMatchesResources(rule v1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool {
|
||||
|
||||
if resource == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if !hasString(rule.APIGroups, apiGroup) && !hasString(rule.APIGroups, v1.ResourceAll) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(rule.ResourceNames) > 0 && !hasString(rule.ResourceNames, resourceName) {
|
||||
return false
|
||||
}
|
||||
|
||||
combinedResource := resource
|
||||
|
||||
if subresource != "" {
|
||||
combinedResource = combinedResource + "/" + subresource
|
||||
}
|
||||
|
||||
for _, res := range rule.Resources {
|
||||
|
||||
// match "*"
|
||||
if res == v1.ResourceAll || res == combinedResource {
|
||||
return true
|
||||
}
|
||||
|
||||
// match "*/subresource"
|
||||
if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") {
|
||||
return true
|
||||
}
|
||||
// match "resource/*"
|
||||
if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func ruleMatchesRequest(rule v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool {
|
||||
|
||||
if !hasString(rule.Verbs, verb) && !hasString(rule.Verbs, v1.VerbAll) {
|
||||
return false
|
||||
}
|
||||
|
||||
if nonResourceURL == "" {
|
||||
return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName)
|
||||
} else {
|
||||
return ruleMatchesNonResource(rule, nonResourceURL)
|
||||
}
|
||||
}
|
||||
|
||||
func ruleMatchesNonResource(rule v1.PolicyRule, nonResourceURL string) bool {
|
||||
|
||||
if nonResourceURL == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, spec := range rule.NonResourceURLs {
|
||||
if pathMatches(nonResourceURL, spec) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func pathMatches(path, spec string) bool {
|
||||
// Allow wildcard match
|
||||
if spec == "*" {
|
||||
return true
|
||||
}
|
||||
// Allow exact match
|
||||
if spec == path {
|
||||
return true
|
||||
}
|
||||
// Allow a trailing * subpath match
|
||||
if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasString(slice []string, value string) bool {
|
||||
for _, s := range slice {
|
||||
if s == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user