Compare commits

..

12 Commits

Author SHA1 Message Date
Wiley Wang
7a479e6493 Merge pull request #174 from wnxn/express
get k8s version through k8sclient at express branch
2018-09-26 13:46:20 +08:00
wileywang
e1aed9f652 get k8s version through k8sclient 2018-09-25 17:52:41 +08:00
Wiley Wang
337d71d677 Merge pull request #169 from wnxn/express
Add controller to create Ceph secret in express branch
2018-09-25 15:32:25 +08:00
wileywang
390491ee31 Add controller to create Ceph secret 2018-09-19 16:25:30 +08:00
zryfish
733bea4e74 Merge pull request #164 from wnxn/express
update dependency at express branch
2018-09-17 13:38:01 +08:00
Wiley Wang
484cc0ecbb update dependency 2018-09-17 03:15:19 +00:00
calvinyv
e4aa866ca5 Merge pull request #155 from calvinyv/refact_docs
refactor docs
2018-08-08 14:31:28 +08:00
Calvin Yu
b3bfb549e0 refactor docs 2018-08-08 14:16:14 +08:00
richardxz
bf6c3245e4 add desciption field in application response 2018-08-06 03:21:54 +00:00
richardxz
e803897d10 add resync function and support to view deployed applications 2018-07-31 14:04:36 +08:00
richardxz
a4a48eb4fc add swagger ui 2018-07-31 14:04:05 +08:00
jeff
5091a87c1e fix router config bug 2018-07-23 11:09:05 +08:00
166 changed files with 29209 additions and 850 deletions

13
AUTHORS
View File

@@ -1,13 +0,0 @@
# This is the official list of KubeSphere authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as one of
# Organization's name
# Individual's name <submission email address>
# Individual's name <submission email address> <email2> <emailN>
# See CONTRIBUTORS for the meaning of multiple email addresses.
# Please keep the list sorted.
Yunify Inc.

View File

@@ -1,20 +0,0 @@
# This is the official list of people who can contribute
# (and typically have contributed) code to the KubeSphere repository.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Yunify employees are listed here
# but not in AUTHORS, because Yunify holds the copyright.
#
# When adding J Random Contributor's name to this file,
# either J's name or J's organization's name should be
# added to the AUTHORS file.
# Names should be added to this file like so:
# Individual's name <submission email address>
# Individual's name <submission email address> <email2> <emailN>
#
# An entry with multiple email addresses specifies that the
# first address should be used in the submit logs.
# Please keep the list sorted.
Ray@qingcloud <ray@yunify.com>

View File

@@ -6,5 +6,5 @@ RUN apk add --update ca-certificates \
COPY ./bin/* /usr/local/bin/
COPY ./install/ingress-controller /etc/kubesphere/ingress-controller
COPY ./install/swagger-ui /usr/lib/kubesphere/swagger-ui
CMD ["sh"]

249
Gopkg.lock generated
View File

@@ -2,24 +2,47 @@
[[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",
@@ -29,27 +52,33 @@
"mvcc/mvccpb",
"pkg/tlsutil",
"pkg/transport",
"pkg/types"
"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"
"reference",
]
pruneopts = "UT"
revision = "749f6afb4572201e3c37325d0ffedb6f32be8950"
[[projects]]
digest = "1:ec821dda59d7dd340498d74f798aa218b2c782bba54a690e866dc4f520d900d5"
name = "github.com/docker/docker"
packages = [
"api",
@@ -71,191 +100,295 @@
"pkg/ioutils",
"pkg/longpath",
"pkg/system",
"pkg/tlsconfig"
"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"
"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"
"spdy",
]
pruneopts = "UT"
revision = "bc6354cbbc295e925e4c611ffe90c1f287ee54db"
[[projects]]
digest = "1:798072bbab2506719d8292cd9b5840a0b5babe0348393bd7097d8fb25ecf0b82"
name = "github.com/emicklei/go-restful"
packages = [
".",
"log"
"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"
"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"
"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"
"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"
"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",
@@ -267,20 +400,24 @@
"internal/socks",
"internal/timeseries",
"proxy",
"trace"
"trace",
]
pruneopts = "UT"
revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196"
[[projects]]
branch = "master"
digest = "1:a17927b3d78603ae6691d5bf8d3d91467a6edd4ca43c9509347e016a54477f96"
name = "golang.org/x/sys"
packages = [
"unix",
"windows"
"windows",
]
pruneopts = "UT"
revision = "fc8bd948cf46f9c7af0f07d34151ce25fe90e477"
[[projects]]
digest = "1:0c56024909189aee3364b7f21a95a27459f718aa7c199a5c111c36cfffd9eaef"
name = "golang.org/x/text"
packages = [
"collate",
@@ -296,30 +433,39 @@
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable"
"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 = [
".",
@@ -347,30 +493,38 @@
"stats",
"status",
"tap",
"transport"
"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",
@@ -400,12 +554,14 @@
"settings/v1alpha1",
"storage/v1",
"storage/v1alpha1",
"storage/v1beta1"
"storage/v1beta1",
]
pruneopts = "UT"
revision = "73d903622b7391f3312dcbac6483fed484e185f8"
version = "kubernetes-1.10.0"
[[projects]]
digest = "1:d0089d5f7811ded4279da7a8a66d2721488afec8208d86bdad3f4a20d3687e81"
name = "k8s.io/apimachinery"
packages = [
"pkg/api/errors",
@@ -450,12 +606,14 @@
"pkg/version",
"pkg/watch",
"third_party/forked/golang/netutil",
"third_party/forked/golang/reflect"
"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",
@@ -577,20 +735,73 @@
"util/flowcontrol",
"util/homedir",
"util/integer",
"util/retry"
"util/retry",
]
pruneopts = "UT"
revision = "23781f4d6632d88e869066eaebb743857aa1ef9b"
version = "v7.0.0"
[[projects]]
digest = "1:2bdbea32607f4effd9e91dadd90baab1ecf224839b613bcaa8f50db5a5f133f5"
name = "k8s.io/kubernetes"
packages = ["pkg/util/slice"]
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
inputs-digest = "afd0a3a0e96a5054e6b99afd53b78888125726fc89c62f121984cd73a6ca4fb3"
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/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/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

View File

@@ -8,20 +8,19 @@
**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 environment.
- 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-volume-provisioner) 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.
- Federation 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.
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
---------------
@@ -31,8 +30,8 @@ Getting Started
## Contributing to the project
All [members](docs/members.md) of the KubeSphere community must abide by [Code of Conduct](code-of-conduct.md). Only by respecting each other can we develop a productive, collaborative community.
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 check out how to [setup for development](docs/development.md).
You can then find out more detail [here](docs/welcome-toKubeSphere-new-developer-guide.md).

View File

@@ -1,51 +0,0 @@
# OpenPitrix Developer Guide
The developer guide is for anyone wanting to either write code which directly accesses the
OpenPitrix API, or to contribute directly to the OpenPitrix project.
## The process of developing and contributing code to the OpenPitrix project
* **Welcome to OpenPitrix (New Developer Guide)**
([welcome-to-OpenPitrix-new-developer-guide.md](welcome-to-OpenPitrix-new-developer-guide.md)):
An introductory guide to contributing to OpenPitrix.
* **On Collaborative Development** ([collab.md](collab.md)): Info on pull requests and code reviews.
* **GitHub Issues** ([issues.md](issues.md)): How incoming issues are triaged.
* **Pull Request Process** ([pull-requests.md](pull-requests.md)): When and why pull requests are closed.
* **Getting Recent Builds** ([getting-builds.md](getting-builds.md)): How to get recent builds including the latest builds that pass CI.
* **Automated Tools** ([automation.md](automation.md)): Descriptions of the automation that is running on our github repository.
## Setting up your dev environment, coding, and debugging
* **Development Guide** ([development.md](development.md)): Setting up your development environment.
* **Testing** ([testing.md](testing.md)): How to run unit, integration, and end-to-end tests in your development sandbox.
* **Hunting flaky tests** ([flaky-tests.md](flaky-tests.md)): We have a goal of 99.9% flake free tests.
Here's how to run your tests many times.
* **Logging Conventions** ([logging.md](logging.md)): Glog levels.
* **Coding Conventions** ([coding-conventions.md](coding-conventions.md)):
Coding style advice for contributors.
* **Document Conventions** ([how-to-doc.md](how-to-doc.md))
Document style advice for contributors.
* **Running a cluster locally** ([running-locally.md](running-locally.md)):
A fast and lightweight local cluster deployment for development.
## Developing against the OpenPitrix API
* The [REST API documentation](http://openpitrix.io/docs/reference/) explains the REST
API exposed by apiserver.
## Building releases
See the [openpitrix/release](https://github.com/kubernetes/release) repository for details on creating releases and related tools and helper scripts.

View File

@@ -1,74 +0,0 @@
# Developing for KubeSphere
The [community repository](https://github.com/kubesphere) hosts all information about
building KubeSphere from source, how to contribute code and documentation, who to contact about what, etc. 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).
----
## To start developing KubeSphere
First of all, you should fork the project. Then follow one of the three options below to develop the project. Please note you should replace the official repo when using __go get__ or __git clone__ below with your own one.
### 1. You have a working [Docker Compose](https://docs.docker.com/compose/install) environment [recommend].
>You need to install [Docker](https://docs.docker.com/engine/installation/) first.
```shell
$ git clone https://github.com/kubesphere/kubesphere
$ cd kubesphere
$ make build
$ make compose-up
```
Exit docker runtime environment
```shell
$ make compose-down
```
### 2. You have a working [Docker](https://docs.docker.com/engine/installation/) environment.
Exit docker runtime environment
```shell
$ docker stop $(docker ps -f name=kubesphere -q)
```
### 3. You have a working [Go](prereqs.md#setting-up-go) environment.
- Install [protoc compiler](https://github.com/google/protobuf/releases/)
- Install protoc plugin:
```shell
$ go get github.com/golang/protobuf/protoc-gen-go
$ go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
$ go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
$ go get github.com/mwitkow/go-proto-validators/protoc-gen-govalidators
```
- Get kubesphere source code and build service:
```shell
$ go get -d kubesphere.io/kubesphere
$ cd $GOPATH/src/kubesphere.io/kubesphere
$ make generate
$ GOBIN=`pwd`/bin go install ./cmd/...
```
- Start KubeSphere service:
- Exit go runtime environment
```shell
$ ps aux | grep kubesphere- | grep -v grep | awk '{print $2}' | xargs kill -9
```
----
## Test KubeSphere
Visit http://127.0.0.1:9100/swagger-ui in browser, and try it online, or test kubesphere api service via command line:
----
## DevOps
Please check [How to set up DevOps environment](devops.md).

View File

@@ -1,79 +0,0 @@
# Set Up DevOps Environment
DevOps is recommended to use for this project. Please follow the instructions below to set up your environment. We use Jenkins with Blue Ocean plugin and deploy it on Kubernetes, also continuously deploy KubeSphere on the Kubernetes cluster.
----
- [Create Kubernetes Cluster](#create-kubernetes-cluster)
- [Deploy Jenkins](#deploy-jenkins)
- [Configure Jenkins](#configure-jenkins)
- [Create a Pipeline](#create-a-pipeline)
## Create Kubernetes Cluster
We are using [Kubernetes on QingCloud](https://appcenter.qingcloud.com/apps/app-u0llx5j8) to create a kubernetes production environment by one click. Please follow the [instructions](https://appcenter-docs.qingcloud.com/user-guide/apps/docs/kubernetes/) to create your own cluster. Access the Kubernetes client using one of the following options.
- **Open VPN**<a id="openvpn"></a>: Go to the left navigation tree of the [QingCloud console](https://console.qingcloud.com), choose _Networks & CDN_, then _VPC Networks_; on the content of the VPC page, choose _Management Configuration_, _VPN Service_, then you will find _Open VPN_ service. Here is the [screenshot](images/openvpn.png) of the page.
- **Port Forwarding**<a id="port-forwarding"></a>: same as Open VPN, but choose _Port Forwarding_ on the content of VPC page instead of VPN Service; and add a rule to forward source port to the port of ssh port of the kubernetes client, for instance, forward 10007 to 22 of the kubernetes client with the private IP being 192.168.100.7. After that, you need to open the firewall to allow the port 10007 accessible from outside. Please click the _Security Group_ ID on the same page of the VPC, and add the downstream rule for the firewall.
- **VNC**: If you don't want to access the client node remotely, just go to the kubernetes cluster detailed page on the [QingCloud console](https://console.qingcloud.com), and click the windows icon aside of the client ID shown as the [screenshot](images/kubernets.png) (user/password: root/k8s). The way is not recommended, however you can check kubernetes quickly using VNC since you don't configure anything.
## Deploy Jenkins
1. Copy the [yaml file](../devops/kubernetes/jenkins-qingcloud.yaml) to the kubernetes client, and deploy
```
# kubectl apply -f jenkins-qingcloud.yaml
```
2. Access Jenkins console by opening http://\<ip\>:9200 where ip depends on how you expose the Jenkins service to outside explained below. (You can find your way to access Jenkins console such as ingress, cloud LB etc.) On the kubernetes client
```
# iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 9200 -j DNAT --to-destination "$(kubectl get svc -n jenkins --selector=app=jenkins -o jsonpath='{.items..spec.clusterIP}')":9200
# iptables -t nat -A POSTROUTING -p tcp --dport 9200 -j MASQUERADE
# sysctl -w net.ipv4.conf.eth0.route_localnet=1
```
3. Now the request to the kubernetes client port 9200 will be forwarded to the Jenkins service.
- If you use [Open VPN](#openvpn) to access the kubernetes client, then open http://\<kubernetes client private ip\>:9200 to access Jenkins console.
- If you use [Port Forwarding](#port-forwarding) to access the client, then forward the VPC port 9200 to the kubernetes client port 9200. Now open http://\<VPC EIP\>:9200 to access Jenkins console.
## Configure Jenkins
> You can refer [jenkins.io](https://jenkins.io/doc/tutorials/using-jenkins-to-build-a-java-maven-project/) about how to configure Jenkins and create a pipeline.
1. Unlock Jenkins
- Get the Adminstrator password from the log on the kubernetes client
```
# kubectl logs "$(kubectl get pods -n jenkins --selector=app=jenkins -o jsonpath='{.items..metadata.name}')" -c jenkins -n jenkins
```
- Go to Jenkins console, paste the password and continue. Install suggested plugins, then create the first admin user and save & finish.
2. Configure Jenkins
We will deploy KubeSphere application into the same Kubernetes cluster as the one that the Jenkins is running on. So we need configure the Jenkins pod to access the Kubernetes cluster, and log in docker registry given that during the [Jenkins pipeline](#create-a-pipeline) we push KubeSphere image into a registry which you can change on your own.
On the Kubernetes client, execute the following to log in Jenkins container.
```
# kubectl exec -it "$(kubectl get pods -n jenkins --selector=app=jenkins -o jsonpath='{.items..metadata.name}')" -c jenkins -n jenkins -- /bin/bash
```
After logging in the Jenkins container, then run the following to log in docker registry and prepare folder to hold kubectl configuration.
```
bash-4.3# docker login -u xxx -p xxxx
bash-4.3# mkdir /root/.kube
bash-4.3# exit
```
Once back again to the Kubernetes client, run the following to copy the tool kubectl and its configuration from the client to the Jenkins container.
```
# kubectl cp /usr/bin/kubectl jenkins/"$(kubectl get pods -n jenkins --selector=app=jenkins -o jsonpath='{.items..metadata.name}')":/usr/bin/kubectl -c jenkins
# kubectl cp /root/.kube/config jenkins/"$(kubectl get pods -n jenkins --selector=app=jenkins -o jsonpath='{.items..metadata.name}')":/root/.kube/config -c jenkins
```
## Create a pipeline
- Fork KubeSphere from github for your development. You need to change the docker repository to your own in the files [kubesphere.yaml](devops/kubernetes/kubesphere.yaml), [build-images.sh](devops/scripts/build-images.sh), [push-images.sh](devops/scripts/push-images.sh) and [clean.sh](devops/scripts/clean.sh).
- On the Jenkins panel, click _Open Blue Ocean_ and start to create a new pipeline. Choose _GitHub_, paste your access key of GitHub, select the repository you want to create a CI/CD pipeline. We already created the pipeline Jenkinsfile on the upstream repository which includes compiling KubeSphere, building images, push images, deploying the application, verifying the application and cleaning up.
- It is better to configure one more thing. On the Jenkins panel, go to the configuration of KubeSphere, check _Periodically if not otherwise run_ under _Scan Repository Triggers_ and select the interval at your will.
- If your repository is an upstream, you can select _Discover pull requests from forks_ under _Behaviors_ so that the pipeline will work for PR before merged.
- Now it is good to go. Whenever you commit a change to your forked repository, the pipeline will work during the Jenkins trigger interval.

View File

@@ -1,12 +0,0 @@
# Contributors
## Component and Member List
| Name | Leads |
|------|-------|
| Deployment | (yunify) |
| Service | (yunify) |
| Application | (yunify) |
| Cluster | Jeff (yunify) |
| App Runtime | (yunify) |
| Documents | |

View File

@@ -1,22 +0,0 @@
## QuickStart
KubeSphere uses same app-manager module from [OpenPitrix](https://github/openpitrix/openpitrix), which is another open source project initiated by QingCloud.
For testing and development purpose, follow steps below to setup app-manager service locally:
* Make sure git and docker runtime is installed in your local environment
* Clone the OpenPitrix project to your local environment:
```console
git clone https://github.com/openpitrix/openpitrix.git
```
* Get into openpitrix directory, run commands below:
```console
cd openpitrix
make build
make compose-up-app
```
## Test app-manager
Visit http://127.0.0.1:9100/swagger-ui in browser, and try it online, or test app-manager api service via command line:
```shell
$ curl http://localhost:9100/v1/apps
{"total_items":0,"total_pages":0,"page_size":10,"current_page":1}

View File

@@ -12,7 +12,6 @@ branch, but release branches should not change.
- [Prerequisites](#prerequisites)
- [Setting up Go](#setting-up-go)
- [Setting up Swagger](#setting-up-swagger)
- [To start developing KubeSphere](#to-start-developing-kubesphere)
- [DevOps](#devops)
@@ -37,20 +36,6 @@ $ export GOPATH=~/go
$ export PATH=$PATH:$GOPATH/bin
```
### Setting up Swagger
KubeSphere is using [OpenAPI/Swagger](https://swagger.io) to develop API, so follow
[the instructions](https://github.com/go-swagger/go-swagger/tree/master/docs) to
install Swagger. If you are not familar with Swagger, please read the
[tutorial](http://apihandyman.io/writing-openapi-swagger-specification-tutorial-part-1-introduction/#writing-openapi-fka-swagger-specification-tutorial). If you install Swagger using docker distribution,
please run
```shell
$ docker pull quay.io/goswagger/swagger
$ alias swagger="docker run --rm -it -e GOPATH=$GOPATH:/go -v $HOME:$HOME -w $(pwd) quay.io/goswagger/swagger"
$ swagger version
```
## To start developing KubeSphere
There are two options to get KubeSphere source code and build the project:
@@ -70,7 +55,3 @@ $ git clone https://github.com/kubesphere/kubesphere
$ cd kubesphere
$ make
```
## DevOps
Please check [How to set up DevOps environment](devops.md)

View File

@@ -4,7 +4,6 @@ This doc explains the process and best practices for submitting a PR to the [Kub
- [Before You Submit a PR](#before-you-submit-a-pr)
* [Run Local Verifications](#run-local-verifications)
* [Sign the CLA](#sign-the-cla)
- [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)
@@ -38,22 +37,10 @@ This guide is for contributors who already have a PR to submit. If you're lookin
You can run these local verifications before you submit your PR to predict the
pass or fail of continuous integration.
* Run and pass `make verify` (can take 30-40 minutes)
* Run and pass `make test`
* Run and pass `make test-integration`
## Sign the CLA
You must sign the CLA before your first contribution. [Read more about the CLA.](https://github.com/kubesphere/kubesphere/docs/CLA.md)
If you haven't signed the Contributor License Agreement (CLA) before making a PR,
the `@o8x-ci-robot` will leave a comment with instructions on how to sign the CLA.
# 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.
- Sign the CLA (prerequisite)
- Make the PR
- Release notes - do one of the following:
- Add notes in the release notes block, or
@@ -152,15 +139,15 @@ If you want to solicit reviews before the implementation of your pull request is
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
## Comment Commands Reference//TODO
[The commands doc]() contains a reference for all comment commands. //TODO
## Automation
## 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
## 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
@@ -212,7 +199,7 @@ Let's talk about best practices so your PR gets reviewed quickly.
## 0. Familiarize yourself with project conventions
* [Development guide](development.md)
* [Development guide](code-of-conduct.md)
## 1. Is the feature wanted? Make a Design Doc or Sketch PR
@@ -220,7 +207,7 @@ Are you sure Feature-X is something the KubeSphere team wants or will accept? Is
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](), or reach out to the affected special interest group (SIG). Here's a [list of SIGs](https://github.com/KubeSphere/KubeSphere/docs/sig-list.md)
- 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

View File

@@ -2,57 +2,30 @@
Welcome to KubeSphere! (New Developer Guide)
============================================
_This document assumes that you know what KubeSphere does. If you don't,
try the demo at [https://o8x.io/](https://kubesphere.io/)._
_This document assumes that you know what KubeSphere does.
Introduction
------------
Have you ever wanted to contribute to the coolest cloud technology? This
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/) or join our [mailing
list](https://groups.google.com/forum/#!forum/KubeSphere-dev). If you join the
[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.
Special Interest Groups
-----------------------
KubeSphere developers work in teams called Special Interest Groups (SIGs). At
the time of this writing there are [2 SIGs](sig-list.md).
The developers within each SIG have autonomy and ownership over that SIG's part
of KubeSphere. SIGs organize themselves by meeting regularly and submitting
markdown design documents to the
[KubeSphere/community](https://github.com/KubeSphere/community) repository.
Like everything else in KubeSphere, a SIG is an open, community, effort. Anybody
is welcome to jump into a SIG and begin fixing issues, critiquing design
proposals and reviewing code.
Most people who visit the KubeSphere repository for the first time are
bewildered by the thousands of [open
issues](https://github.com/KubeSphere/KubeSphere/issues) in our main repository.
But now that you know about SIGs, it's easy to filter by labels to see what's
going on in a particular SIG. For more information about our issue system, check
out
[issues.md](https://github.com/KubeSphere/community/blob/master/contributors/devel/issues.md).
//TODO
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
[github.com/KubeSphere/KubeSphere/docs/](https://github.com/KubeSphere/KubeSphere/docs/).
Check out
[development.md](https://github.com/KubeSphere/KubeSphere/docs/development.md)
for more details.
[docs.kubesphere.io](https://docs.kubesphere.io).
Pull-Request Process
--------------------
@@ -61,21 +34,4 @@ 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.
The Release Process and Code Freeze
-----------------------------------
Every so often @o8x-merge-robot will refuse to merge your PR, saying something
about release milestones. This happens when we are in a code freeze for a
release. In order to ensure KubeSphere is stable, we stop merging everything
that's not a bugfix, then focus on making all the release tests pass. This code
freeze usually lasts two weeks and happens once per quarter.
If you're new to KubeSphere, you won't have to worry about this too much. After
you've contributed for a few months, you will be added as a [community
member](https://github.com/KubeSphere/KubeSphere/docs/membership.md)
and take ownership of some of the tests. At this point, you'll work with members
of your SIG to review PRs coming into your area and track down issues that occur
in tests.
Thanks for reading!

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,60 @@
<!-- 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>

View File

@@ -0,0 +1,67 @@
<!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

View File

@@ -0,0 +1 @@
{"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

View File

@@ -21,14 +21,23 @@ import (
"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) {
ws.Route(ws.GET(subPath).To(getClusterQuota).Produces(restful.MIME_JSON))
ws.Route(ws.GET(subPath + "/namespaces/{namespace}").To(getNamespaceQuota).Produces(restful.MIME_JSON))
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))
}

View File

@@ -21,22 +21,33 @@ import (
"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) {
ws.Route(ws.GET(subPath + "/{resource}").To(listResource).Produces(restful.MIME_JSON))
tags := []string{"resources"}
ws.Route(ws.GET(subPath+"/{resource}").To(handleResource).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("paging",
"support paging function").DataType("string")).Writes(models.ResourceList{}))
}
func listResource(req *restful.Request, resp *restful.Response) {
func handleResource(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")
res, err := models.ListResource(resource, conditions, paging)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
@@ -45,3 +56,30 @@ func listResource(req *restful.Request, resp *restful.Response) {
resp.WriteEntity(res)
}
func handleApplication(req *restful.Request, resp *restful.Response) {
//searchWord := req.QueryParameter("search-word")
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)
}

View File

@@ -22,6 +22,8 @@ import (
"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/iam"
@@ -29,10 +31,17 @@ import (
func Register(ws *restful.WebService, subPath string) {
ws.Route(ws.POST(subPath).To(createUser).Consumes("*/*").Produces(restful.MIME_JSON))
ws.Route(ws.DELETE(subPath).To(delUser).Produces(restful.MIME_JSON))
ws.Route(ws.GET(subPath + "/kubectl").To(getKubectl).Produces(restful.MIME_JSON))
ws.Route(ws.GET(subPath + "/kubeconfig").To(getKubeconfig).Produces(restful.MIME_JSON))
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))
}
@@ -46,7 +55,7 @@ func createUser(req *restful.Request, resp *restful.Response) {
return
}
err = models.CreateKubectlPod(user)
err = models.CreateKubectlDeploy(user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})
@@ -60,7 +69,7 @@ func delUser(req *restful.Request, resp *restful.Response) {
user := req.PathParameter("user")
err := models.DelKubectlPod(user)
err := models.DelKubectlDeploy(user)
if err != nil && !apierrors.IsNotFound(err) {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()})

View File

@@ -21,14 +21,19 @@ import (
"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) {
ws.Route(ws.GET(subPath).To(getClusterStatus).Produces(restful.MIME_JSON))
ws.Route(ws.GET(subPath + "/namespaces/{namespace}").To(getNamespaceStatus).Produces(restful.MIME_JSON))
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))
}

View File

@@ -23,14 +23,20 @@ import (
"github.com/emicklei/go-restful"
"github.com/golang/glog"
"k8s.io/api/core/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
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"
_ "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/options"
)
@@ -61,19 +67,50 @@ func newKubeSphereServer(options *options.ServerRunOptions) *kubeSphereServer {
func preCheck() error {
k8sClient := client.NewK8sClient()
nsList, err := k8sClient.CoreV1().Namespaces().List(meta_v1.ListOptions{})
if err != nil {
_, err := k8sClient.CoreV1().Namespaces().Get(constants.KubeSphereControlNamespace, metaV1.GetOptions{})
if err != nil && !errors.IsNotFound(err) {
return err
}
for _, ns := range nsList.Items {
if ns.Name == constants.KubeSphereControlNamespace {
return nil
if errors.IsNotFound(err) {
namespace := v1.Namespace{ObjectMeta: metaV1.ObjectMeta{Name: constants.KubeSphereControlNamespace}}
_, err = k8sClient.CoreV1().Namespaces().Create(&namespace)
if err != nil {
return err
}
}
namespace := v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: constants.KubeSphereControlNamespace}}
_, err = k8sClient.CoreV1().Namespaces().Create(&namespace)
_, err = k8sClient.AppsV1().Deployments(constants.KubeSphereControlNamespace).Get(constants.AdminUserName, metaV1.GetOptions{})
if errors.IsNotFound(err) {
models.CreateKubeConfig(constants.AdminUserName)
models.CreateKubectlDeploy(constants.AdminUserName)
return nil
}
return err
}
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 {
@@ -83,6 +120,8 @@ func (server *kubeSphereServer) run() {
go controllers.Run()
registerSwagger()
if len(server.certFile) > 0 && len(server.keyFile) > 0 {
servingCert, err := tls.LoadX509KeyPair(server.certFile, server.keyFile)
if err != nil {

View File

@@ -34,8 +34,8 @@ const (
KubeSphereNamespace = "kubesphere-system"
KubeSphereControlNamespace = "kubesphere-controls-system"
IngressControllerNamespace = KubeSphereControlNamespace
DataHome = "/etc/kubesphere"
IngressControllerFolder = DataHome + "/ingress-controller"
IngressControllerPrefix = "kubesphere-router-"
AdminUserName = "admin"
DataHome = "/etc/kubesphere"
IngressControllerFolder = DataHome + "/ingress-controller"
IngressControllerPrefix = "kubesphere-router-"
)

View File

@@ -0,0 +1,499 @@
/*
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) getCreator(desc string) string {
// var dc description
// err := json.Unmarshal([]byte(desc), &dc)
// if err != nil {
// return unknown
// }
// return dc.Creator
//}
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
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
}

View File

@@ -27,9 +27,11 @@ import (
"k8s.io/client-go/tools/cache"
)
const systemPrefix = "system:"
func (ctl *ClusterRoleCtl) generateObject(item v1.ClusterRole) *ClusterRole {
name := item.Name
if strings.HasPrefix(name, "system:") {
if strings.HasPrefix(name, systemPrefix) {
return nil
}
@@ -43,30 +45,22 @@ func (ctl *ClusterRoleCtl) generateObject(item v1.ClusterRole) *ClusterRole {
return object
}
func (ctl *ClusterRoleCtl) listAndWatch() {
defer func() {
close(ctl.aliveChan)
if err := recover(); err != nil {
glog.Error(err)
return
}
}()
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{})
k8sClient := ctl.K8sClient
kubeInformerFactory := informers.NewSharedInformerFactory(k8sClient, time.Second*resyncCircle)
informer := kubeInformerFactory.Rbac().V1().ClusterRoles().Informer()
lister := kubeInformerFactory.Rbac().V1().ClusterRoles().Lister()
ctl.initListerAndInformer()
list, err := lister.List(labels.Everything())
list, err := ctl.lister.List(labels.Everything())
if err != nil {
glog.Error(err)
return
@@ -74,10 +68,38 @@ func (ctl *ClusterRoleCtl) listAndWatch() {
for _, item := range list {
obj := ctl.generateObject(*item)
db.Create(obj)
if obj != nil {
db.Create(obj)
}
}
ctl.informer.Run(stopChan)
}
func (ctl *ClusterRoleCtl) total() int {
list, err := ctl.lister.List(labels.Everything())
if err != nil {
glog.Errorf("count %s falied, reason:%s", err, ctl.Name())
return 0
}
count := 0
for _, item := range list {
if !strings.HasPrefix(item.Name, systemPrefix) {
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{}) {
@@ -102,13 +124,15 @@ func (ctl *ClusterRoleCtl) listAndWatch() {
},
})
informer.Run(ctl.stopChan)
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)
}
@@ -124,10 +148,3 @@ func (ctl *ClusterRoleCtl) ListWithConditions(conditions string, paging *Paging)
return total, list, nil
}
func (ctl *ClusterRoleCtl) Count(namespace string) int {
var count int
db := ctl.DB
db.Model(&ClusterRole{}).Count(&count)
return count
}

View File

@@ -16,7 +16,17 @@ limitations under the License.
package controllers
import "github.com/jinzhu/gorm"
import (
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/golang/glog"
"github.com/jinzhu/gorm"
"github.com/pkg/errors"
)
func listWithConditions(db *gorm.DB, total *int, object, list interface{}, conditions string, paging *Paging, order string) {
if len(conditions) == 0 {
@@ -50,3 +60,83 @@ func countWithConditions(db *gorm.DB, conditions string, object interface{}) int
}
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))
}
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)
for {
select {
case <-ctl.chanStop():
return
default:
time.Sleep(30 * time.Minute)
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) {
defer handleCrash(ctl)
stopChan := make(chan struct{})
go ctl.sync(stopChan)
checkAndResync(ctl, stopChan)
}

View File

@@ -61,30 +61,21 @@ func (ctl *DaemonsetCtl) generateObject(item v1.DaemonSet) *Daemonset {
return object
}
func (ctl *DaemonsetCtl) listAndWatch() {
defer func() {
close(ctl.aliveChan)
if err := recover(); err != nil {
glog.Error(err)
return
}
}()
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{})
k8sClient := ctl.K8sClient
kubeInformerFactory := informers.NewSharedInformerFactory(k8sClient, time.Second*resyncCircle)
informer := kubeInformerFactory.Apps().V1().DaemonSets().Informer()
lister := kubeInformerFactory.Apps().V1().DaemonSets().Lister()
list, err := lister.List(labels.Everything())
ctl.initListerAndInformer()
list, err := ctl.lister.List(labels.Everything())
if err != nil {
glog.Error(err)
return
@@ -93,9 +84,27 @@ func (ctl *DaemonsetCtl) listAndWatch() {
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{}) {
@@ -117,7 +126,7 @@ func (ctl *DaemonsetCtl) listAndWatch() {
},
})
informer.Run(ctl.stopChan)
ctl.informer = informer
}
func (ctl *DaemonsetCtl) CountWithConditions(conditions string) int {
@@ -138,13 +147,13 @@ func (ctl *DaemonsetCtl) ListWithConditions(conditions string, paging *Paging) (
return total, list, nil
}
func (ctl *DaemonsetCtl) Count(namespace string) int {
var count int
db := ctl.DB
if len(namespace) == 0 {
db.Model(&Daemonset{}).Count(&count)
} else {
db.Model(&Daemonset{}).Where("namespace = ?", namespace).Count(&count)
}
return count
}
//func (ctl *DaemonsetCtl) Count(namespace string) int {
// var count int
// db := ctl.DB
// if len(namespace) == 0 {
// db.Model(&Daemonset{}).Count(&count)
// } else {
// db.Model(&Daemonset{}).Where("namespace = ?", namespace).Count(&count)
// }
// return count
//}

View File

@@ -21,9 +21,8 @@ import (
"github.com/golang/glog"
"k8s.io/api/apps/v1"
"k8s.io/client-go/informers"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/informers"
"k8s.io/client-go/tools/cache"
)
@@ -65,15 +64,11 @@ func (ctl *DeploymentCtl) generateObject(item v1.Deployment) *Deployment {
App: app, UpdateTime: updateTime, Status: status, Annotation: Annotation{item.Annotations}}
}
func (ctl *DeploymentCtl) listAndWatch() {
defer func() {
close(ctl.aliveChan)
if err := recover(); err != nil {
glog.Error(err)
return
}
}()
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{})
@@ -81,12 +76,8 @@ func (ctl *DeploymentCtl) listAndWatch() {
db = db.CreateTable(&Deployment{})
k8sClient := ctl.K8sClient
kubeInformerFactory := informers.NewSharedInformerFactory(k8sClient, time.Second*resyncCircle)
informer := kubeInformerFactory.Apps().V1().Deployments().Informer()
lister := kubeInformerFactory.Apps().V1().Deployments().Lister()
list, err := lister.List(labels.Everything())
ctl.initListerAndInformer()
list, err := ctl.lister.List(labels.Everything())
if err != nil {
glog.Error(err)
return
@@ -95,9 +86,29 @@ func (ctl *DeploymentCtl) listAndWatch() {
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{}) {
@@ -118,8 +129,7 @@ func (ctl *DeploymentCtl) listAndWatch() {
},
})
informer.Run(ctl.stopChan)
ctl.informer = informer
}
func (ctl *DeploymentCtl) CountWithConditions(conditions string) int {
@@ -139,14 +149,3 @@ func (ctl *DeploymentCtl) ListWithConditions(conditions string, paging *Paging)
return total, list, nil
}
func (ctl *DeploymentCtl) Count(namespace string) int {
var count int
db := ctl.DB
if len(namespace) == 0 {
db.Model(&Deployment{}).Count(&count)
} else {
db.Model(&Deployment{}).Where("namespace = ?", namespace).Count(&count)
}
return count
}

View File

@@ -20,6 +20,8 @@ import (
"strings"
"time"
"encoding/json"
"github.com/golang/glog"
"k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/labels"
@@ -46,35 +48,41 @@ func (ctl *IngressCtl) generateObject(item v1beta1.Ingress) *Ingress {
ip = strings.Join(ipList, ",")
}
object := &Ingress{Namespace: namespace, Name: name, TlsTermination: tls, Ip: ip, CreateTime: createTime, Annotation: Annotation{item.Annotations}}
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, TlsTermination: tls, Ip: ip, CreateTime: createTime, Annotation: Annotation{item.Annotations}, Rules: string(ruleStr)}
return object
}
func (ctl *IngressCtl) listAndWatch() {
defer func() {
close(ctl.aliveChan)
if err := recover(); err != nil {
glog.Error(err)
return
}
}()
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{})
k8sClient := ctl.K8sClient
kubeInformerFactory := informers.NewSharedInformerFactory(k8sClient, time.Second*resyncCircle)
informer := kubeInformerFactory.Extensions().V1beta1().Ingresses().Informer()
lister := kubeInformerFactory.Extensions().V1beta1().Ingresses().Lister()
list, err := lister.List(labels.Everything())
ctl.initListerAndInformer()
list, err := ctl.lister.List(labels.Everything())
if err != nil {
glog.Error(err)
return
@@ -83,9 +91,28 @@ func (ctl *IngressCtl) listAndWatch() {
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{}) {
@@ -107,7 +134,7 @@ func (ctl *IngressCtl) listAndWatch() {
},
})
informer.Run(ctl.stopChan)
ctl.informer = informer
}
func (ctl *IngressCtl) CountWithConditions(conditions string) int {
@@ -128,13 +155,13 @@ func (ctl *IngressCtl) ListWithConditions(conditions string, paging *Paging) (in
return total, list, nil
}
func (ctl *IngressCtl) Count(namespace string) int {
var count int
db := ctl.DB
if len(namespace) == 0 {
db.Model(&Ingress{}).Count(&count)
} else {
db.Model(&Ingress{}).Where("namespace = ?", namespace).Count(&count)
}
return count
}
//func (ctl *IngressCtl) Count(namespace string) int {
// var count int
// db := ctl.DB
// if len(namespace) == 0 {
// db.Model(&Ingress{}).Count(&count)
// } else {
// db.Model(&Ingress{}).Where("namespace = ?", namespace).Count(&count)
// }
// return count
//}

View File

@@ -19,9 +19,6 @@ package controllers
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/golang/glog"
@@ -30,12 +27,17 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
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/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"
@@ -69,25 +71,6 @@ type DeleteRunTime struct {
RuntimeId []string `json:"runtime_id"`
}
func makeHttpRequest(method, url, data string) ([]byte, error) {
req, err := http.NewRequest(method, url, strings.NewReader(data))
if err != nil {
glog.Error(err)
return nil, err
}
httpClient := &http.Client{}
resp, err := httpClient.Do(req)
if err != nil {
glog.Error(err)
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
return body, err
}
func (ctl *NamespaceCtl) getKubeConfig(user string) (string, error) {
k8sClient := client.NewK8sClient()
configmap, err := k8sClient.CoreV1().ConfigMaps(kubectlNamespace).Get(user, metaV1.GetOptions{})
@@ -247,6 +230,69 @@ func (ctl *NamespaceCtl) createRoleAndRuntime(item v1.Namespace) {
}
}
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 {
name := item.Name
@@ -262,15 +308,11 @@ func (ctl *NamespaceCtl) generateObject(item v1.Namespace) *Namespace {
return object
}
func (ctl *NamespaceCtl) listAndWatch() {
defer func() {
close(ctl.aliveChan)
if err := recover(); err != nil {
glog.Error(err)
return
}
}()
func (ctl *NamespaceCtl) Name() string {
return ctl.CommonAttribute.Name
}
func (ctl *NamespaceCtl) sync(stopChan chan struct{}) {
db := ctl.DB
if db.HasTable(&Namespace{}) {
@@ -279,12 +321,8 @@ func (ctl *NamespaceCtl) listAndWatch() {
db = db.CreateTable(&Namespace{})
k8sClient := ctl.K8sClient
kubeInformerFactory := informers.NewSharedInformerFactory(k8sClient, time.Second*resyncCircle)
informer := kubeInformerFactory.Core().V1().Namespaces().Informer()
lister := kubeInformerFactory.Core().V1().Namespaces().Lister()
list, err := lister.List(labels.Everything())
ctl.initListerAndInformer()
list, err := ctl.lister.List(labels.Everything())
if err != nil {
glog.Error(err)
return
@@ -294,9 +332,28 @@ func (ctl *NamespaceCtl) listAndWatch() {
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{}) {
@@ -304,6 +361,7 @@ func (ctl *NamespaceCtl) listAndWatch() {
mysqlObject := ctl.generateObject(*object)
db.Create(mysqlObject)
ctl.createRoleAndRuntime(*object)
ctl.createCephSecretAfterNewNs(*object)
},
UpdateFunc: func(old, new interface{}) {
object := new.(*v1.Namespace)
@@ -321,7 +379,7 @@ func (ctl *NamespaceCtl) listAndWatch() {
},
})
informer.Run(ctl.stopChan)
ctl.informer = informer
}
func (ctl *NamespaceCtl) CountWithConditions(conditions string) int {
@@ -339,26 +397,21 @@ func (ctl *NamespaceCtl) ListWithConditions(conditions string, paging *Paging) (
listWithConditions(ctl.DB, &total, &object, &list, conditions, paging, order)
for index := range list {
usage, err := ctl.GetNamespaceQuota(list[index].Name)
if err == nil {
list[index].Usaeg = usage
if paging != nil {
for index := range list {
usage, err := ctl.GetNamespaceQuota(list[index].Name)
if err == nil {
list[index].Usaeg = usage
}
}
}
return total, list, nil
}
func (ctl *NamespaceCtl) Count(namespace string) int {
var count int
db := ctl.DB
db.Model(&Namespace{}).Count(&count)
return count
}
func getUsage(namespace, resource string) int {
ctl := rec.controllers[resource]
return ctl.Count(namespace)
ctl := ResourceControllers.Controllers[resource]
return ctl.CountWithConditions(fmt.Sprintf("namespace = '%s' ", namespace))
}
func (ctl *NamespaceCtl) GetNamespaceQuota(namespace string) (v1.ResourceList, error) {
@@ -373,7 +426,7 @@ func (ctl *NamespaceCtl) GetNamespaceQuota(namespace string) (v1.ResourceList, e
usage[v1.ResourceName(resourceName)] = quantity
}
podCtl := rec.controllers[Pods]
podCtl := ResourceControllers.Controllers[Pods]
var quantity resource.Quantity
used := podCtl.CountWithConditions(fmt.Sprintf("status=\"%s\" And namespace=\"%s\"", "Running", namespace))
quantity.Set(int64(used))

View File

@@ -199,25 +199,24 @@ func (ctl *PodCtl) generateObject(item v1.Pod) *Pod {
return object
}
func (ctl *PodCtl) listAndWatch() {
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{})
k8sClient := ctl.K8sClient
kubeInformerFactory := informers.NewSharedInformerFactory(k8sClient, time.Second*resyncCircle)
informer := kubeInformerFactory.Core().V1().Pods().Informer()
lister := kubeInformerFactory.Core().V1().Pods().Lister()
list, err := lister.List(labels.Everything())
ctl.initListerAndInformer()
list, err := ctl.lister.List(labels.Everything())
if err != nil {
glog.Error(err)
panic(err)
return
}
for _, item := range list {
@@ -225,6 +224,26 @@ func (ctl *PodCtl) listAndWatch() {
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)
@@ -249,7 +268,7 @@ func (ctl *PodCtl) listAndWatch() {
},
})
informer.Run(ctl.stopChan)
ctl.informer = informer
}
func (ctl *PodCtl) CountWithConditions(conditions string) int {
@@ -270,13 +289,13 @@ func (ctl *PodCtl) ListWithConditions(conditions string, paging *Paging) (int, i
return total, list, nil
}
func (ctl *PodCtl) Count(namespace string) int {
var count int
db := ctl.DB
if len(namespace) == 0 {
db.Model(&Pod{}).Count(&count)
} else {
db.Model(&Pod{}).Where("namespace = ?", namespace).Count(&count)
}
return count
}
//func (ctl *PodCtl) Count(namespace string) int {
// var count int
// db := ctl.DB
// if len(namespace) == 0 {
// db.Model(&Pod{}).Count(&count)
// } else {
// db.Model(&Pod{}).Where("namespace = ?", namespace).Count(&count)
// }
// return count
//}

View File

@@ -64,30 +64,21 @@ func (ctl *PvcCtl) generateObject(item *v1.PersistentVolumeClaim) *Pvc {
return object
}
func (ctl *PvcCtl) listAndWatch() {
defer func() {
close(ctl.aliveChan)
if err := recover(); err != nil {
glog.Error(err)
return
}
}()
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{})
k8sClient := ctl.K8sClient
kubeInformerFactory := informers.NewSharedInformerFactory(k8sClient, time.Second*resyncCircle)
informer := kubeInformerFactory.Core().V1().PersistentVolumeClaims().Informer()
lister := kubeInformerFactory.Core().V1().PersistentVolumeClaims().Lister()
list, err := lister.List(labels.Everything())
ctl.initListerAndInformer()
list, err := ctl.lister.List(labels.Everything())
if err != nil {
glog.Error(err)
return
@@ -96,9 +87,28 @@ func (ctl *PvcCtl) listAndWatch() {
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{}) {
@@ -119,7 +129,7 @@ func (ctl *PvcCtl) listAndWatch() {
},
})
informer.Run(ctl.stopChan)
ctl.informer = informer
}
func (ctl *PvcCtl) CountWithConditions(conditions string) int {
@@ -153,13 +163,13 @@ func (ctl *PvcCtl) ListWithConditions(conditions string, paging *Paging) (int, i
return total, list, nil
}
func (ctl *PvcCtl) Count(namespace string) int {
var count int
db := ctl.DB
if len(namespace) == 0 {
db.Model(&Pvc{}).Count(&count)
} else {
db.Model(&Pvc{}).Where("namespace = ?", namespace).Count(&count)
}
return count
}
//func (ctl *PvcCtl) Count(namespace string) int {
// var count int
// db := ctl.DB
// if len(namespace) == 0 {
// db.Model(&Pvc{}).Count(&count)
// } else {
// db.Model(&Pvc{}).Where("namespace = ?", namespace).Count(&count)
// }
// return count
//}

View File

@@ -29,7 +29,7 @@ import (
func (ctl *RoleCtl) generateObject(item v1.Role) *Role {
name := item.Name
if strings.HasPrefix(name, "system:") {
if strings.HasPrefix(name, systemPrefix) {
return nil
}
namespace := item.Namespace
@@ -43,30 +43,21 @@ func (ctl *RoleCtl) generateObject(item v1.Role) *Role {
return object
}
func (ctl *RoleCtl) listAndWatch() {
defer func() {
close(ctl.aliveChan)
if err := recover(); err != nil {
glog.Error(err)
return
}
}()
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{})
k8sClient := ctl.K8sClient
kubeInformerFactory := informers.NewSharedInformerFactory(k8sClient, time.Second*resyncCircle)
informer := kubeInformerFactory.Rbac().V1().Roles().Informer()
lister := kubeInformerFactory.Rbac().V1().Roles().Lister()
list, err := lister.List(labels.Everything())
ctl.initListerAndInformer()
list, err := ctl.lister.List(labels.Everything())
if err != nil {
glog.Error(err)
return
@@ -74,10 +65,39 @@ func (ctl *RoleCtl) listAndWatch() {
for _, item := range list {
obj := ctl.generateObject(*item)
db.Create(obj)
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 falied, reason:%s", err, ctl.Name())
return 0
}
count := 0
for _, item := range list {
if !strings.HasPrefix(item.Name, systemPrefix) {
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{}) {
@@ -103,7 +123,7 @@ func (ctl *RoleCtl) listAndWatch() {
},
})
informer.Run(ctl.stopChan)
ctl.informer = informer
}
func (ctl *RoleCtl) CountWithConditions(conditions string) int {
@@ -124,9 +144,9 @@ func (ctl *RoleCtl) ListWithConditions(conditions string, paging *Paging) (int,
return total, list, nil
}
func (ctl *RoleCtl) Count(namespace string) int {
var count int
db := ctl.DB
db.Model(&Role{}).Where("namespace = ?", namespace).Count(&count)
return count
}
//func (ctl *RoleCtl) Count(namespace string) int {
// var count int
// db := ctl.DB
// db.Model(&Role{}).Where("namespace = ?", namespace).Count(&count)
// return count
//}

View File

@@ -27,45 +27,46 @@ import (
)
type resourceControllers struct {
controllers map[string]Controller
Controllers map[string]Controller
k8sClient *kubernetes.Clientset
}
var stopChan chan struct{}
var rec resourceControllers
var ResourceControllers resourceControllers
func (rec *resourceControllers) runContoller(name string) {
var ctl Controller
attr := CommonAttribute{DB: client.NewDBClient(), K8sClient: rec.k8sClient, stopChan: stopChan, aliveChan: make(chan struct{})}
attr := CommonAttribute{DB: client.NewDBClient(), K8sClient: rec.k8sClient, stopChan: stopChan,
aliveChan: make(chan struct{}), Name: name}
switch name {
case Deployments:
ctl = &DeploymentCtl{attr}
ctl = &DeploymentCtl{CommonAttribute: attr}
case Statefulsets:
ctl = &StatefulsetCtl{attr}
ctl = &StatefulsetCtl{CommonAttribute: attr}
case Daemonsets:
ctl = &DaemonsetCtl{attr}
ctl = &DaemonsetCtl{CommonAttribute: attr}
case Ingresses:
ctl = &IngressCtl{attr}
ctl = &IngressCtl{CommonAttribute: attr}
case PersistentVolumeClaim:
ctl = &PvcCtl{attr}
ctl = &PvcCtl{CommonAttribute: attr}
case Roles:
ctl = &RoleCtl{attr}
ctl = &RoleCtl{CommonAttribute: attr}
case ClusterRoles:
ctl = &ClusterRoleCtl{attr}
ctl = &ClusterRoleCtl{CommonAttribute: attr}
case Services:
ctl = &ServiceCtl{attr}
ctl = &ServiceCtl{CommonAttribute: attr}
case Pods:
ctl = &PodCtl{attr}
ctl = &PodCtl{CommonAttribute: attr}
case Namespaces:
ctl = &NamespaceCtl{attr}
ctl = &NamespaceCtl{CommonAttribute: attr}
case StorageClasses:
ctl = &StorageClassCtl{attr}
ctl = &StorageClassCtl{CommonAttribute: attr}
default:
return
}
rec.controllers[name] = ctl
go ctl.listAndWatch()
rec.Controllers[name] = ctl
go listAndWatch(ctl)
}
@@ -93,22 +94,23 @@ func Run() {
stopChan := make(chan struct{})
defer close(stopChan)
rec = resourceControllers{k8sClient: client.NewK8sClient(), controllers: make(map[string]Controller)}
k8sClient := client.NewK8sClient()
ResourceControllers = resourceControllers{k8sClient: k8sClient, Controllers: make(map[string]Controller)}
for _, item := range []string{Deployments, Statefulsets, Daemonsets, PersistentVolumeClaim, Pods, Services,
Ingresses, Roles, ClusterRoles, Namespaces, StorageClasses} {
rec.runContoller(item)
ResourceControllers.runContoller(item)
}
go dbHealthCheck(client.NewDBClient())
for {
for ctlName, controller := range rec.controllers {
for ctlName, controller := range ResourceControllers.Controllers {
select {
case _, isClose := <-controller.chanAlive():
if !isClose {
glog.Errorf("controller %s have stopped, restart it", ctlName)
rec.runContoller(ctlName)
ResourceControllers.runContoller(ctlName)
}
default:
time.Sleep(5 * time.Second)

View File

@@ -26,7 +26,7 @@ import (
"k8s.io/client-go/tools/cache"
)
func (ctl *ServiceCtl) loadBalancerStatusStringer(item v1.Service) string {
func loadBalancerStatusStringer(item v1.Service) string {
ingress := item.Status.LoadBalancer.Ingress
result := sets.NewString()
for i := range ingress {
@@ -41,7 +41,7 @@ func (ctl *ServiceCtl) loadBalancerStatusStringer(item v1.Service) string {
return r
}
func (ctl *ServiceCtl) getExternalIp(item v1.Service) string {
func getExternalIp(item v1.Service) string {
switch item.Spec.Type {
case "ClusterIP", "NodePort":
if len(item.Spec.ExternalIPs) > 0 {
@@ -51,7 +51,7 @@ func (ctl *ServiceCtl) getExternalIp(item v1.Service) string {
return item.Spec.ExternalName
case "LoadBalancer":
lbIps := ctl.loadBalancerStatusStringer(item)
lbIps := loadBalancerStatusStringer(item)
if len(item.Spec.ExternalIPs) > 0 {
results := []string{}
if len(lbIps) > 0 {
@@ -68,12 +68,11 @@ func (ctl *ServiceCtl) getExternalIp(item v1.Service) string {
return ""
}
func (ctl *ServiceCtl) generateObject(item v1.Service) *Service {
func generateSvcObject(item v1.Service) *Service {
name := item.Name
namespace := item.Namespace
createTime := item.CreationTimestamp.Time
externalIp := ctl.getExternalIp(item)
externalIp := getExternalIp(item)
serviceType := item.Spec.Type
vip := item.Spec.ClusterIP
ports := ""
@@ -129,17 +128,18 @@ func (ctl *ServiceCtl) generateObject(item v1.Service) *Service {
}
return object
}
func (ctl *ServiceCtl) listAndWatch() {
defer func() {
close(ctl.aliveChan)
if err := recover(); err != nil {
glog.Error(err)
return
}
}()
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{}) {
@@ -148,12 +148,8 @@ func (ctl *ServiceCtl) listAndWatch() {
db = db.CreateTable(&Service{})
k8sClient := ctl.K8sClient
kubeInformerFactory := informers.NewSharedInformerFactory(k8sClient, time.Second*resyncCircle)
informer := kubeInformerFactory.Core().V1().Services().Informer()
lister := kubeInformerFactory.Core().V1().Services().Lister()
list, err := lister.List(labels.Everything())
ctl.initListerAndInformer()
list, err := ctl.lister.List(labels.Everything())
if err != nil {
glog.Error(err)
return
@@ -164,6 +160,25 @@ func (ctl *ServiceCtl) listAndWatch() {
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{}) {
@@ -185,7 +200,7 @@ func (ctl *ServiceCtl) listAndWatch() {
},
})
informer.Run(ctl.stopChan)
ctl.informer = informer
}
func (ctl *ServiceCtl) CountWithConditions(conditions string) int {
@@ -205,14 +220,3 @@ func (ctl *ServiceCtl) ListWithConditions(conditions string, paging *Paging) (in
return total, list, nil
}
func (ctl *ServiceCtl) Count(namespace string) int {
var count int
db := ctl.DB
if len(namespace) == 0 {
db.Model(&Service{}).Count(&count)
} else {
db.Model(&Service{}).Where("namespace = ?", namespace).Count(&count)
}
return count
}

View File

@@ -62,27 +62,21 @@ func (ctl *StatefulsetCtl) generateObject(item v1.StatefulSet) *Statefulset {
return statefulSetObject
}
func (ctl *StatefulsetCtl) listAndWatch() {
defer func() {
close(ctl.aliveChan)
if err := recover(); err != nil {
glog.Error(err)
return
}
}()
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{})
k8sClient := ctl.K8sClient
kubeInformerFactory := informers.NewSharedInformerFactory(k8sClient, time.Second*resyncCircle)
informer := kubeInformerFactory.Apps().V1().StatefulSets().Informer()
lister := kubeInformerFactory.Apps().V1().StatefulSets().Lister()
list, err := lister.List(labels.Everything())
ctl.initListerAndInformer()
list, err := ctl.lister.List(labels.Everything())
if err != nil {
glog.Error(err)
return
@@ -91,9 +85,28 @@ func (ctl *StatefulsetCtl) listAndWatch() {
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{}) {
@@ -115,7 +128,7 @@ func (ctl *StatefulsetCtl) listAndWatch() {
},
})
informer.Run(ctl.stopChan)
ctl.informer = informer
}
func (ctl *StatefulsetCtl) CountWithConditions(conditions string) int {
@@ -135,14 +148,3 @@ func (ctl *StatefulsetCtl) ListWithConditions(conditions string, paging *Paging)
return total, list, nil
}
func (ctl *StatefulsetCtl) Count(namespace string) int {
var count int
db := ctl.DB
if len(namespace) == 0 {
db.Model(&Statefulset{}).Count(&count)
} else {
db.Model(&Statefulset{}).Where("namespace = ?", namespace).Count(&count)
}
return count
}

View File

@@ -20,12 +20,22 @@ import (
"fmt"
"time"
"github.com/golang/glog"
"k8s.io/api/storage/v1"
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 {
@@ -46,15 +56,11 @@ func (ctl *StorageClassCtl) generateObject(item v1.StorageClass) *StorageClass {
return object
}
func (ctl *StorageClassCtl) listAndWatch() {
defer func() {
close(ctl.aliveChan)
if err := recover(); err != nil {
glog.Error(err)
return
}
}()
func (ctl *StorageClassCtl) Name() string {
return ctl.CommonAttribute.Name
}
func (ctl *StorageClassCtl) sync(stopChan chan struct{}) {
db := ctl.DB
if db.HasTable(&StorageClass{}) {
@@ -63,12 +69,8 @@ func (ctl *StorageClassCtl) listAndWatch() {
db = db.CreateTable(&StorageClass{})
k8sClient := ctl.K8sClient
kubeInformerFactory := informers.NewSharedInformerFactory(k8sClient, time.Second*resyncCircle)
informer := kubeInformerFactory.Storage().V1().StorageClasses().Informer()
lister := kubeInformerFactory.Storage().V1().StorageClasses().Lister()
list, err := lister.List(labels.Everything())
ctl.initListerAndInformer()
list, err := ctl.lister.List(labels.Everything())
if err != nil {
glog.Error(err)
return
@@ -77,15 +79,123 @@ func (ctl *StorageClassCtl) listAndWatch() {
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)
@@ -101,8 +211,7 @@ func (ctl *StorageClassCtl) listAndWatch() {
},
})
informer.Run(ctl.stopChan)
ctl.informer = informer
}
func (ctl *StorageClassCtl) CountWithConditions(conditions string) int {
@@ -122,17 +231,10 @@ func (ctl *StorageClassCtl) ListWithConditions(conditions string, paging *Paging
for index, storageClass := range list {
name := storageClass.Name
pvcCtl := PvcCtl{CommonAttribute{K8sClient: ctl.K8sClient, DB: ctl.DB}}
pvcCtl := ResourceControllers.Controllers[PersistentVolumeClaim]
list[index].Count = pvcCtl.CountWithConditions(fmt.Sprintf("storage_class=\"%s\"", name))
}
return total, list, nil
}
func (ctl *StorageClassCtl) Count(name string) int {
var count int
db := ctl.DB
db.Model(&StorageClass{}).Count(&count)
return count
}

View File

@@ -26,10 +26,16 @@ import (
"github.com/jinzhu/gorm"
"k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes"
appV1 "k8s.io/client-go/listers/apps/v1"
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 = 180
resyncCircle = 600
Stopped = "stopped"
PvcPending = "Pending"
Running = "running"
@@ -57,6 +63,7 @@ const (
ClusterRoles = "cluster-roles"
Services = "services"
StorageClasses = "storage-classes"
Applications = "applications"
)
type Annotation struct {
@@ -163,10 +170,18 @@ func (Pvc) TableName() string {
return tablePersistentVolumeClaim
}
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"`
Namespace string `gorm:"primary_key" json:"namespace"`
Ip string `json:"ip,omitempty"`
Rules string `gorm:"type:text" json:"rules, omitempty"`
TlsTermination string `json:"tlsTermination,omitempty"`
Annotation Annotation `json:"annotations"`
CreateTime time.Time `gorm:"column:createTime" json:"createTime,omitempty"`
@@ -273,16 +288,19 @@ type Paging struct {
}
type Controller interface {
listAndWatch()
chanStop() chan struct{}
chanAlive() chan struct{}
Count(namespace string) int
CountWithConditions(condition string) int
total() int
initListerAndInformer()
sync(stopChan chan struct{})
Name() string
ListWithConditions(condition string, paging *Paging) (int, interface{}, error)
}
type CommonAttribute struct {
K8sClient *kubernetes.Clientset
Name string
DB *gorm.DB
stopChan chan struct{}
aliveChan chan struct{}
@@ -300,44 +318,66 @@ func (ca *CommonAttribute) chanAlive() chan struct{} {
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
}

View File

@@ -31,11 +31,12 @@ import (
"github.com/golang/glog"
"gopkg.in/yaml.v2"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/api/core/v1"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/options"
@@ -46,6 +47,7 @@ const (
keyPath = "/etc/kubernetes/pki/ca.key"
clusterName = "kubernetes"
kubectlConfigKey = "config"
defaultNamespace = "default"
)
type clusterInfo struct {
@@ -59,8 +61,9 @@ type cluster struct {
}
type contextInfo struct {
Cluster string `yaml:"cluster"`
User string `yaml:"user"`
Cluster string `yaml:"cluster"`
User string `yaml:"user"`
NameSpace string `yaml:"namespace"`
}
type contextObject struct {
@@ -186,14 +189,14 @@ func newCertificate(info CertInformation) *x509.Certificate {
}
func generateCaAndKey(user, caPath, keyPath string) (string, string, error) {
crtinfo := CertInformation{CommonName: user, IsCA: false}
crtInfo := CertInformation{CommonName: user, IsCA: false}
crt, pri, err := Parse(caPath, keyPath)
if err != nil {
glog.Error(err)
return "", "", err
}
cert, key, err := createCRT(crt, pri, crtinfo)
cert, key, err := createCRT(crt, pri, crtInfo)
if err != nil {
glog.Error(err)
return "", "", err
@@ -217,7 +220,7 @@ func createKubeConfig(userName string) (string, error) {
tmpKubeConfig.Clusters = append(tmpKubeConfig.Clusters, tmpCluster)
contextName := userName + "@" + clusterName
tmpContext := contextObject{Context: contextInfo{User: userName, Cluster: clusterName}, Name: contextName}
tmpContext := contextObject{Context: contextInfo{User: userName, Cluster: clusterName, NameSpace: defaultNamespace}, Name: contextName}
tmpKubeConfig.Contexts = append(tmpKubeConfig.Contexts, tmpContext)
cert, key, err := generateCaAndKey(userName, caPath, keyPath)
@@ -240,42 +243,48 @@ func createKubeConfig(userName string) (string, error) {
func CreateKubeConfig(user string) error {
k8sClient := client.NewK8sClient()
config, err := createKubeConfig(user)
if err != nil {
glog.Errorln(err)
return err
_, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(user, metaV1.GetOptions{})
if errors.IsNotFound(err) {
config, err := createKubeConfig(user)
if err != nil {
glog.Errorln(err)
return err
}
data := map[string]string{"config": string(config)}
configMap := v1.ConfigMap{TypeMeta: metaV1.TypeMeta{Kind: "Configmap", APIVersion: "v1"}, ObjectMeta: metaV1.ObjectMeta{Name: user}, Data: data}
_, err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(&configMap)
if err != nil && !errors.IsAlreadyExists(err) {
glog.Errorf("create user %s's kubeConfig failed, reason:", user, err)
return err
}
}
data := map[string]string{"config": string(config)}
var configmap = v1.ConfigMap{TypeMeta: metav1.TypeMeta{Kind: "Configmap", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Name: user}, Data: data}
_, err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(&configmap)
if err != nil && !errors.IsAlreadyExists(err) {
glog.Errorf("create user %s's kubeConfig failed, reason:", user, err)
return err
}
return nil
}
func GetKubeConfig(user string) (string, error) {
k8sClient := client.NewK8sClient()
configmap, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(user, metav1.GetOptions{})
configMap, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(user, metaV1.GetOptions{})
if err != nil {
glog.Errorf("cannot get user %s's kubeConfig, reason:", user, err)
return "", err
}
return configmap.Data[kubectlConfigKey], nil
return configMap.Data[kubectlConfigKey], nil
}
func DelKubeConfig(user string) error {
k8sClient := client.NewK8sClient()
_, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(user, metav1.GetOptions{})
_, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(user, metaV1.GetOptions{})
if errors.IsNotFound(err) {
return nil
}
deletePolicy := metav1.DeletePropagationBackground
err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Delete(user, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy})
deletePolicy := metaV1.DeletePropagationBackground
err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Delete(user, &metaV1.DeleteOptions{PropagationPolicy: &deletePolicy})
if err != nil {
glog.Errorf("delete user %s's kubeConfig failed, reason:", user, err)
return err

View File

@@ -35,21 +35,20 @@ import (
const (
namespace = constants.KubeSphereControlNamespace
retry = 5
)
type kubectlPodInfo struct {
type KubectlPodInfo struct {
Namespace string `json:"namespace"`
Pod string `json:"pod"`
Container string `json:"container"`
}
func GetKubectlPod(user string) (kubectlPodInfo, error) {
func GetKubectlPod(user string) (KubectlPodInfo, error) {
k8sClient := client.NewK8sClient()
deploy, err := k8sClient.AppsV1beta2().Deployments(namespace).Get(user, metav1.GetOptions{})
if err != nil {
glog.Errorln(err)
return kubectlPodInfo{}, err
return KubectlPodInfo{}, err
}
selectors := deploy.Spec.Selector.MatchLabels
@@ -57,16 +56,16 @@ func GetKubectlPod(user string) (kubectlPodInfo, error) {
podList, err := k8sClient.CoreV1().Pods(namespace).List(metav1.ListOptions{LabelSelector: labelSelector})
if err != nil {
glog.Errorln(err)
return kubectlPodInfo{}, err
return KubectlPodInfo{}, err
}
pod, err := selectCorrectPod(namespace, podList.Items)
if err != nil {
glog.Errorln(err)
return kubectlPodInfo{}, err
return KubectlPodInfo{}, err
}
info := kubectlPodInfo{Namespace: pod.Namespace, Pod: pod.Name, Container: pod.Status.ContainerStatuses[0].Name}
info := KubectlPodInfo{Namespace: pod.Namespace, Pod: pod.Name, Container: pod.Status.ContainerStatuses[0].Name}
return info, nil
@@ -91,7 +90,12 @@ func selectCorrectPod(namespace string, pods []v1.Pod) (kubectlPod v1.Pod, err e
return kubectPodList[random], nil
}
func CreateKubectlPod(user string) error {
func CreateKubectlDeploy(user string) error {
k8sClient := client.NewK8sClient()
_, err := k8sClient.AppsV1().Deployments(namespace).Get(user, metav1.GetOptions{})
if err == nil {
return nil
}
replica := int32(1)
selector := metav1.LabelSelector{MatchLabels: map[string]string{"user": user}}
@@ -122,17 +126,12 @@ func CreateKubectlPod(user string) error {
},
}
k8sClient := client.NewK8sClient()
_, err := k8sClient.AppsV1beta2().Deployments(namespace).Create(&deployment)
if errors.IsAlreadyExists(err) {
return nil
}
_, err = k8sClient.AppsV1beta2().Deployments(namespace).Create(&deployment)
return err
}
func DelKubectlPod(user string) error {
func DelKubectlDeploy(user string) error {
k8sClient := client.NewK8sClient()
_, err := k8sClient.AppsV1beta2().Deployments(namespace).Get(user, metav1.GetOptions{})
if errors.IsNotFound(err) {

View File

@@ -22,6 +22,8 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"fmt"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/models/controllers"
)
@@ -45,7 +47,7 @@ var resourceMap = map[string]string{daemonsetsKey: controllers.Daemonsets, deplo
statefulsetsKey: controllers.Statefulsets, persistentvolumeclaimsKey: controllers.PersistentVolumeClaim, podsKey: controllers.Pods,
namespaceKey: controllers.Namespaces, storageClassesKey: controllers.StorageClasses, clusterRolesKey: controllers.ClusterRoles}
type resourceQuota struct {
type ResourceQuota struct {
NameSpace string `json:"namespace"`
Data v1.ResourceQuotaStatus `json:"data"`
}
@@ -55,10 +57,15 @@ func getUsage(namespace, resource string) int {
if err != nil {
return 0
}
return ctl.Count(namespace)
if len(namespace) == 0 {
return ctl.CountWithConditions("")
}
return ctl.CountWithConditions(fmt.Sprintf("namespace = '%s' ", namespace))
}
func GetClusterQuota() (*resourceQuota, error) {
func GetClusterQuota() (*ResourceQuota, error) {
quota := v1.ResourceQuotaStatus{Hard: make(v1.ResourceList), Used: make(v1.ResourceList)}
for k, v := range resourceMap {
@@ -68,11 +75,11 @@ func GetClusterQuota() (*resourceQuota, error) {
quota.Used[v1.ResourceName(k)] = quantity
}
return &resourceQuota{NameSpace: "\"\"", Data: quota}, nil
return &ResourceQuota{NameSpace: "\"\"", Data: quota}, nil
}
func GetNamespaceQuota(namespace string) (*resourceQuota, error) {
func GetNamespaceQuota(namespace string) (*ResourceQuota, error) {
quota, err := getNamespaceResourceQuota(namespace)
if err != nil {
glog.Error(err)
@@ -95,7 +102,7 @@ func GetNamespaceQuota(namespace string) (*resourceQuota, error) {
}
}
return &resourceQuota{NameSpace: namespace, Data: *quota}, nil
return &ResourceQuota{NameSpace: namespace, Data: *quota}, nil
}
func updateNamespaceQuota(tmpResourceList, resourceList v1.ResourceList) {

View File

@@ -6,8 +6,15 @@ import (
"strconv"
"strings"
"kubesphere.io/kubesphere/pkg/client"
"github.com/golang/glog"
"kubesphere.io/kubesphere/pkg/models/controllers"
"kubesphere.io/kubesphere/pkg/options"
)
const (
limit = "limit"
page = "page"
)
type ResourceList struct {
@@ -18,35 +25,16 @@ type ResourceList struct {
}
func getController(resource string) (controllers.Controller, error) {
var ctl controllers.Controller
attr := controllers.CommonAttribute{DB: client.NewDBClient()}
switch resource {
case controllers.Deployments:
ctl = &controllers.DeploymentCtl{attr}
case controllers.Statefulsets:
ctl = &controllers.StatefulsetCtl{attr}
case controllers.Daemonsets:
ctl = &controllers.DaemonsetCtl{attr}
case controllers.Ingresses:
ctl = &controllers.IngressCtl{attr}
case controllers.PersistentVolumeClaim:
ctl = &controllers.PvcCtl{attr}
case controllers.Roles:
ctl = &controllers.RoleCtl{attr}
case controllers.ClusterRoles:
ctl = &controllers.ClusterRoleCtl{attr}
case controllers.Services:
ctl = &controllers.ServiceCtl{attr}
case controllers.Pods:
ctl = &controllers.PodCtl{attr}
case controllers.Namespaces:
ctl = &controllers.NamespaceCtl{attr}
case controllers.StorageClasses:
ctl = &controllers.StorageClassCtl{attr}
case controllers.Deployments, controllers.Statefulsets, controllers.Daemonsets, controllers.Ingresses,
controllers.PersistentVolumeClaim, controllers.Roles, controllers.ClusterRoles, controllers.Services,
controllers.Pods, controllers.Namespaces, controllers.StorageClasses:
return controllers.ResourceControllers.Controllers[resource], nil
default:
return nil, errors.New("invalid resource type")
return nil, fmt.Errorf("invalid resource Name '%s'", resource)
}
return ctl, nil
return nil, nil
}
@@ -90,26 +78,44 @@ func getConditions(str string) (map[string]string, map[string]string, error) {
return match, fuzzy, nil
}
func getPaging(str string) (map[string]int, error) {
paging := make(map[string]int)
if len(str) == 0 {
return paging, nil
func getPaging(resourceName, pagingStr string) (*controllers.Paging, map[string]int, error) {
defaultPaging := &controllers.Paging{Limit: 10, Offset: 0}
defautlPagingMap := map[string]int{"page": 1, "limit": 10}
if resourceName == controllers.Namespaces {
defaultPaging = nil
defautlPagingMap = map[string]int{"page": 0, "limit": 0}
}
list := strings.Split(str, ",")
pagingMap := make(map[string]int)
if len(pagingStr) == 0 {
return defaultPaging, defautlPagingMap, nil
}
list := strings.Split(pagingStr, ",")
for _, item := range list {
kvs := strings.Split(item, "=")
if len(kvs) < 2 {
return nil, errors.New("invalid Paging input")
return nil, nil, errors.New("invalid Paging input")
}
value, err := strconv.Atoi(kvs[1])
if err != nil {
return nil, err
return nil, nil, errors.New("invalid Paging input")
}
paging[kvs[0]] = value
pagingMap[kvs[0]] = value
}
return paging, nil
if pagingMap[limit] <= 0 || pagingMap[page] <= 0 {
return nil, nil, errors.New("invalid Paging input")
}
if pagingMap[limit] > 0 && pagingMap[page] > 0 {
offset := (pagingMap[page] - 1) * pagingMap[limit]
return &controllers.Paging{Limit: pagingMap[limit], Offset: offset}, pagingMap, nil
}
return defaultPaging, defautlPagingMap, nil
}
func ListResource(resourceName, conditonSrt, pagingStr string) (*ResourceList, error) {
@@ -118,12 +124,12 @@ func ListResource(resourceName, conditonSrt, pagingStr string) (*ResourceList, e
return nil, err
}
pagingMap, err := getPaging(pagingStr)
paging, pagingMap, err := getPaging(resourceName, pagingStr)
if err != nil {
return nil, err
}
conditionStr, paging := generateConditionAndPaging(match, fuzzy, pagingMap)
conditionStr := generateConditionStr(match, fuzzy)
ctl, err := getController(resourceName)
if err != nil {
@@ -135,10 +141,10 @@ func ListResource(resourceName, conditonSrt, pagingStr string) (*ResourceList, e
return nil, err
}
return &ResourceList{Total: total, Items: items, Page: pagingMap["page"], Limit: pagingMap["limit"]}, nil
return &ResourceList{Total: total, Items: items, Page: pagingMap[page], Limit: pagingMap[limit]}, nil
}
func generateConditionAndPaging(match map[string]string, fuzzy map[string]string, paging map[string]int) (string, *controllers.Paging) {
func generateConditionStr(match map[string]string, fuzzy map[string]string) string {
conditionStr := ""
for k, v := range match {
@@ -157,12 +163,7 @@ func generateConditionAndPaging(match map[string]string, fuzzy map[string]string
}
}
if paging["limit"] > 0 && paging["page"] >= 0 {
offset := (paging["page"] - 1) * paging["limit"]
return conditionStr, &controllers.Paging{Limit: paging["limit"], Offset: offset}
}
return conditionStr, nil
return conditionStr
}
type workLoadStatus struct {
@@ -176,14 +177,14 @@ func GetNamespacesResourceStatus(namespace string) (*workLoadStatus, error) {
var status *ResourceList
var err error
for _, resource := range []string{controllers.Deployments, controllers.Statefulsets, controllers.Daemonsets, controllers.PersistentVolumeClaim} {
resourceStatus := controllers.Updating
notReadyStatus := controllers.Updating
if resource == controllers.PersistentVolumeClaim {
resourceStatus = controllers.PvcPending
notReadyStatus = controllers.PvcPending
}
if len(namespace) > 0 {
status, err = ListResource(resource, fmt.Sprintf("status=%s,namespace=%s", resourceStatus, namespace), "")
status, err = ListResource(resource, fmt.Sprintf("status=%s,namespace=%s", notReadyStatus, namespace), "")
} else {
status, err = ListResource(resource, fmt.Sprintf("status=%s", resourceStatus), "")
status, err = ListResource(resource, fmt.Sprintf("status=%s", notReadyStatus), "")
}
if err != nil {
@@ -191,9 +192,7 @@ func GetNamespacesResourceStatus(namespace string) (*workLoadStatus, error) {
}
count := status.Total
//items := status.Items
res.Count[resource] = count
//res.Items[resource] = items
}
return &res, nil
@@ -203,3 +202,31 @@ func GetClusterResourceStatus() (*workLoadStatus, error) {
return GetNamespacesResourceStatus("")
}
func GetApplication(clusterId string) (interface{}, error) {
ctl := &controllers.ApplicationCtl{OpenpitrixAddr: options.ServerOptions.GetOpAddress()}
return ctl.GetApp(clusterId)
}
func ListApplication(runtimeId, conditions, pagingStr string) (*ResourceList, error) {
paging, pagingMap, err := getPaging(controllers.Applications, pagingStr)
if err != nil {
return nil, err
}
match, fuzzy, err := getConditions(conditions)
if err != nil {
glog.Error(err)
return nil, err
}
ctl := &controllers.ApplicationCtl{OpenpitrixAddr: options.ServerOptions.GetOpAddress()}
total, items, err := ctl.ListApplication(runtimeId, match, fuzzy, paging)
if err != nil {
glog.Errorf("get application list failed, reason: %s", err)
return nil, err
}
return &ResourceList{Total: total, Items: items, Page: pagingMap[page], Limit: pagingMap[limit]}, nil
}

View File

@@ -28,6 +28,7 @@ import (
"k8s.io/api/rbac/v1"
"errors"
"strings"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/constants"
@@ -125,6 +126,9 @@ func LoadYamls() ([]string, error) {
}
for _, file := range files {
if file.IsDir() || !strings.HasSuffix(file.Name(), ".yaml") {
continue
}
content, err := ioutil.ReadFile(constants.IngressControllerFolder + "/" + file.Name())
if err != nil {

5
vendor/github.com/PuerkitoBio/purell/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,5 @@
*.sublime-*
.DS_Store
*.swp
*.swo
tags

7
vendor/github.com/PuerkitoBio/purell/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,7 @@
language: go
go:
- 1.4
- 1.5
- 1.6
- tip

12
vendor/github.com/PuerkitoBio/purell/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,12 @@
Copyright (c) 2012, Martin Angers
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

187
vendor/github.com/PuerkitoBio/purell/README.md generated vendored Normal file
View File

@@ -0,0 +1,187 @@
# Purell
Purell is a tiny Go library to normalize URLs. It returns a pure URL. Pure-ell. Sanitizer and all. Yeah, I know...
Based on the [wikipedia paper][wiki] and the [RFC 3986 document][rfc].
[![build status](https://secure.travis-ci.org/PuerkitoBio/purell.png)](http://travis-ci.org/PuerkitoBio/purell)
## Install
`go get github.com/PuerkitoBio/purell`
## Changelog
* **2016-11-14 (v1.1.0)** : IDN: Conform to RFC 5895: Fold character width (thanks to @beeker1121).
* **2016-07-27 (v1.0.0)** : Normalize IDN to ASCII (thanks to @zenovich).
* **2015-02-08** : Add fix for relative paths issue ([PR #5][pr5]) and add fix for unnecessary encoding of reserved characters ([see issue #7][iss7]).
* **v0.2.0** : Add benchmarks, Attempt IDN support.
* **v0.1.0** : Initial release.
## Examples
From `example_test.go` (note that in your code, you would import "github.com/PuerkitoBio/purell", and would prefix references to its methods and constants with "purell."):
```go
package purell
import (
"fmt"
"net/url"
)
func ExampleNormalizeURLString() {
if normalized, err := NormalizeURLString("hTTp://someWEBsite.com:80/Amazing%3f/url/",
FlagLowercaseScheme|FlagLowercaseHost|FlagUppercaseEscapes); err != nil {
panic(err)
} else {
fmt.Print(normalized)
}
// Output: http://somewebsite.com:80/Amazing%3F/url/
}
func ExampleMustNormalizeURLString() {
normalized := MustNormalizeURLString("hTTpS://someWEBsite.com:443/Amazing%fa/url/",
FlagsUnsafeGreedy)
fmt.Print(normalized)
// Output: http://somewebsite.com/Amazing%FA/url
}
func ExampleNormalizeURL() {
if u, err := url.Parse("Http://SomeUrl.com:8080/a/b/.././c///g?c=3&a=1&b=9&c=0#target"); err != nil {
panic(err)
} else {
normalized := NormalizeURL(u, FlagsUsuallySafeGreedy|FlagRemoveDuplicateSlashes|FlagRemoveFragment)
fmt.Print(normalized)
}
// Output: http://someurl.com:8080/a/c/g?c=3&a=1&b=9&c=0
}
```
## API
As seen in the examples above, purell offers three methods, `NormalizeURLString(string, NormalizationFlags) (string, error)`, `MustNormalizeURLString(string, NormalizationFlags) (string)` and `NormalizeURL(*url.URL, NormalizationFlags) (string)`. They all normalize the provided URL based on the specified flags. Here are the available flags:
```go
const (
// Safe normalizations
FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1
FlagLowercaseHost // http://HOST -> http://host
FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF
FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA
FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$
FlagRemoveDefaultPort // http://host:80 -> http://host
FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path
// Usually safe normalizations
FlagRemoveTrailingSlash // http://host/path/ -> http://host/path
FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags)
FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c
// Unsafe normalizations
FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/
FlagRemoveFragment // http://host/path#fragment -> http://host/path
FlagForceHTTP // https://host -> http://host
FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b
FlagRemoveWWW // http://www.host/ -> http://host/
FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags)
FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3
// Normalizations not in the wikipedia article, required to cover tests cases
// submitted by jehiah
FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147
FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147
FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147
FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path
FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path
// Convenience set of safe normalizations
FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator
// For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags,
// while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix".
// Convenience set of usually safe normalizations (includes FlagsSafe)
FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments
FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments
// Convenience set of unsafe normalizations (includes FlagsUsuallySafe)
FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery
FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery
// Convenience set of all available flags
FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
)
```
For convenience, the set of flags `FlagsSafe`, `FlagsUsuallySafe[Greedy|NonGreedy]`, `FlagsUnsafe[Greedy|NonGreedy]` and `FlagsAll[Greedy|NonGreedy]` are provided for the similarly grouped normalizations on [wikipedia's URL normalization page][wiki]. You can add (using the bitwise OR `|` operator) or remove (using the bitwise AND NOT `&^` operator) individual flags from the sets if required, to build your own custom set.
The [full godoc reference is available on gopkgdoc][godoc].
Some things to note:
* `FlagDecodeUnnecessaryEscapes`, `FlagEncodeNecessaryEscapes`, `FlagUppercaseEscapes` and `FlagRemoveEmptyQuerySeparator` are always implicitly set, because internally, the URL string is parsed as an URL object, which automatically decodes unnecessary escapes, uppercases and encodes necessary ones, and removes empty query separators (an unnecessary `?` at the end of the url). So this operation cannot **not** be done. For this reason, `FlagRemoveEmptyQuerySeparator` (as well as the other three) has been included in the `FlagsSafe` convenience set, instead of `FlagsUnsafe`, where Wikipedia puts it.
* The `FlagDecodeUnnecessaryEscapes` decodes the following escapes (*from -> to*):
- %24 -> $
- %26 -> &
- %2B-%3B -> +,-./0123456789:;
- %3D -> =
- %40-%5A -> @ABCDEFGHIJKLMNOPQRSTUVWXYZ
- %5F -> _
- %61-%7A -> abcdefghijklmnopqrstuvwxyz
- %7E -> ~
* When the `NormalizeURL` function is used (passing an URL object), this source URL object is modified (that is, after the call, the URL object will be modified to reflect the normalization).
* The *replace IP with domain name* normalization (`http://208.77.188.166/ → http://www.example.com/`) is obviously not possible for a library without making some network requests. This is not implemented in purell.
* The *remove unused query string parameters* and *remove default query parameters* are also not implemented, since this is a very case-specific normalization, and it is quite trivial to do with an URL object.
### Safe vs Usually Safe vs Unsafe
Purell allows you to control the level of risk you take while normalizing an URL. You can aggressively normalize, play it totally safe, or anything in between.
Consider the following URL:
`HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid`
Normalizing with the `FlagsSafe` gives:
`https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid`
With the `FlagsUsuallySafeGreedy`:
`https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid`
And with `FlagsUnsafeGreedy`:
`http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3`
## TODOs
* Add a class/default instance to allow specifying custom directory index names? At the moment, removing directory index removes `(^|/)((?:default|index)\.\w{1,4})$`.
## Thanks / Contributions
@rogpeppe
@jehiah
@opennota
@pchristopher1275
@zenovich
@beeker1121
## License
The [BSD 3-Clause license][bsd].
[bsd]: http://opensource.org/licenses/BSD-3-Clause
[wiki]: http://en.wikipedia.org/wiki/URL_normalization
[rfc]: http://tools.ietf.org/html/rfc3986#section-6
[godoc]: http://go.pkgdoc.org/github.com/PuerkitoBio/purell
[pr5]: https://github.com/PuerkitoBio/purell/pull/5
[iss7]: https://github.com/PuerkitoBio/purell/issues/7

379
vendor/github.com/PuerkitoBio/purell/purell.go generated vendored Normal file
View File

@@ -0,0 +1,379 @@
/*
Package purell offers URL normalization as described on the wikipedia page:
http://en.wikipedia.org/wiki/URL_normalization
*/
package purell
import (
"bytes"
"fmt"
"net/url"
"regexp"
"sort"
"strconv"
"strings"
"github.com/PuerkitoBio/urlesc"
"golang.org/x/net/idna"
"golang.org/x/text/unicode/norm"
"golang.org/x/text/width"
)
// A set of normalization flags determines how a URL will
// be normalized.
type NormalizationFlags uint
const (
// Safe normalizations
FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1
FlagLowercaseHost // http://HOST -> http://host
FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF
FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA
FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$
FlagRemoveDefaultPort // http://host:80 -> http://host
FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path
// Usually safe normalizations
FlagRemoveTrailingSlash // http://host/path/ -> http://host/path
FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags)
FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c
// Unsafe normalizations
FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/
FlagRemoveFragment // http://host/path#fragment -> http://host/path
FlagForceHTTP // https://host -> http://host
FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b
FlagRemoveWWW // http://www.host/ -> http://host/
FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags)
FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3
// Normalizations not in the wikipedia article, required to cover tests cases
// submitted by jehiah
FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147
FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147
FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147
FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path
FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path
// Convenience set of safe normalizations
FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator
// For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags,
// while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix".
// Convenience set of usually safe normalizations (includes FlagsSafe)
FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments
FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments
// Convenience set of unsafe normalizations (includes FlagsUsuallySafe)
FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery
FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery
// Convenience set of all available flags
FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
)
const (
defaultHttpPort = ":80"
defaultHttpsPort = ":443"
)
// Regular expressions used by the normalizations
var rxPort = regexp.MustCompile(`(:\d+)/?$`)
var rxDirIndex = regexp.MustCompile(`(^|/)((?:default|index)\.\w{1,4})$`)
var rxDupSlashes = regexp.MustCompile(`/{2,}`)
var rxDWORDHost = regexp.MustCompile(`^(\d+)((?:\.+)?(?:\:\d*)?)$`)
var rxOctalHost = regexp.MustCompile(`^(0\d*)\.(0\d*)\.(0\d*)\.(0\d*)((?:\.+)?(?:\:\d*)?)$`)
var rxHexHost = regexp.MustCompile(`^0x([0-9A-Fa-f]+)((?:\.+)?(?:\:\d*)?)$`)
var rxHostDots = regexp.MustCompile(`^(.+?)(:\d+)?$`)
var rxEmptyPort = regexp.MustCompile(`:+$`)
// Map of flags to implementation function.
// FlagDecodeUnnecessaryEscapes has no action, since it is done automatically
// by parsing the string as an URL. Same for FlagUppercaseEscapes and FlagRemoveEmptyQuerySeparator.
// Since maps have undefined traversing order, make a slice of ordered keys
var flagsOrder = []NormalizationFlags{
FlagLowercaseScheme,
FlagLowercaseHost,
FlagRemoveDefaultPort,
FlagRemoveDirectoryIndex,
FlagRemoveDotSegments,
FlagRemoveFragment,
FlagForceHTTP, // Must be after remove default port (because https=443/http=80)
FlagRemoveDuplicateSlashes,
FlagRemoveWWW,
FlagAddWWW,
FlagSortQuery,
FlagDecodeDWORDHost,
FlagDecodeOctalHost,
FlagDecodeHexHost,
FlagRemoveUnnecessaryHostDots,
FlagRemoveEmptyPortSeparator,
FlagRemoveTrailingSlash, // These two (add/remove trailing slash) must be last
FlagAddTrailingSlash,
}
// ... and then the map, where order is unimportant
var flags = map[NormalizationFlags]func(*url.URL){
FlagLowercaseScheme: lowercaseScheme,
FlagLowercaseHost: lowercaseHost,
FlagRemoveDefaultPort: removeDefaultPort,
FlagRemoveDirectoryIndex: removeDirectoryIndex,
FlagRemoveDotSegments: removeDotSegments,
FlagRemoveFragment: removeFragment,
FlagForceHTTP: forceHTTP,
FlagRemoveDuplicateSlashes: removeDuplicateSlashes,
FlagRemoveWWW: removeWWW,
FlagAddWWW: addWWW,
FlagSortQuery: sortQuery,
FlagDecodeDWORDHost: decodeDWORDHost,
FlagDecodeOctalHost: decodeOctalHost,
FlagDecodeHexHost: decodeHexHost,
FlagRemoveUnnecessaryHostDots: removeUnncessaryHostDots,
FlagRemoveEmptyPortSeparator: removeEmptyPortSeparator,
FlagRemoveTrailingSlash: removeTrailingSlash,
FlagAddTrailingSlash: addTrailingSlash,
}
// MustNormalizeURLString returns the normalized string, and panics if an error occurs.
// It takes an URL string as input, as well as the normalization flags.
func MustNormalizeURLString(u string, f NormalizationFlags) string {
result, e := NormalizeURLString(u, f)
if e != nil {
panic(e)
}
return result
}
// NormalizeURLString returns the normalized string, or an error if it can't be parsed into an URL object.
// It takes an URL string as input, as well as the normalization flags.
func NormalizeURLString(u string, f NormalizationFlags) (string, error) {
parsed, err := url.Parse(u)
if err != nil {
return "", err
}
if f&FlagLowercaseHost == FlagLowercaseHost {
parsed.Host = strings.ToLower(parsed.Host)
}
// The idna package doesn't fully conform to RFC 5895
// (https://tools.ietf.org/html/rfc5895), so we do it here.
// Taken from Go 1.8 cycle source, courtesy of bradfitz.
// TODO: Remove when (if?) idna package conforms to RFC 5895.
parsed.Host = width.Fold.String(parsed.Host)
parsed.Host = norm.NFC.String(parsed.Host)
if parsed.Host, err = idna.ToASCII(parsed.Host); err != nil {
return "", err
}
return NormalizeURL(parsed, f), nil
}
// NormalizeURL returns the normalized string.
// It takes a parsed URL object as input, as well as the normalization flags.
func NormalizeURL(u *url.URL, f NormalizationFlags) string {
for _, k := range flagsOrder {
if f&k == k {
flags[k](u)
}
}
return urlesc.Escape(u)
}
func lowercaseScheme(u *url.URL) {
if len(u.Scheme) > 0 {
u.Scheme = strings.ToLower(u.Scheme)
}
}
func lowercaseHost(u *url.URL) {
if len(u.Host) > 0 {
u.Host = strings.ToLower(u.Host)
}
}
func removeDefaultPort(u *url.URL) {
if len(u.Host) > 0 {
scheme := strings.ToLower(u.Scheme)
u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string {
if (scheme == "http" && val == defaultHttpPort) || (scheme == "https" && val == defaultHttpsPort) {
return ""
}
return val
})
}
}
func removeTrailingSlash(u *url.URL) {
if l := len(u.Path); l > 0 {
if strings.HasSuffix(u.Path, "/") {
u.Path = u.Path[:l-1]
}
} else if l = len(u.Host); l > 0 {
if strings.HasSuffix(u.Host, "/") {
u.Host = u.Host[:l-1]
}
}
}
func addTrailingSlash(u *url.URL) {
if l := len(u.Path); l > 0 {
if !strings.HasSuffix(u.Path, "/") {
u.Path += "/"
}
} else if l = len(u.Host); l > 0 {
if !strings.HasSuffix(u.Host, "/") {
u.Host += "/"
}
}
}
func removeDotSegments(u *url.URL) {
if len(u.Path) > 0 {
var dotFree []string
var lastIsDot bool
sections := strings.Split(u.Path, "/")
for _, s := range sections {
if s == ".." {
if len(dotFree) > 0 {
dotFree = dotFree[:len(dotFree)-1]
}
} else if s != "." {
dotFree = append(dotFree, s)
}
lastIsDot = (s == "." || s == "..")
}
// Special case if host does not end with / and new path does not begin with /
u.Path = strings.Join(dotFree, "/")
if u.Host != "" && !strings.HasSuffix(u.Host, "/") && !strings.HasPrefix(u.Path, "/") {
u.Path = "/" + u.Path
}
// Special case if the last segment was a dot, make sure the path ends with a slash
if lastIsDot && !strings.HasSuffix(u.Path, "/") {
u.Path += "/"
}
}
}
func removeDirectoryIndex(u *url.URL) {
if len(u.Path) > 0 {
u.Path = rxDirIndex.ReplaceAllString(u.Path, "$1")
}
}
func removeFragment(u *url.URL) {
u.Fragment = ""
}
func forceHTTP(u *url.URL) {
if strings.ToLower(u.Scheme) == "https" {
u.Scheme = "http"
}
}
func removeDuplicateSlashes(u *url.URL) {
if len(u.Path) > 0 {
u.Path = rxDupSlashes.ReplaceAllString(u.Path, "/")
}
}
func removeWWW(u *url.URL) {
if len(u.Host) > 0 && strings.HasPrefix(strings.ToLower(u.Host), "www.") {
u.Host = u.Host[4:]
}
}
func addWWW(u *url.URL) {
if len(u.Host) > 0 && !strings.HasPrefix(strings.ToLower(u.Host), "www.") {
u.Host = "www." + u.Host
}
}
func sortQuery(u *url.URL) {
q := u.Query()
if len(q) > 0 {
arKeys := make([]string, len(q))
i := 0
for k, _ := range q {
arKeys[i] = k
i++
}
sort.Strings(arKeys)
buf := new(bytes.Buffer)
for _, k := range arKeys {
sort.Strings(q[k])
for _, v := range q[k] {
if buf.Len() > 0 {
buf.WriteRune('&')
}
buf.WriteString(fmt.Sprintf("%s=%s", k, urlesc.QueryEscape(v)))
}
}
// Rebuild the raw query string
u.RawQuery = buf.String()
}
}
func decodeDWORDHost(u *url.URL) {
if len(u.Host) > 0 {
if matches := rxDWORDHost.FindStringSubmatch(u.Host); len(matches) > 2 {
var parts [4]int64
dword, _ := strconv.ParseInt(matches[1], 10, 0)
for i, shift := range []uint{24, 16, 8, 0} {
parts[i] = dword >> shift & 0xFF
}
u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[2])
}
}
}
func decodeOctalHost(u *url.URL) {
if len(u.Host) > 0 {
if matches := rxOctalHost.FindStringSubmatch(u.Host); len(matches) > 5 {
var parts [4]int64
for i := 1; i <= 4; i++ {
parts[i-1], _ = strconv.ParseInt(matches[i], 8, 0)
}
u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[5])
}
}
}
func decodeHexHost(u *url.URL) {
if len(u.Host) > 0 {
if matches := rxHexHost.FindStringSubmatch(u.Host); len(matches) > 2 {
// Conversion is safe because of regex validation
parsed, _ := strconv.ParseInt(matches[1], 16, 0)
// Set host as DWORD (base 10) encoded host
u.Host = fmt.Sprintf("%d%s", parsed, matches[2])
// The rest is the same as decoding a DWORD host
decodeDWORDHost(u)
}
}
}
func removeUnncessaryHostDots(u *url.URL) {
if len(u.Host) > 0 {
if matches := rxHostDots.FindStringSubmatch(u.Host); len(matches) > 1 {
// Trim the leading and trailing dots
u.Host = strings.Trim(matches[1], ".")
if len(matches) > 2 {
u.Host += matches[2]
}
}
}
}
func removeEmptyPortSeparator(u *url.URL) {
if len(u.Host) > 0 {
u.Host = rxEmptyPort.ReplaceAllString(u.Host, "")
}
}

15
vendor/github.com/PuerkitoBio/urlesc/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,15 @@
language: go
go:
- 1.4.x
- 1.5.x
- 1.6.x
- 1.7.x
- 1.8.x
- tip
install:
- go build .
script:
- go test -v

27
vendor/github.com/PuerkitoBio/urlesc/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

16
vendor/github.com/PuerkitoBio/urlesc/README.md generated vendored Normal file
View File

@@ -0,0 +1,16 @@
urlesc [![Build Status](https://travis-ci.org/PuerkitoBio/urlesc.svg?branch=master)](https://travis-ci.org/PuerkitoBio/urlesc) [![GoDoc](http://godoc.org/github.com/PuerkitoBio/urlesc?status.svg)](http://godoc.org/github.com/PuerkitoBio/urlesc)
======
Package urlesc implements query escaping as per RFC 3986.
It contains some parts of the net/url package, modified so as to allow
some reserved characters incorrectly escaped by net/url (see [issue 5684](https://github.com/golang/go/issues/5684)).
## Install
go get github.com/PuerkitoBio/urlesc
## License
Go license (BSD-3-Clause)

180
vendor/github.com/PuerkitoBio/urlesc/urlesc.go generated vendored Normal file
View File

@@ -0,0 +1,180 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package urlesc implements query escaping as per RFC 3986.
// It contains some parts of the net/url package, modified so as to allow
// some reserved characters incorrectly escaped by net/url.
// See https://github.com/golang/go/issues/5684
package urlesc
import (
"bytes"
"net/url"
"strings"
)
type encoding int
const (
encodePath encoding = 1 + iota
encodeUserPassword
encodeQueryComponent
encodeFragment
)
// Return true if the specified character should be escaped when
// appearing in a URL string, according to RFC 3986.
func shouldEscape(c byte, mode encoding) bool {
// §2.3 Unreserved characters (alphanum)
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
return false
}
switch c {
case '-', '.', '_', '~': // §2.3 Unreserved characters (mark)
return false
// §2.2 Reserved characters (reserved)
case ':', '/', '?', '#', '[', ']', '@', // gen-delims
'!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': // sub-delims
// Different sections of the URL allow a few of
// the reserved characters to appear unescaped.
switch mode {
case encodePath: // §3.3
// The RFC allows sub-delims and : @.
// '/', '[' and ']' can be used to assign meaning to individual path
// segments. This package only manipulates the path as a whole,
// so we allow those as well. That leaves only ? and # to escape.
return c == '?' || c == '#'
case encodeUserPassword: // §3.2.1
// The RFC allows : and sub-delims in
// userinfo. The parsing of userinfo treats ':' as special so we must escape
// all the gen-delims.
return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@'
case encodeQueryComponent: // §3.4
// The RFC allows / and ?.
return c != '/' && c != '?'
case encodeFragment: // §4.1
// The RFC text is silent but the grammar allows
// everything, so escape nothing but #
return c == '#'
}
}
// Everything else must be escaped.
return true
}
// QueryEscape escapes the string so it can be safely placed
// inside a URL query.
func QueryEscape(s string) string {
return escape(s, encodeQueryComponent)
}
func escape(s string, mode encoding) string {
spaceCount, hexCount := 0, 0
for i := 0; i < len(s); i++ {
c := s[i]
if shouldEscape(c, mode) {
if c == ' ' && mode == encodeQueryComponent {
spaceCount++
} else {
hexCount++
}
}
}
if spaceCount == 0 && hexCount == 0 {
return s
}
t := make([]byte, len(s)+2*hexCount)
j := 0
for i := 0; i < len(s); i++ {
switch c := s[i]; {
case c == ' ' && mode == encodeQueryComponent:
t[j] = '+'
j++
case shouldEscape(c, mode):
t[j] = '%'
t[j+1] = "0123456789ABCDEF"[c>>4]
t[j+2] = "0123456789ABCDEF"[c&15]
j += 3
default:
t[j] = s[i]
j++
}
}
return string(t)
}
var uiReplacer = strings.NewReplacer(
"%21", "!",
"%27", "'",
"%28", "(",
"%29", ")",
"%2A", "*",
)
// unescapeUserinfo unescapes some characters that need not to be escaped as per RFC3986.
func unescapeUserinfo(s string) string {
return uiReplacer.Replace(s)
}
// Escape reassembles the URL into a valid URL string.
// The general form of the result is one of:
//
// scheme:opaque
// scheme://userinfo@host/path?query#fragment
//
// If u.Opaque is non-empty, String uses the first form;
// otherwise it uses the second form.
//
// In the second form, the following rules apply:
// - if u.Scheme is empty, scheme: is omitted.
// - if u.User is nil, userinfo@ is omitted.
// - if u.Host is empty, host/ is omitted.
// - if u.Scheme and u.Host are empty and u.User is nil,
// the entire scheme://userinfo@host/ is omitted.
// - if u.Host is non-empty and u.Path begins with a /,
// the form host/path does not add its own /.
// - if u.RawQuery is empty, ?query is omitted.
// - if u.Fragment is empty, #fragment is omitted.
func Escape(u *url.URL) string {
var buf bytes.Buffer
if u.Scheme != "" {
buf.WriteString(u.Scheme)
buf.WriteByte(':')
}
if u.Opaque != "" {
buf.WriteString(u.Opaque)
} else {
if u.Scheme != "" || u.Host != "" || u.User != nil {
buf.WriteString("//")
if ui := u.User; ui != nil {
buf.WriteString(unescapeUserinfo(ui.String()))
buf.WriteByte('@')
}
if h := u.Host; h != "" {
buf.WriteString(h)
}
}
if u.Path != "" && u.Path[0] != '/' && u.Host != "" {
buf.WriteByte('/')
}
buf.WriteString(escape(u.Path, encodePath))
}
if u.RawQuery != "" {
buf.WriteByte('?')
buf.WriteString(u.RawQuery)
}
if u.Fragment != "" {
buf.WriteByte('#')
buf.WriteString(escape(u.Fragment, encodeFragment))
}
return buf.String()
}

View File

@@ -0,0 +1 @@
examples/examples

View File

@@ -0,0 +1,4 @@
language: go
go:
- 1.x

View File

@@ -0,0 +1,10 @@
# changes to the go-restful-openapi package
## v0.11.0
- Register pointer to array/slice of primitives as such rather than as reference to the primitive type definition. (#46)
- Add support for map types using "additional properties" (#44)
## <= v0.10.0
See `git log`.

22
vendor/github.com/emicklei/go-restful-openapi/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
Copyright (c) 2017 Ernest Micklei
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,26 @@
# go-restful-openapi
[![Build Status](https://travis-ci.org/emicklei/go-restful-openapi.png)](https://travis-ci.org/emicklei/go-restful-openapi)
[![GoDoc](https://godoc.org/github.com/emicklei/go-restful-openapi?status.svg)](https://godoc.org/github.com/emicklei/go-restful-openapi)
[openapi](https://www.openapis.org) extension to the go-restful package, targeting [version 2.0](https://github.com/OAI/OpenAPI-Specification)
## The following Go field tags are translated to OpenAPI equivalents
- description
- minimum
- maximum
- optional ( if set to "true" then it is not listed in `required`)
- unique
- modelDescription
- type (overrides the Go type String())
- enum
- readOnly
See TestThatExtraTagsAreReadIntoModel for examples.
## dependencies
- [go-restful](https://github.com/emicklei/go-restful)
- [go-openapi](https://github.com/go-openapi/spec)
© 2017, ernestmicklei.com. MIT License. Contributions welcome.

View File

@@ -0,0 +1,32 @@
package restfulspec
import (
"reflect"
restful "github.com/emicklei/go-restful"
"github.com/go-openapi/spec"
)
func buildDefinitions(ws *restful.WebService, cfg Config) (definitions spec.Definitions) {
definitions = spec.Definitions{}
for _, each := range ws.Routes() {
addDefinitionsFromRouteTo(each, cfg, definitions)
}
return
}
func addDefinitionsFromRouteTo(r restful.Route, cfg Config, d spec.Definitions) {
builder := definitionBuilder{Definitions: d, Config: cfg}
if r.ReadSample != nil {
builder.addModel(reflect.TypeOf(r.ReadSample), "")
}
if r.WriteSample != nil {
builder.addModel(reflect.TypeOf(r.WriteSample), "")
}
for _, v := range r.ResponseErrors {
if v.Model == nil {
continue
}
builder.addModel(reflect.TypeOf(v.Model), "")
}
}

View File

@@ -0,0 +1,254 @@
package restfulspec
import (
"net/http"
"reflect"
"regexp"
"strconv"
"strings"
restful "github.com/emicklei/go-restful"
"github.com/go-openapi/spec"
)
// KeyOpenAPITags is a Metadata key for a restful Route
const KeyOpenAPITags = "openapi.tags"
func buildPaths(ws *restful.WebService, cfg Config) spec.Paths {
p := spec.Paths{Paths: map[string]spec.PathItem{}}
for _, each := range ws.Routes() {
path, patterns := sanitizePath(each.Path)
existingPathItem, ok := p.Paths[path]
if !ok {
existingPathItem = spec.PathItem{}
}
p.Paths[path] = buildPathItem(ws, each, existingPathItem, patterns, cfg)
}
return p
}
// sanitizePath removes regex expressions from named path params,
// since openapi only supports setting the pattern as a a property named "pattern".
// Expressions like "/api/v1/{name:[a-z]/" are converted to "/api/v1/{name}/".
// The second return value is a map which contains the mapping from the path parameter
// name to the extracted pattern
func sanitizePath(restfulPath string) (string, map[string]string) {
openapiPath := ""
patterns := map[string]string{}
for _, fragment := range strings.Split(restfulPath, "/") {
if fragment == "" {
continue
}
if strings.HasPrefix(fragment, "{") && strings.Contains(fragment, ":") {
split := strings.Split(fragment, ":")
fragment = split[0][1:]
pattern := split[1][:len(split[1])-1]
patterns[fragment] = pattern
fragment = "{" + fragment + "}"
}
openapiPath += "/" + fragment
}
return openapiPath, patterns
}
func buildPathItem(ws *restful.WebService, r restful.Route, existingPathItem spec.PathItem, patterns map[string]string, cfg Config) spec.PathItem {
op := buildOperation(ws, r, patterns, cfg)
switch r.Method {
case "GET":
existingPathItem.Get = op
case "POST":
existingPathItem.Post = op
case "PUT":
existingPathItem.Put = op
case "DELETE":
existingPathItem.Delete = op
case "PATCH":
existingPathItem.Patch = op
case "OPTIONS":
existingPathItem.Options = op
case "HEAD":
existingPathItem.Head = op
}
return existingPathItem
}
func buildOperation(ws *restful.WebService, r restful.Route, patterns map[string]string, cfg Config) *spec.Operation {
o := spec.NewOperation(r.Operation)
o.Description = r.Notes
o.Summary = stripTags(r.Doc)
o.Consumes = r.Consumes
o.Produces = r.Produces
o.Deprecated = r.Deprecated
if r.Metadata != nil {
if tags, ok := r.Metadata[KeyOpenAPITags]; ok {
if tagList, ok := tags.([]string); ok {
o.Tags = tagList
}
}
}
// collect any path parameters
for _, param := range ws.PathParameters() {
o.Parameters = append(o.Parameters, buildParameter(r, param, patterns[param.Data().Name], cfg))
}
// route specific params
for _, each := range r.ParameterDocs {
o.Parameters = append(o.Parameters, buildParameter(r, each, patterns[each.Data().Name], cfg))
}
o.Responses = new(spec.Responses)
props := &o.Responses.ResponsesProps
props.StatusCodeResponses = map[int]spec.Response{}
for k, v := range r.ResponseErrors {
r := buildResponse(v, cfg)
props.StatusCodeResponses[k] = r
if 200 == k { // any 2xx code?
o.Responses.Default = &r
}
}
if len(o.Responses.StatusCodeResponses) == 0 {
o.Responses.StatusCodeResponses[200] = spec.Response{ResponseProps: spec.ResponseProps{Description: http.StatusText(http.StatusOK)}}
}
return o
}
// stringAutoType automatically picks the correct type from an ambiguously typed
// string. Ex. numbers become int, true/false become bool, etc.
func stringAutoType(ambiguous string) interface{} {
if ambiguous == "" {
return nil
}
if parsedInt, err := strconv.ParseInt(ambiguous, 10, 64); err == nil {
return parsedInt
}
if parsedBool, err := strconv.ParseBool(ambiguous); err == nil {
return parsedBool
}
return ambiguous
}
func buildParameter(r restful.Route, restfulParam *restful.Parameter, pattern string, cfg Config) spec.Parameter {
p := spec.Parameter{}
param := restfulParam.Data()
p.In = asParamType(param.Kind)
if param.AllowMultiple {
p.Type = "array"
p.Items = spec.NewItems()
p.Items.Type = param.DataType
p.CollectionFormat = param.CollectionFormat
} else {
p.Type = param.DataType
}
p.Description = param.Description
p.Name = param.Name
p.Required = param.Required
if param.Kind == restful.PathParameterKind {
p.Pattern = pattern
}
st := reflect.TypeOf(r.ReadSample)
if param.Kind == restful.BodyParameterKind && r.ReadSample != nil && param.DataType == st.String() {
p.Schema = new(spec.Schema)
p.SimpleSchema = spec.SimpleSchema{}
if st.Kind() == reflect.Array || st.Kind() == reflect.Slice {
dataTypeName := definitionBuilder{}.keyFrom(st.Elem())
p.Schema.Type = []string{"array"}
p.Schema.Items = &spec.SchemaOrArray{
Schema: &spec.Schema{},
}
isPrimitive := isPrimitiveType(dataTypeName)
if isPrimitive {
mapped := jsonSchemaType(dataTypeName)
p.Schema.Items.Schema.Type = []string{mapped}
} else {
p.Schema.Items.Schema.Ref = spec.MustCreateRef("#/definitions/" + dataTypeName)
}
} else {
p.Schema.Ref = spec.MustCreateRef("#/definitions/" + param.DataType)
}
} else {
p.Type = param.DataType
p.Default = stringAutoType(param.DefaultValue)
p.Format = param.DataFormat
}
return p
}
func buildResponse(e restful.ResponseError, cfg Config) (r spec.Response) {
r.Description = e.Message
if e.Model != nil {
st := reflect.TypeOf(e.Model)
if st.Kind() == reflect.Ptr {
// For pointer type, use element type as the key; otherwise we'll
// endup with '#/definitions/*Type' which violates openapi spec.
st = st.Elem()
}
r.Schema = new(spec.Schema)
if st.Kind() == reflect.Array || st.Kind() == reflect.Slice {
modelName := definitionBuilder{}.keyFrom(st.Elem())
r.Schema.Type = []string{"array"}
r.Schema.Items = &spec.SchemaOrArray{
Schema: &spec.Schema{},
}
isPrimitive := isPrimitiveType(modelName)
if isPrimitive {
mapped := jsonSchemaType(modelName)
r.Schema.Items.Schema.Type = []string{mapped}
} else {
r.Schema.Items.Schema.Ref = spec.MustCreateRef("#/definitions/" + modelName)
}
} else {
modelName := definitionBuilder{}.keyFrom(st)
if isPrimitiveType(modelName) {
// If the response is a primitive type, then don't reference any definitions.
// Instead, set the schema's "type" to the model name.
r.Schema.AddType(modelName, "")
} else {
modelName := definitionBuilder{}.keyFrom(st)
r.Schema.Ref = spec.MustCreateRef("#/definitions/" + modelName)
}
}
}
return r
}
// stripTags takes a snippet of HTML and returns only the text content.
// For example, `<b>&lt;Hi!&gt;</b> <br>` -> `&lt;Hi!&gt; `.
func stripTags(html string) string {
re := regexp.MustCompile("<[^>]*>")
return re.ReplaceAllString(html, "")
}
func isPrimitiveType(modelName string) bool {
if len(modelName) == 0 {
return false
}
return strings.Contains("uint uint8 uint16 uint32 uint64 int int8 int16 int32 int64 float32 float64 bool string byte rune time.Time", modelName)
}
func jsonSchemaType(modelName string) string {
schemaMap := map[string]string{
"uint": "integer",
"uint8": "integer",
"uint16": "integer",
"uint32": "integer",
"uint64": "integer",
"int": "integer",
"int8": "integer",
"int16": "integer",
"int32": "integer",
"int64": "integer",
"byte": "integer",
"float64": "number",
"float32": "number",
"bool": "boolean",
"time.Time": "string",
}
mapped, ok := schemaMap[modelName]
if !ok {
return modelName // use as is (custom or struct)
}
return mapped
}

View File

@@ -0,0 +1,41 @@
package restfulspec
import (
"reflect"
restful "github.com/emicklei/go-restful"
"github.com/go-openapi/spec"
)
// MapSchemaFormatFunc can be used to modify typeName at definition time.
// To use it set the SchemaFormatHandler in the config.
type MapSchemaFormatFunc func(typeName string) string
// MapModelTypeNameFunc can be used to return the desired typeName for a given
// type. It will return false if the default name should be used.
// To use it set the ModelTypeNameHandler in the config.
type MapModelTypeNameFunc func(t reflect.Type) (string, bool)
// PostBuildSwaggerObjectFunc can be used to change the creates Swagger Object
// before serving it. To use it set the PostBuildSwaggerObjectHandler in the config.
type PostBuildSwaggerObjectFunc func(s *spec.Swagger)
// Config holds service api metadata.
type Config struct {
// WebServicesURL is a DEPRECATED field; it never had any effect in this package.
WebServicesURL string
// APIPath is the path where the JSON api is avaiable , e.g. /apidocs.json
APIPath string
// api listing is constructed from this list of restful WebServices.
WebServices []*restful.WebService
// [optional] on default CORS (Cross-Origin-Resource-Sharing) is enabled.
DisableCORS bool
// Top-level API version. Is reflected in the resource listing.
APIVersion string
// [optional] If set, model builder should call this handler to get addition typename-to-swagger-format-field conversion.
SchemaFormatHandler MapSchemaFormatFunc
// [optional] If set, model builder should call this handler to retrieve the name for a given type.
ModelTypeNameHandler MapModelTypeNameFunc
// [optional] If set then call this function with the generated Swagger Object
PostBuildSwaggerObjectHandler PostBuildSwaggerObjectFunc
}

View File

@@ -0,0 +1,491 @@
package restfulspec
import (
"encoding/json"
"reflect"
"strings"
"github.com/go-openapi/spec"
)
type definitionBuilder struct {
Definitions spec.Definitions
Config Config
}
// Documented is
type Documented interface {
SwaggerDoc() map[string]string
}
// Check if this structure has a method with signature func (<theModel>) SwaggerDoc() map[string]string
// If it exists, retrieve the documentation and overwrite all struct tag descriptions
func getDocFromMethodSwaggerDoc2(model reflect.Type) map[string]string {
if docable, ok := reflect.New(model).Elem().Interface().(Documented); ok {
return docable.SwaggerDoc()
}
return make(map[string]string)
}
// addModelFrom creates and adds a Schema to the builder and detects and calls
// the post build hook for customizations
func (b definitionBuilder) addModelFrom(sample interface{}) {
b.addModel(reflect.TypeOf(sample), "")
}
func (b definitionBuilder) addModel(st reflect.Type, nameOverride string) *spec.Schema {
// Turn pointers into simpler types so further checks are
// correct.
if st.Kind() == reflect.Ptr {
st = st.Elem()
}
modelName := b.keyFrom(st)
if nameOverride != "" {
modelName = nameOverride
}
// no models needed for primitive types
if b.isPrimitiveType(modelName) {
return nil
}
// golang encoding/json packages says array and slice values encode as
// JSON arrays, except that []byte encodes as a base64-encoded string.
// If we see a []byte here, treat it at as a primitive type (string)
// and deal with it in buildArrayTypeProperty.
if (st.Kind() == reflect.Slice || st.Kind() == reflect.Array) &&
st.Elem().Kind() == reflect.Uint8 {
return nil
}
// see if we already have visited this model
if _, ok := b.Definitions[modelName]; ok {
return nil
}
sm := spec.Schema{
SchemaProps: spec.SchemaProps{
Required: []string{},
Properties: map[string]spec.Schema{},
},
}
// reference the model before further initializing (enables recursive structs)
b.Definitions[modelName] = sm
// check for slice or array
if st.Kind() == reflect.Slice || st.Kind() == reflect.Array {
st = st.Elem()
}
// check for structure or primitive type
if st.Kind() != reflect.Struct {
return &sm
}
fullDoc := getDocFromMethodSwaggerDoc2(st)
modelDescriptions := []string{}
for i := 0; i < st.NumField(); i++ {
field := st.Field(i)
jsonName, modelDescription, prop := b.buildProperty(field, &sm, modelName)
if len(modelDescription) > 0 {
modelDescriptions = append(modelDescriptions, modelDescription)
}
// add if not omitted
if len(jsonName) != 0 {
// update description
if fieldDoc, ok := fullDoc[jsonName]; ok {
prop.Description = fieldDoc
}
// update Required
if b.isPropertyRequired(field) {
sm.Required = append(sm.Required, jsonName)
}
sm.Properties[jsonName] = prop
}
}
// We always overwrite documentation if SwaggerDoc method exists
// "" is special for documenting the struct itself
if modelDoc, ok := fullDoc[""]; ok {
sm.Description = modelDoc
} else if len(modelDescriptions) != 0 {
sm.Description = strings.Join(modelDescriptions, "\n")
}
// Needed to pass openapi validation. This field exists for json-schema compatibility,
// but it conflicts with the openapi specification.
// See https://github.com/go-openapi/spec/issues/23 for more context
sm.ID = ""
// update model builder with completed model
b.Definitions[modelName] = sm
return &sm
}
func (b definitionBuilder) isPropertyRequired(field reflect.StructField) bool {
required := true
if optionalTag := field.Tag.Get("optional"); optionalTag == "true" {
return false
}
if jsonTag := field.Tag.Get("json"); jsonTag != "" {
s := strings.Split(jsonTag, ",")
if len(s) > 1 && s[1] == "omitempty" {
return false
}
}
return required
}
func (b definitionBuilder) buildProperty(field reflect.StructField, model *spec.Schema, modelName string) (jsonName, modelDescription string, prop spec.Schema) {
jsonName = b.jsonNameOfField(field)
if len(jsonName) == 0 {
// empty name signals skip property
return "", "", prop
}
if field.Name == "XMLName" && field.Type.String() == "xml.Name" {
// property is metadata for the xml.Name attribute, can be skipped
return "", "", prop
}
if tag := field.Tag.Get("modelDescription"); tag != "" {
modelDescription = tag
}
setPropertyMetadata(&prop, field)
if prop.Type != nil {
return jsonName, modelDescription, prop
}
fieldType := field.Type
// check if type is doing its own marshalling
marshalerType := reflect.TypeOf((*json.Marshaler)(nil)).Elem()
if fieldType.Implements(marshalerType) {
var pType = "string"
if prop.Type == nil {
prop.Type = []string{pType}
}
if prop.Format == "" {
prop.Format = b.jsonSchemaFormat(b.keyFrom(fieldType))
}
return jsonName, modelDescription, prop
}
// check if annotation says it is a string
if jsonTag := field.Tag.Get("json"); jsonTag != "" {
s := strings.Split(jsonTag, ",")
if len(s) > 1 && s[1] == "string" {
stringt := "string"
prop.Type = []string{stringt}
return jsonName, modelDescription, prop
}
}
fieldKind := fieldType.Kind()
switch {
case fieldKind == reflect.Struct:
jsonName, prop := b.buildStructTypeProperty(field, jsonName, model)
return jsonName, modelDescription, prop
case fieldKind == reflect.Slice || fieldKind == reflect.Array:
jsonName, prop := b.buildArrayTypeProperty(field, jsonName, modelName)
return jsonName, modelDescription, prop
case fieldKind == reflect.Ptr:
jsonName, prop := b.buildPointerTypeProperty(field, jsonName, modelName)
return jsonName, modelDescription, prop
case fieldKind == reflect.String:
stringt := "string"
prop.Type = []string{stringt}
return jsonName, modelDescription, prop
case fieldKind == reflect.Map:
jsonName, prop := b.buildMapTypeProperty(field, jsonName, modelName)
return jsonName, modelDescription, prop
}
fieldTypeName := b.keyFrom(fieldType)
if b.isPrimitiveType(fieldTypeName) {
mapped := b.jsonSchemaType(fieldTypeName)
prop.Type = []string{mapped}
prop.Format = b.jsonSchemaFormat(fieldTypeName)
return jsonName, modelDescription, prop
}
modelType := b.keyFrom(fieldType)
prop.Ref = spec.MustCreateRef("#/definitions/" + modelType)
if fieldType.Name() == "" { // override type of anonymous structs
nestedTypeName := modelName + "." + jsonName
prop.Ref = spec.MustCreateRef("#/definitions/" + nestedTypeName)
b.addModel(fieldType, nestedTypeName)
}
return jsonName, modelDescription, prop
}
func hasNamedJSONTag(field reflect.StructField) bool {
parts := strings.Split(field.Tag.Get("json"), ",")
if len(parts) == 0 {
return false
}
for _, s := range parts[1:] {
if s == "inline" {
return false
}
}
return len(parts[0]) > 0
}
func (b definitionBuilder) buildStructTypeProperty(field reflect.StructField, jsonName string, model *spec.Schema) (nameJson string, prop spec.Schema) {
setPropertyMetadata(&prop, field)
fieldType := field.Type
// check for anonymous
if len(fieldType.Name()) == 0 {
// anonymous
anonType := model.ID + "." + jsonName
b.addModel(fieldType, anonType)
prop.Ref = spec.MustCreateRef("#/definitions/" + anonType)
return jsonName, prop
}
if field.Name == fieldType.Name() && field.Anonymous && !hasNamedJSONTag(field) {
// embedded struct
sub := definitionBuilder{make(spec.Definitions), b.Config}
sub.addModel(fieldType, "")
subKey := sub.keyFrom(fieldType)
// merge properties from sub
subModel, _ := sub.Definitions[subKey]
for k, v := range subModel.Properties {
model.Properties[k] = v
// if subModel says this property is required then include it
required := false
for _, each := range subModel.Required {
if k == each {
required = true
break
}
}
if required {
model.Required = append(model.Required, k)
}
}
// add all new referenced models
for key, sub := range sub.Definitions {
if key != subKey {
if _, ok := b.Definitions[key]; !ok {
b.Definitions[key] = sub
}
}
}
// empty name signals skip property
return "", prop
}
// simple struct
b.addModel(fieldType, "")
var pType = b.keyFrom(fieldType)
prop.Ref = spec.MustCreateRef("#/definitions/" + pType)
return jsonName, prop
}
func (b definitionBuilder) buildArrayTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop spec.Schema) {
setPropertyMetadata(&prop, field)
fieldType := field.Type
if fieldType.Elem().Kind() == reflect.Uint8 {
stringt := "string"
prop.Type = []string{stringt}
return jsonName, prop
}
var pType = "array"
prop.Type = []string{pType}
isPrimitive := b.isPrimitiveType(fieldType.Elem().Name())
elemTypeName := b.getElementTypeName(modelName, jsonName, fieldType.Elem())
prop.Items = &spec.SchemaOrArray{
Schema: &spec.Schema{},
}
if isPrimitive {
mapped := b.jsonSchemaType(elemTypeName)
prop.Items.Schema.Type = []string{mapped}
} else {
prop.Items.Schema.Ref = spec.MustCreateRef("#/definitions/" + elemTypeName)
}
// add|overwrite model for element type
if fieldType.Elem().Kind() == reflect.Ptr {
fieldType = fieldType.Elem()
}
if !isPrimitive {
b.addModel(fieldType.Elem(), elemTypeName)
}
return jsonName, prop
}
func (b definitionBuilder) buildMapTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop spec.Schema) {
setPropertyMetadata(&prop, field)
fieldType := field.Type
var pType = "object"
prop.Type = []string{pType}
// As long as the element isn't an interface, we should be able to figure out what the
// intended type is and represent it in `AdditionalProperties`.
// See: https://swagger.io/docs/specification/data-models/dictionaries/
if fieldType.Elem().Kind().String() != "interface" {
isPrimitive := b.isPrimitiveType(fieldType.Elem().Name())
elemTypeName := b.getElementTypeName(modelName, jsonName, fieldType.Elem())
prop.AdditionalProperties = &spec.SchemaOrBool{
Schema: &spec.Schema{},
}
if isPrimitive {
mapped := b.jsonSchemaType(elemTypeName)
prop.AdditionalProperties.Schema.Type = []string{mapped}
} else {
prop.AdditionalProperties.Schema.Ref = spec.MustCreateRef("#/definitions/" + elemTypeName)
}
// add|overwrite model for element type
if fieldType.Elem().Kind() == reflect.Ptr {
fieldType = fieldType.Elem()
}
if !isPrimitive {
b.addModel(fieldType.Elem(), elemTypeName)
}
}
return jsonName, prop
}
func (b definitionBuilder) buildPointerTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop spec.Schema) {
setPropertyMetadata(&prop, field)
fieldType := field.Type
// override type of pointer to list-likes
if fieldType.Elem().Kind() == reflect.Slice || fieldType.Elem().Kind() == reflect.Array {
var pType = "array"
prop.Type = []string{pType}
isPrimitive := b.isPrimitiveType(fieldType.Elem().Elem().Name())
elemName := b.getElementTypeName(modelName, jsonName, fieldType.Elem().Elem())
prop.Items = &spec.SchemaOrArray{
Schema: &spec.Schema{},
}
if isPrimitive {
primName := b.jsonSchemaType(elemName)
prop.Items.Schema.Type = []string{primName}
} else {
prop.Items.Schema.Ref = spec.MustCreateRef("#/definitions/" + elemName)
}
if !isPrimitive {
// add|overwrite model for element type
b.addModel(fieldType.Elem().Elem(), elemName)
}
} else {
// non-array, pointer type
fieldTypeName := b.keyFrom(fieldType.Elem())
var pType = b.jsonSchemaType(fieldTypeName) // no star, include pkg path
if b.isPrimitiveType(fieldTypeName) {
prop.Type = []string{pType}
prop.Format = b.jsonSchemaFormat(fieldTypeName)
return jsonName, prop
}
prop.Ref = spec.MustCreateRef("#/definitions/" + pType)
elemName := ""
if fieldType.Elem().Name() == "" {
elemName = modelName + "." + jsonName
prop.Ref = spec.MustCreateRef("#/definitions/" + elemName)
}
b.addModel(fieldType.Elem(), elemName)
}
return jsonName, prop
}
func (b definitionBuilder) getElementTypeName(modelName, jsonName string, t reflect.Type) string {
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Name() == "" {
return modelName + "." + jsonName
}
return b.keyFrom(t)
}
func (b definitionBuilder) keyFrom(st reflect.Type) string {
key := st.String()
if b.Config.ModelTypeNameHandler != nil {
if name, ok := b.Config.ModelTypeNameHandler(st); ok {
key = name
}
}
if len(st.Name()) == 0 { // unnamed type
// If it is an array, remove the leading []
key = strings.TrimPrefix(key, "[]")
// Swagger UI has special meaning for [
key = strings.Replace(key, "[]", "||", -1)
}
return key
}
// see also https://golang.org/ref/spec#Numeric_types
func (b definitionBuilder) isPrimitiveType(modelName string) bool {
if len(modelName) == 0 {
return false
}
return strings.Contains("uint uint8 uint16 uint32 uint64 int int8 int16 int32 int64 float32 float64 bool string byte rune time.Time", modelName)
}
// jsonNameOfField returns the name of the field as it should appear in JSON format
// An empty string indicates that this field is not part of the JSON representation
func (b definitionBuilder) jsonNameOfField(field reflect.StructField) string {
if jsonTag := field.Tag.Get("json"); jsonTag != "" {
s := strings.Split(jsonTag, ",")
if s[0] == "-" {
// empty name signals skip property
return ""
} else if s[0] != "" {
return s[0]
}
}
return field.Name
}
// see also http://json-schema.org/latest/json-schema-core.html#anchor8
func (b definitionBuilder) jsonSchemaType(modelName string) string {
schemaMap := map[string]string{
"uint": "integer",
"uint8": "integer",
"uint16": "integer",
"uint32": "integer",
"uint64": "integer",
"int": "integer",
"int8": "integer",
"int16": "integer",
"int32": "integer",
"int64": "integer",
"byte": "integer",
"float64": "number",
"float32": "number",
"bool": "boolean",
"time.Time": "string",
}
mapped, ok := schemaMap[modelName]
if !ok {
return modelName // use as is (custom or struct)
}
return mapped
}
func (b definitionBuilder) jsonSchemaFormat(modelName string) string {
if b.Config.SchemaFormatHandler != nil {
if mapped := b.Config.SchemaFormatHandler(modelName); mapped != "" {
return mapped
}
}
schemaMap := map[string]string{
"int": "int32",
"int32": "int32",
"int64": "int64",
"byte": "byte",
"uint": "integer",
"uint8": "byte",
"float64": "double",
"float32": "float",
"time.Time": "date-time",
"*time.Time": "date-time",
}
mapped, ok := schemaMap[modelName]
if !ok {
return "" // no format
}
return mapped
}

View File

@@ -0,0 +1,19 @@
package restfulspec
import restful "github.com/emicklei/go-restful"
func asParamType(kind int) string {
switch {
case kind == restful.PathParameterKind:
return "path"
case kind == restful.QueryParameterKind:
return "query"
case kind == restful.BodyParameterKind:
return "body"
case kind == restful.HeaderParameterKind:
return "header"
case kind == restful.FormParameterKind:
return "formData"
}
return ""
}

View File

@@ -0,0 +1,104 @@
package restfulspec
import (
"reflect"
"strconv"
"strings"
"github.com/go-openapi/spec"
)
func setDescription(prop *spec.Schema, field reflect.StructField) {
if tag := field.Tag.Get("description"); tag != "" {
prop.Description = tag
}
}
func setDefaultValue(prop *spec.Schema, field reflect.StructField) {
if tag := field.Tag.Get("default"); tag != "" {
prop.Default = stringAutoType(tag)
}
}
func setEnumValues(prop *spec.Schema, field reflect.StructField) {
// We use | to separate the enum values. This value is chosen
// since its unlikely to be useful in actual enumeration values.
if tag := field.Tag.Get("enum"); tag != "" {
enums := []interface{}{}
for _, s := range strings.Split(tag, "|") {
enums = append(enums, s)
}
prop.Enum = enums
}
}
func setMaximum(prop *spec.Schema, field reflect.StructField) {
if tag := field.Tag.Get("maximum"); tag != "" {
value, err := strconv.ParseFloat(tag, 64)
if err == nil {
prop.Maximum = &value
}
}
}
func setMinimum(prop *spec.Schema, field reflect.StructField) {
if tag := field.Tag.Get("minimum"); tag != "" {
value, err := strconv.ParseFloat(tag, 64)
if err == nil {
prop.Minimum = &value
}
}
}
func setType(prop *spec.Schema, field reflect.StructField) {
if tag := field.Tag.Get("type"); tag != "" {
// Check if the first two characters of the type tag are
// intended to emulate slice/array behaviour.
//
// If type is intended to be a slice/array then add the
// overriden type to the array item instead of the main property
if len(tag) > 2 && tag[0:2] == "[]" {
pType := "array"
prop.Type = []string{pType}
prop.Items = &spec.SchemaOrArray{
Schema: &spec.Schema{},
}
iType := tag[2:]
prop.Items.Schema.Type = []string{iType}
return
}
prop.Type = []string{tag}
}
}
func setUniqueItems(prop *spec.Schema, field reflect.StructField) {
tag := field.Tag.Get("unique")
switch tag {
case "true":
prop.UniqueItems = true
case "false":
prop.UniqueItems = false
}
}
func setReadOnly(prop *spec.Schema, field reflect.StructField) {
tag := field.Tag.Get("readOnly")
switch tag {
case "true":
prop.ReadOnly = true
case "false":
prop.ReadOnly = false
}
}
func setPropertyMetadata(prop *spec.Schema, field reflect.StructField) {
setDescription(prop, field)
setDefaultValue(prop, field)
setEnumValues(prop, field)
setMinimum(prop, field)
setMaximum(prop, field)
setUniqueItems(prop, field)
setType(prop, field)
setReadOnly(prop, field)
}

View File

@@ -0,0 +1,69 @@
package restfulspec
import (
restful "github.com/emicklei/go-restful"
"github.com/go-openapi/spec"
)
// NewOpenAPIService returns a new WebService that provides the API documentation of all services
// conform the OpenAPI documentation specifcation.
func NewOpenAPIService(config Config) *restful.WebService {
ws := new(restful.WebService)
ws.Path(config.APIPath)
ws.Produces(restful.MIME_JSON)
if config.DisableCORS {
ws.Filter(enableCORS)
}
swagger := BuildSwagger(config)
resource := specResource{swagger: swagger}
ws.Route(ws.GET("/").To(resource.getSwagger))
return ws
}
// BuildSwagger returns a Swagger object for all services' API endpoints.
func BuildSwagger(config Config) *spec.Swagger {
// collect paths and model definitions to build Swagger object.
paths := &spec.Paths{Paths: map[string]spec.PathItem{}}
definitions := spec.Definitions{}
for _, each := range config.WebServices {
for path, item := range buildPaths(each, config).Paths {
paths.Paths[path] = item
}
for name, def := range buildDefinitions(each, config) {
definitions[name] = def
}
}
swagger := &spec.Swagger{
SwaggerProps: spec.SwaggerProps{
Swagger: "2.0",
Paths: paths,
Definitions: definitions,
},
}
if config.PostBuildSwaggerObjectHandler != nil {
config.PostBuildSwaggerObjectHandler(swagger)
}
return swagger
}
func enableCORS(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
if origin := req.HeaderParameter(restful.HEADER_Origin); origin != "" {
// prevent duplicate header
if len(resp.Header().Get(restful.HEADER_AccessControlAllowOrigin)) == 0 {
resp.AddHeader(restful.HEADER_AccessControlAllowOrigin, origin)
}
}
chain.ProcessFilter(req, resp)
}
// specResource is a REST resource to serve the Open-API spec.
type specResource struct {
swagger *spec.Swagger
}
func (s specResource) getSwagger(req *restful.Request, resp *restful.Response) {
resp.WriteAsJson(s.swagger)
}

26
vendor/github.com/go-openapi/jsonpointer/.editorconfig generated vendored Normal file
View File

@@ -0,0 +1,26 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
# Set default charset
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
charset = utf-8
# Tab indentation (no size specified)
[*.go]
indent_style = tab
[*.md]
trim_trailing_whitespace = false
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

1
vendor/github.com/go-openapi/jsonpointer/.gitignore generated vendored Normal file
View File

@@ -0,0 +1 @@
secrets.yml

15
vendor/github.com/go-openapi/jsonpointer/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,15 @@
language: go
go:
- "1.8"
- "1.9"
- "1.10"
install:
- go get -u github.com/stretchr/testify/assert
- go get -u github.com/go-openapi/swag
script:
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
after_success:
- bash <(curl -s https://codecov.io/bash)
notifications:
slack:
secure: a5VgoiwB1G/AZqzmephPZIhEB9avMlsWSlVnM1dSAtYAwdrQHGTQxAmpOxYIoSPDhWNN5bfZmjd29++UlTwLcHSR+e0kJhH6IfDlsHj/HplNCJ9tyI0zYc7XchtdKgeMxMzBKCzgwFXGSbQGydXTliDNBo0HOzmY3cou/daMFTP60K+offcjS+3LRAYb1EroSRXZqrk1nuF/xDL3792DZUdPMiFR/L/Df6y74D6/QP4sTkTDFQitz4Wy/7jbsfj8dG6qK2zivgV6/l+w4OVjFkxVpPXogDWY10vVXNVynqxfJ7to2d1I9lNCHE2ilBCkWMIPdyJF7hjF8pKW+82yP4EzRh0vu8Xn0HT5MZpQxdRY/YMxNrWaG7SxsoEaO4q5uhgdzAqLYY3TRa7MjIK+7Ur+aqOeTXn6OKwVi0CjvZ6mIU3WUKSwiwkFZMbjRAkSb5CYwMEfGFO/z964xz83qGt6WAtBXNotqCQpTIiKtDHQeLOMfksHImCg6JLhQcWBVxamVgu0G3Pdh8Y6DyPnxraXY95+QDavbjqv7TeYT9T/FNnrkXaTTK0s4iWE5H4ACU0Qvz0wUYgfQrZv0/Hp7V17+rabUwnzYySHCy9SWX/7OV9Cfh31iMp9ZIffr76xmmThtOEqs8TrTtU6BWI3rWwvA9cXQipZTVtL0oswrGw=

View File

@@ -0,0 +1,74 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at ivan+abuse@flanders.co.nz. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

202
vendor/github.com/go-openapi/jsonpointer/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,202 @@
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.

15
vendor/github.com/go-openapi/jsonpointer/README.md generated vendored Normal file
View File

@@ -0,0 +1,15 @@
# gojsonpointer [![Build Status](https://travis-ci.org/go-openapi/jsonpointer.svg?branch=master)](https://travis-ci.org/go-openapi/jsonpointer) [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonpointer?status.svg)](http://godoc.org/github.com/go-openapi/jsonpointer)
An implementation of JSON Pointer - Go language
## Status
Completed YES
Tested YES
## References
http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
### Note
The 4.Evaluation part of the previous reference, starting with 'If the currently referenced value is a JSON array, the reference token MUST contain either...' is not implemented.

390
vendor/github.com/go-openapi/jsonpointer/pointer.go generated vendored Normal file
View File

@@ -0,0 +1,390 @@
// Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
//
// 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.
// author sigu-399
// author-github https://github.com/sigu-399
// author-mail sigu.399@gmail.com
//
// repository-name jsonpointer
// repository-desc An implementation of JSON Pointer - Go language
//
// description Main and unique file.
//
// created 25-02-2013
package jsonpointer
import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"github.com/go-openapi/swag"
)
const (
emptyPointer = ``
pointerSeparator = `/`
invalidStart = `JSON pointer must be empty or start with a "` + pointerSeparator
)
var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem()
var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem()
// JSONPointable is an interface for structs to implement when they need to customize the
// json pointer process
type JSONPointable interface {
JSONLookup(string) (interface{}, error)
}
// JSONSetable is an interface for structs to implement when they need to customize the
// json pointer process
type JSONSetable interface {
JSONSet(string, interface{}) error
}
// New creates a new json pointer for the given string
func New(jsonPointerString string) (Pointer, error) {
var p Pointer
err := p.parse(jsonPointerString)
return p, err
}
// Pointer the json pointer reprsentation
type Pointer struct {
referenceTokens []string
}
// "Constructor", parses the given string JSON pointer
func (p *Pointer) parse(jsonPointerString string) error {
var err error
if jsonPointerString != emptyPointer {
if !strings.HasPrefix(jsonPointerString, pointerSeparator) {
err = errors.New(invalidStart)
} else {
referenceTokens := strings.Split(jsonPointerString, pointerSeparator)
for _, referenceToken := range referenceTokens[1:] {
p.referenceTokens = append(p.referenceTokens, referenceToken)
}
}
}
return err
}
// Get uses the pointer to retrieve a value from a JSON document
func (p *Pointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
return p.get(document, swag.DefaultJSONNameProvider)
}
// Set uses the pointer to set a value from a JSON document
func (p *Pointer) Set(document interface{}, value interface{}) (interface{}, error) {
return document, p.set(document, value, swag.DefaultJSONNameProvider)
}
// GetForToken gets a value for a json pointer token 1 level deep
func GetForToken(document interface{}, decodedToken string) (interface{}, reflect.Kind, error) {
return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider)
}
// SetForToken gets a value for a json pointer token 1 level deep
func SetForToken(document interface{}, decodedToken string, value interface{}) (interface{}, error) {
return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider)
}
func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
rValue := reflect.Indirect(reflect.ValueOf(node))
kind := rValue.Kind()
switch kind {
case reflect.Struct:
if rValue.Type().Implements(jsonPointableType) {
r, err := node.(JSONPointable).JSONLookup(decodedToken)
if err != nil {
return nil, kind, err
}
return r, kind, nil
}
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
if !ok {
return nil, kind, fmt.Errorf("object has no field %q", decodedToken)
}
fld := rValue.FieldByName(nm)
return fld.Interface(), kind, nil
case reflect.Map:
kv := reflect.ValueOf(decodedToken)
mv := rValue.MapIndex(kv)
if mv.IsValid() && !swag.IsZero(mv) {
return mv.Interface(), kind, nil
}
return nil, kind, fmt.Errorf("object has no key %q", decodedToken)
case reflect.Slice:
tokenIndex, err := strconv.Atoi(decodedToken)
if err != nil {
return nil, kind, err
}
sLength := rValue.Len()
if tokenIndex < 0 || tokenIndex >= sLength {
return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex)
}
elem := rValue.Index(tokenIndex)
return elem.Interface(), kind, nil
default:
return nil, kind, fmt.Errorf("invalid token reference %q", decodedToken)
}
}
func setSingleImpl(node, data interface{}, decodedToken string, nameProvider *swag.NameProvider) error {
rValue := reflect.Indirect(reflect.ValueOf(node))
switch rValue.Kind() {
case reflect.Struct:
if ns, ok := node.(JSONSetable); ok { // pointer impl
return ns.JSONSet(decodedToken, data)
}
if rValue.Type().Implements(jsonSetableType) {
return node.(JSONSetable).JSONSet(decodedToken, data)
}
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
if !ok {
return fmt.Errorf("object has no field %q", decodedToken)
}
fld := rValue.FieldByName(nm)
if fld.IsValid() {
fld.Set(reflect.ValueOf(data))
}
return nil
case reflect.Map:
kv := reflect.ValueOf(decodedToken)
rValue.SetMapIndex(kv, reflect.ValueOf(data))
return nil
case reflect.Slice:
tokenIndex, err := strconv.Atoi(decodedToken)
if err != nil {
return err
}
sLength := rValue.Len()
if tokenIndex < 0 || tokenIndex >= sLength {
return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
}
elem := rValue.Index(tokenIndex)
if !elem.CanSet() {
return fmt.Errorf("can't set slice index %s to %v", decodedToken, data)
}
elem.Set(reflect.ValueOf(data))
return nil
default:
return fmt.Errorf("invalid token reference %q", decodedToken)
}
}
func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
if nameProvider == nil {
nameProvider = swag.DefaultJSONNameProvider
}
kind := reflect.Invalid
// Full document when empty
if len(p.referenceTokens) == 0 {
return node, kind, nil
}
for _, token := range p.referenceTokens {
decodedToken := Unescape(token)
r, knd, err := getSingleImpl(node, decodedToken, nameProvider)
if err != nil {
return nil, knd, err
}
node, kind = r, knd
}
rValue := reflect.ValueOf(node)
kind = rValue.Kind()
return node, kind, nil
}
func (p *Pointer) set(node, data interface{}, nameProvider *swag.NameProvider) error {
knd := reflect.ValueOf(node).Kind()
if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
return fmt.Errorf("only structs, pointers, maps and slices are supported for setting values")
}
if nameProvider == nil {
nameProvider = swag.DefaultJSONNameProvider
}
// Full document when empty
if len(p.referenceTokens) == 0 {
return nil
}
lastI := len(p.referenceTokens) - 1
for i, token := range p.referenceTokens {
isLastToken := i == lastI
decodedToken := Unescape(token)
if isLastToken {
return setSingleImpl(node, data, decodedToken, nameProvider)
}
rValue := reflect.Indirect(reflect.ValueOf(node))
kind := rValue.Kind()
switch kind {
case reflect.Struct:
if rValue.Type().Implements(jsonPointableType) {
r, err := node.(JSONPointable).JSONLookup(decodedToken)
if err != nil {
return err
}
fld := reflect.ValueOf(r)
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
node = fld.Addr().Interface()
continue
}
node = r
continue
}
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
if !ok {
return fmt.Errorf("object has no field %q", decodedToken)
}
fld := rValue.FieldByName(nm)
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
node = fld.Addr().Interface()
continue
}
node = fld.Interface()
case reflect.Map:
kv := reflect.ValueOf(decodedToken)
mv := rValue.MapIndex(kv)
if !mv.IsValid() {
return fmt.Errorf("object has no key %q", decodedToken)
}
if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr {
node = mv.Addr().Interface()
continue
}
node = mv.Interface()
case reflect.Slice:
tokenIndex, err := strconv.Atoi(decodedToken)
if err != nil {
return err
}
sLength := rValue.Len()
if tokenIndex < 0 || tokenIndex >= sLength {
return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
}
elem := rValue.Index(tokenIndex)
if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr {
node = elem.Addr().Interface()
continue
}
node = elem.Interface()
default:
return fmt.Errorf("invalid token reference %q", decodedToken)
}
}
return nil
}
// DecodedTokens returns the decoded tokens
func (p *Pointer) DecodedTokens() []string {
result := make([]string, 0, len(p.referenceTokens))
for _, t := range p.referenceTokens {
result = append(result, Unescape(t))
}
return result
}
// IsEmpty returns true if this is an empty json pointer
// this indicates that it points to the root document
func (p *Pointer) IsEmpty() bool {
return len(p.referenceTokens) == 0
}
// Pointer to string representation function
func (p *Pointer) String() string {
if len(p.referenceTokens) == 0 {
return emptyPointer
}
pointerString := pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator)
return pointerString
}
// Specific JSON pointer encoding here
// ~0 => ~
// ~1 => /
// ... and vice versa
const (
encRefTok0 = `~0`
encRefTok1 = `~1`
decRefTok0 = `~`
decRefTok1 = `/`
)
// Unescape unescapes a json pointer reference token string to the original representation
func Unescape(token string) string {
step1 := strings.Replace(token, encRefTok1, decRefTok1, -1)
step2 := strings.Replace(step1, encRefTok0, decRefTok0, -1)
return step2
}
// Escape escapes a pointer reference token string
func Escape(token string) string {
step1 := strings.Replace(token, decRefTok0, encRefTok0, -1)
step2 := strings.Replace(step1, decRefTok1, encRefTok1, -1)
return step2
}

View File

@@ -0,0 +1 @@
secrets.yml

16
vendor/github.com/go-openapi/jsonreference/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,16 @@
language: go
go:
- "1.8"
- "1.9"
- "1.10"
install:
- go get -u github.com/stretchr/testify/assert
- go get -u github.com/PuerkitoBio/purell
- go get -u github.com/go-openapi/jsonpointer
script:
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
after_success:
- bash <(curl -s https://codecov.io/bash)
notifications:
slack:
secure: OpQG/36F7DSF00HLm9WZMhyqFCYYyYTsVDObW226cWiR8PWYiNfLZiSEvIzT1Gx4dDjhigKTIqcLhG34CkL5iNXDjm9Yyo2RYhQPlK8NErNqUEXuBqn4RqYHW48VGhEhOyDd4Ei0E2FN5ZbgpvHgtpkdZ6XDi64r3Ac89isP9aPHXQTuv2Jog6b4/OKKiUTftLcTIst0p4Cp3gqOJWf1wnoj+IadWiECNVQT6zb47IYjtyw6+uV8iUjTzdKcRB6Zc6b4Dq7JAg1Zd7Jfxkql3hlKp4PNlRf9Cy7y5iA3G7MLyg3FcPX5z2kmcyPt2jOTRMBWUJ5zIQpOxizAcN8WsT3WWBL5KbuYK6k0PzujrIDLqdxGpNmjkkMfDBT9cKmZpm2FdW+oZgPFJP+oKmAo4u4KJz/vjiPTXgQlN5bmrLuRMCp+AwC5wkIohTqWZVPE2TK6ZSnMYcg/W39s+RP/9mJoyryAvPSpBOLTI+biCgaUCTOAZxNTWpMFc3tPYntc41WWkdKcooZ9JA5DwfcaVFyTGQ3YXz+HvX6G1z/gW0Q/A4dBi9mj2iE1xm7tRTT+4VQ2AXFvSEI1HJpfPgYnwAtwOD1v3Qm2EUHk9sCdtEDR4wVGEPIVn44GnwFMnGKx9JWppMPYwFu3SVDdHt+E+LOlhZUply11Aa+IVrT2KUQ=

View File

@@ -0,0 +1,74 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at ivan+abuse@flanders.co.nz. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

202
vendor/github.com/go-openapi/jsonreference/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,202 @@
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.

15
vendor/github.com/go-openapi/jsonreference/README.md generated vendored Normal file
View File

@@ -0,0 +1,15 @@
# gojsonreference [![Build Status](https://travis-ci.org/go-openapi/jsonreference.svg?branch=master)](https://travis-ci.org/go-openapi/jsonreference) [![codecov](https://codecov.io/gh/go-openapi/jsonreference/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonreference) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonreference?status.svg)](http://godoc.org/github.com/go-openapi/jsonreference)
An implementation of JSON Reference - Go language
## Status
Work in progress ( 90% done )
## Dependencies
https://github.com/go-openapi/jsonpointer
## References
http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03

156
vendor/github.com/go-openapi/jsonreference/reference.go generated vendored Normal file
View File

@@ -0,0 +1,156 @@
// Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
//
// 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.
// author sigu-399
// author-github https://github.com/sigu-399
// author-mail sigu.399@gmail.com
//
// repository-name jsonreference
// repository-desc An implementation of JSON Reference - Go language
//
// description Main and unique file.
//
// created 26-02-2013
package jsonreference
import (
"errors"
"net/url"
"strings"
"github.com/PuerkitoBio/purell"
"github.com/go-openapi/jsonpointer"
)
const (
fragmentRune = `#`
)
// New creates a new reference for the given string
func New(jsonReferenceString string) (Ref, error) {
var r Ref
err := r.parse(jsonReferenceString)
return r, err
}
// MustCreateRef parses the ref string and panics when it's invalid.
// Use the New method for a version that returns an error
func MustCreateRef(ref string) Ref {
r, err := New(ref)
if err != nil {
panic(err)
}
return r
}
// Ref represents a json reference object
type Ref struct {
referenceURL *url.URL
referencePointer jsonpointer.Pointer
HasFullURL bool
HasURLPathOnly bool
HasFragmentOnly bool
HasFileScheme bool
HasFullFilePath bool
}
// GetURL gets the URL for this reference
func (r *Ref) GetURL() *url.URL {
return r.referenceURL
}
// GetPointer gets the json pointer for this reference
func (r *Ref) GetPointer() *jsonpointer.Pointer {
return &r.referencePointer
}
// String returns the best version of the url for this reference
func (r *Ref) String() string {
if r.referenceURL != nil {
return r.referenceURL.String()
}
if r.HasFragmentOnly {
return fragmentRune + r.referencePointer.String()
}
return r.referencePointer.String()
}
// IsRoot returns true if this reference is a root document
func (r *Ref) IsRoot() bool {
return r.referenceURL != nil &&
!r.IsCanonical() &&
!r.HasURLPathOnly &&
r.referenceURL.Fragment == ""
}
// IsCanonical returns true when this pointer starts with http(s):// or file://
func (r *Ref) IsCanonical() bool {
return (r.HasFileScheme && r.HasFullFilePath) || (!r.HasFileScheme && r.HasFullURL)
}
// "Constructor", parses the given string JSON reference
func (r *Ref) parse(jsonReferenceString string) error {
parsed, err := url.Parse(jsonReferenceString)
if err != nil {
return err
}
r.referenceURL, _ = url.Parse(purell.NormalizeURL(parsed, purell.FlagsSafe|purell.FlagRemoveDuplicateSlashes))
refURL := r.referenceURL
if refURL.Scheme != "" && refURL.Host != "" {
r.HasFullURL = true
} else {
if refURL.Path != "" {
r.HasURLPathOnly = true
} else if refURL.RawQuery == "" && refURL.Fragment != "" {
r.HasFragmentOnly = true
}
}
r.HasFileScheme = refURL.Scheme == "file"
r.HasFullFilePath = strings.HasPrefix(refURL.Path, "/")
// invalid json-pointer error means url has no json-pointer fragment. simply ignore error
r.referencePointer, _ = jsonpointer.New(refURL.Fragment)
return nil
}
// Inherits creates a new reference from a parent and a child
// If the child cannot inherit from the parent, an error is returned
func (r *Ref) Inherits(child Ref) (*Ref, error) {
childURL := child.GetURL()
parentURL := r.GetURL()
if childURL == nil {
return nil, errors.New("child url is nil")
}
if parentURL == nil {
return &child, nil
}
ref, err := New(parentURL.ResolveReference(childURL).String())
if err != nil {
return nil, err
}
return &ref, nil
}

26
vendor/github.com/go-openapi/spec/.editorconfig generated vendored Normal file
View File

@@ -0,0 +1,26 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
# Set default charset
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
charset = utf-8
# Tab indentation (no size specified)
[*.go]
indent_style = tab
[*.md]
trim_trailing_whitespace = false
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

2
vendor/github.com/go-openapi/spec/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
secrets.yml
coverage.out

20
vendor/github.com/go-openapi/spec/.golangci.yml generated vendored Normal file
View File

@@ -0,0 +1,20 @@
linters-settings:
govet:
check-shadowing: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 25
maligned:
suggest-new: true
dupl:
threshold: 100
goconst:
min-len: 2
min-occurrences: 2
linters:
enable-all: true
disable:
- maligned
- unparam

18
vendor/github.com/go-openapi/spec/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,18 @@
language: go
go:
- 1.7
- 1.8
- 1.9
install:
- go get -u github.com/stretchr/testify
- go get -u github.com/go-openapi/swag
- go get -u gopkg.in/yaml.v2
- go get -u github.com/go-openapi/jsonpointer
- go get -u github.com/go-openapi/jsonreference
script:
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
after_success:
- bash <(curl -s https://codecov.io/bash)
notifications:
slack:
secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E=

74
vendor/github.com/go-openapi/spec/CODE_OF_CONDUCT.md generated vendored Normal file
View File

@@ -0,0 +1,74 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at ivan+abuse@flanders.co.nz. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

202
vendor/github.com/go-openapi/spec/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,202 @@
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.

10
vendor/github.com/go-openapi/spec/README.md generated vendored Normal file
View File

@@ -0,0 +1,10 @@
# OAI object model [![Build Status](https://travis-ci.org/go-openapi/spec.svg?branch=master)](https://travis-ci.org/go-openapi/spec) [![codecov](https://codecov.io/gh/go-openapi/spec/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/spec) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/go-openapi/spec?status.svg)](http://godoc.org/github.com/go-openapi/spec)
[![GolangCI](https://golangci.com/badges/github.com/go-openapi/spec.svg)](https://golangci.com)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/spec)](https://goreportcard.com/report/github.com/go-openapi/spec)
The object model for OpenAPI specification documents.
Currently supports Swagger 2.0.

260
vendor/github.com/go-openapi/spec/bindata.go generated vendored Normal file

File diff suppressed because one or more lines are too long

24
vendor/github.com/go-openapi/spec/contact_info.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
// Copyright 2015 go-swagger maintainers
//
// 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 spec
// ContactInfo contact information for the exposed API.
//
// For more information: http://goo.gl/8us55a#contactObject
type ContactInfo struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
Email string `json:"email,omitempty"`
}

47
vendor/github.com/go-openapi/spec/debug.go generated vendored Normal file
View File

@@ -0,0 +1,47 @@
// Copyright 2015 go-swagger maintainers
//
// 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 spec
import (
"fmt"
"log"
"os"
"path/filepath"
"runtime"
)
var (
// Debug is true when the SWAGGER_DEBUG env var is not empty.
// It enables a more verbose logging of validators.
Debug = os.Getenv("SWAGGER_DEBUG") != ""
// validateLogger is a debug logger for this package
specLogger *log.Logger
)
func init() {
debugOptions()
}
func debugOptions() {
specLogger = log.New(os.Stdout, "spec:", log.LstdFlags)
}
func debugLog(msg string, args ...interface{}) {
// A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog()
if Debug {
_, file1, pos1, _ := runtime.Caller(1)
specLogger.Printf("%s:%d: %s", filepath.Base(file1), pos1, fmt.Sprintf(msg, args...))
}
}

1172
vendor/github.com/go-openapi/spec/expander.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

24
vendor/github.com/go-openapi/spec/external_docs.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
// Copyright 2015 go-swagger maintainers
//
// 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 spec
// ExternalDocumentation allows referencing an external resource for
// extended documentation.
//
// For more information: http://goo.gl/8us55a#externalDocumentationObject
type ExternalDocumentation struct {
Description string `json:"description,omitempty"`
URL string `json:"url,omitempty"`
}

193
vendor/github.com/go-openapi/spec/header.go generated vendored Normal file
View File

@@ -0,0 +1,193 @@
// Copyright 2015 go-swagger maintainers
//
// 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 spec
import (
"encoding/json"
"strings"
"github.com/go-openapi/jsonpointer"
"github.com/go-openapi/swag"
)
// HeaderProps describes a response header
type HeaderProps struct {
Description string `json:"description,omitempty"`
}
// Header describes a header for a response of the API
//
// For more information: http://goo.gl/8us55a#headerObject
type Header struct {
CommonValidations
SimpleSchema
VendorExtensible
HeaderProps
}
// ResponseHeader creates a new header instance for use in a response
func ResponseHeader() *Header {
return new(Header)
}
// WithDescription sets the description on this response, allows for chaining
func (h *Header) WithDescription(description string) *Header {
h.Description = description
return h
}
// Typed a fluent builder method for the type of parameter
func (h *Header) Typed(tpe, format string) *Header {
h.Type = tpe
h.Format = format
return h
}
// CollectionOf a fluent builder method for an array item
func (h *Header) CollectionOf(items *Items, format string) *Header {
h.Type = "array"
h.Items = items
h.CollectionFormat = format
return h
}
// WithDefault sets the default value on this item
func (h *Header) WithDefault(defaultValue interface{}) *Header {
h.Default = defaultValue
return h
}
// WithMaxLength sets a max length value
func (h *Header) WithMaxLength(max int64) *Header {
h.MaxLength = &max
return h
}
// WithMinLength sets a min length value
func (h *Header) WithMinLength(min int64) *Header {
h.MinLength = &min
return h
}
// WithPattern sets a pattern value
func (h *Header) WithPattern(pattern string) *Header {
h.Pattern = pattern
return h
}
// WithMultipleOf sets a multiple of value
func (h *Header) WithMultipleOf(number float64) *Header {
h.MultipleOf = &number
return h
}
// WithMaximum sets a maximum number value
func (h *Header) WithMaximum(max float64, exclusive bool) *Header {
h.Maximum = &max
h.ExclusiveMaximum = exclusive
return h
}
// WithMinimum sets a minimum number value
func (h *Header) WithMinimum(min float64, exclusive bool) *Header {
h.Minimum = &min
h.ExclusiveMinimum = exclusive
return h
}
// WithEnum sets a the enum values (replace)
func (h *Header) WithEnum(values ...interface{}) *Header {
h.Enum = append([]interface{}{}, values...)
return h
}
// WithMaxItems sets the max items
func (h *Header) WithMaxItems(size int64) *Header {
h.MaxItems = &size
return h
}
// WithMinItems sets the min items
func (h *Header) WithMinItems(size int64) *Header {
h.MinItems = &size
return h
}
// UniqueValues dictates that this array can only have unique items
func (h *Header) UniqueValues() *Header {
h.UniqueItems = true
return h
}
// AllowDuplicates this array can have duplicates
func (h *Header) AllowDuplicates() *Header {
h.UniqueItems = false
return h
}
// MarshalJSON marshal this to JSON
func (h Header) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(h.CommonValidations)
if err != nil {
return nil, err
}
b2, err := json.Marshal(h.SimpleSchema)
if err != nil {
return nil, err
}
b3, err := json.Marshal(h.HeaderProps)
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// UnmarshalJSON unmarshals this header from JSON
func (h *Header) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &h.CommonValidations); err != nil {
return err
}
if err := json.Unmarshal(data, &h.SimpleSchema); err != nil {
return err
}
if err := json.Unmarshal(data, &h.VendorExtensible); err != nil {
return err
}
return json.Unmarshal(data, &h.HeaderProps)
}
// JSONLookup look up a value by the json property name
func (h Header) JSONLookup(token string) (interface{}, error) {
if ex, ok := h.Extensions[token]; ok {
return &ex, nil
}
r, _, err := jsonpointer.GetForToken(h.CommonValidations, token)
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
return nil, err
}
if r != nil {
return r, nil
}
r, _, err = jsonpointer.GetForToken(h.SimpleSchema, token)
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
return nil, err
}
if r != nil {
return r, nil
}
r, _, err = jsonpointer.GetForToken(h.HeaderProps, token)
return r, err
}

168
vendor/github.com/go-openapi/spec/info.go generated vendored Normal file
View File

@@ -0,0 +1,168 @@
// Copyright 2015 go-swagger maintainers
//
// 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 spec
import (
"encoding/json"
"strings"
"github.com/go-openapi/jsonpointer"
"github.com/go-openapi/swag"
)
// Extensions vendor specific extensions
type Extensions map[string]interface{}
// Add adds a value to these extensions
func (e Extensions) Add(key string, value interface{}) {
realKey := strings.ToLower(key)
e[realKey] = value
}
// GetString gets a string value from the extensions
func (e Extensions) GetString(key string) (string, bool) {
if v, ok := e[strings.ToLower(key)]; ok {
str, ok := v.(string)
return str, ok
}
return "", false
}
// GetBool gets a string value from the extensions
func (e Extensions) GetBool(key string) (bool, bool) {
if v, ok := e[strings.ToLower(key)]; ok {
str, ok := v.(bool)
return str, ok
}
return false, false
}
// GetStringSlice gets a string value from the extensions
func (e Extensions) GetStringSlice(key string) ([]string, bool) {
if v, ok := e[strings.ToLower(key)]; ok {
arr, isSlice := v.([]interface{})
if !isSlice {
return nil, false
}
var strs []string
for _, iface := range arr {
str, isString := iface.(string)
if !isString {
return nil, false
}
strs = append(strs, str)
}
return strs, ok
}
return nil, false
}
// VendorExtensible composition block.
type VendorExtensible struct {
Extensions Extensions
}
// AddExtension adds an extension to this extensible object
func (v *VendorExtensible) AddExtension(key string, value interface{}) {
if value == nil {
return
}
if v.Extensions == nil {
v.Extensions = make(map[string]interface{})
}
v.Extensions.Add(key, value)
}
// MarshalJSON marshals the extensions to json
func (v VendorExtensible) MarshalJSON() ([]byte, error) {
toser := make(map[string]interface{})
for k, v := range v.Extensions {
lk := strings.ToLower(k)
if strings.HasPrefix(lk, "x-") {
toser[k] = v
}
}
return json.Marshal(toser)
}
// UnmarshalJSON for this extensible object
func (v *VendorExtensible) UnmarshalJSON(data []byte) error {
var d map[string]interface{}
if err := json.Unmarshal(data, &d); err != nil {
return err
}
for k, vv := range d {
lk := strings.ToLower(k)
if strings.HasPrefix(lk, "x-") {
if v.Extensions == nil {
v.Extensions = map[string]interface{}{}
}
v.Extensions[k] = vv
}
}
return nil
}
// InfoProps the properties for an info definition
type InfoProps struct {
Description string `json:"description,omitempty"`
Title string `json:"title,omitempty"`
TermsOfService string `json:"termsOfService,omitempty"`
Contact *ContactInfo `json:"contact,omitempty"`
License *License `json:"license,omitempty"`
Version string `json:"version,omitempty"`
}
// Info object provides metadata about the API.
// The metadata can be used by the clients if needed, and can be presented in the Swagger-UI for convenience.
//
// For more information: http://goo.gl/8us55a#infoObject
type Info struct {
VendorExtensible
InfoProps
}
// JSONLookup look up a value by the json property name
func (i Info) JSONLookup(token string) (interface{}, error) {
if ex, ok := i.Extensions[token]; ok {
return &ex, nil
}
r, _, err := jsonpointer.GetForToken(i.InfoProps, token)
return r, err
}
// MarshalJSON marshal this to JSON
func (i Info) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(i.InfoProps)
if err != nil {
return nil, err
}
b2, err := json.Marshal(i.VendorExtensible)
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2), nil
}
// UnmarshalJSON marshal this from JSON
func (i *Info) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &i.InfoProps); err != nil {
return err
}
if err := json.Unmarshal(data, &i.VendorExtensible); err != nil {
return err
}
return nil
}

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