devops tenant api

Signed-off-by: runzexia <runzexia@yunify.com>
This commit is contained in:
runzexia
2019-04-23 20:47:47 +08:00
committed by zryfish
parent 78f2dab18c
commit 5a6f51d775
143 changed files with 19533 additions and 341 deletions

203
Gopkg.lock generated
View File

@@ -10,7 +10,7 @@
version = "v0.37.0"
[[projects]]
digest = "1:2be791e7b333ff7c06f8fb3dc18a7d70580e9399dbdffd352621d067ff260b6e"
digest = "1:26b14a6dc72ace253599e969997d5ecf2143c63833c015179786bc756c76eaa4"
name = "github.com/Microsoft/go-winio"
packages = ["."]
pruneopts = "NUT"
@@ -43,7 +43,7 @@
[[projects]]
branch = "master"
digest = "1:2795ce42ac79b6a0733d76c5c2de94f458fa0892949c1768351e2b5e41b5abc6"
digest = "1:1e31f7a894b511019f6d5ce4a9462cb60151440645bb25dd48b5940ff2bbbdf1"
name = "github.com/aead/chacha20"
packages = [
".",
@@ -60,6 +60,14 @@
revision = "7c0e3b262f30165a8ec3d0b4c6059fd92703bfb2"
version = "1.0.0"
[[projects]]
digest = "1:5a23cd3a5496a0b2da7e3b348d14e77b11a210738c398200d7d6f04ea8cf3bd8"
name = "github.com/asaskevich/govalidator"
packages = ["."]
pruneopts = "NUT"
revision = "ccb8e960c48f04d6935e72476ae4a51028f9e22f"
version = "v9"
[[projects]]
branch = "master"
digest = "1:707ebe952a8b3d00b343c01536c79c73771d100f63ec6babeaed5c79e2b8a8dd"
@@ -69,7 +77,7 @@
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
[[projects]]
digest = "1:92aa7f157d0b4d08199dd6d9697652bc65c7d2179c15918141a357bf486559e0"
digest = "1:4d43362859489b0672b9a1544d5ae02231202a43172c7eda7a55d44799f97f53"
name = "github.com/bifurcation/mint"
packages = [
".",
@@ -112,7 +120,7 @@
[[projects]]
branch = "master"
digest = "1:4c7d169280debf9f36b84a0f682094889cccc5dc0db8657f9cffc93b21975a57"
digest = "1:5b5643142a4d48d55f7a2ad09da4565be2b0ecc554c903a25d18b18159d39b9f"
name = "github.com/docker/distribution"
packages = [
"digestset",
@@ -122,7 +130,7 @@
revision = "6d62eb1d4a3515399431b713fde3ce5a9b40e8d5"
[[projects]]
digest = "1:b75e9ec256dfcb9208b8e65baf9f5aecfd86ab7d93d5424565aa3d38832ea9c6"
digest = "1:addad6aa992efe1cd74f5bec0b069a68b35db1da8b042581002215d59b3ac54d"
name = "github.com/docker/docker"
packages = [
"api",
@@ -152,7 +160,7 @@
[[projects]]
branch = "master"
digest = "1:42cfcc9461365aacb806b9cfe929c926f0d3fb0f692bfc10c375872f2881b2f2"
digest = "1:0c6d0df813996e4b7bfd40eb2f0671755159576e0b38263babeef1ee7bbfe4d0"
name = "github.com/docker/go-connections"
packages = [
"nat",
@@ -180,7 +188,7 @@
[[projects]]
branch = "master"
digest = "1:eb8f1b1913bffd6e788deee9fe4ba3a4d83267aff6045d3be33105e35ece290b"
digest = "1:8d9247519c7cce3720ec913acc3a8d593ba04690ffd83b3b3a4d08f662c2d6b3"
name = "github.com/docker/spdystream"
packages = [
".",
@@ -198,7 +206,7 @@
version = "v1.0.0"
[[projects]]
digest = "1:aab9e95808c688b75757e520e59c48eb954991693dfc5441f0e87d1f9cb8b7bb"
digest = "1:3267a1e17bf240decf62c364e861d79e68b52b5804d63b510b0baed70b9a6aee"
name = "github.com/emicklei/go-restful"
packages = [
".",
@@ -217,7 +225,7 @@
version = "v1.0.0"
[[projects]]
digest = "1:b498b36dbb2b306d1c5205ee5236c9e60352be8f9eea9bf08186723a9f75b4f3"
digest = "1:85b82e47d3bacb3125fbcb321b828f6c1c1e9afc307166dae6b6342b66aa427c"
name = "github.com/emirpasic/gods"
packages = [
"containers",
@@ -239,6 +247,14 @@
revision = "72bf35d0ff611848c1dc9df0f976c81192392fa5"
version = "v4.1.0"
[[projects]]
digest = "1:aa3ed0a71c4e66e4ae6486bf97a3f4cab28edc78df2e50c5ad01dc7d91604b88"
name = "github.com/fatih/structs"
packages = ["."]
pruneopts = "NUT"
revision = "4966fc68f5b7593aafa6cbbba2d65ec6e1416047"
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:1ccd7321e62f680a988bba496f0f5a9c80410b8104d55b0f6b8ecf84ad328476"
@@ -312,7 +328,7 @@
version = "v0.18.0"
[[projects]]
digest = "1:513691cf6809d4fa2482469adb88aba530f96a5c4a9c68a09a0786519b07c24a"
digest = "1:589fc282fd63dd84be3c91d87feaaa67a969612ba1ca33b911755fd95f460358"
name = "github.com/go-redis/redis"
packages = [
".",
@@ -344,7 +360,17 @@
version = "v1.6.15"
[[projects]]
digest = "1:a1b2a5e38f79688ee8250942d5fa960525fceb1024c855c7bc76fa77b0f3cca2"
digest = "1:3b878cddad697b48929214a1bbd46d1cd411e79855fa502f0d33f7a30c2f882c"
name = "github.com/gocraft/dbr"
packages = [
".",
"dialect",
]
pruneopts = "NUT"
revision = "a0fd650918f6287ffe111d1c7b66bb755ff3be4a"
[[projects]]
digest = "1:f1631663db3b95aec6a1610560b33ede5e70011b0e8cad87b110016a6fbfc2db"
name = "github.com/gogo/protobuf"
packages = [
"proto",
@@ -354,6 +380,14 @@
revision = "ba06b47c162d49f2af050fb4c75bcbc86a159d5c"
version = "v1.2.1"
[[projects]]
branch = "master"
digest = "1:4cd45b97f006fcd976470886b5fe97f59494e0f7b8e626059500cb8ace0e4e73"
name = "github.com/golang/example"
packages = ["stringutil"]
pruneopts = "NUT"
revision = "46695d81d1fae905a270fb7db8a4d11a334562fe"
[[projects]]
branch = "master"
digest = "1:e2b86e41f3d669fc36b50d31d32d22c8ac656c75aa5ea89717ce7177e134ff2a"
@@ -371,7 +405,7 @@
revision = "5b532d6fd5efaf7fa130d4e859a2fde0fc3a9e1b"
[[projects]]
digest = "1:2d0636a8c490d2272dd725db26f74a537111b99b9dbdda0d8b98febe63702aa4"
digest = "1:a8b59d8995b50db3b206d9160817e00aace183e456cb60abf5157de16d12e3c9"
name = "github.com/golang/protobuf"
packages = [
"proto",
@@ -392,6 +426,14 @@
pruneopts = "NUT"
revision = "4030bb1f1f0c35b30ca7009e9ebd06849dd45306"
[[projects]]
digest = "1:a63cff6b5d8b95638bfe300385d93b2a6d9d687734b863da8e09dc834510a690"
name = "github.com/google/go-querystring"
packages = ["query"]
pruneopts = "NUT"
revision = "44c6ddd0a2342c386950e880b658017258da92fc"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:52c5834e2bebac9030c97cc0798ac11c3aa8a39f098aeb419f142533da6cd3cc"
@@ -409,7 +451,7 @@
version = "v1.1.1"
[[projects]]
digest = "1:06a7dadb7b760767341ffb6c8d377238d68a1226f2b21b5d497d2e3f6ecf6b4e"
digest = "1:add738701bd5b2b985c0c37011092c57218bdc46caf1e682a73dc210ad36b03f"
name = "github.com/googleapis/gnostic"
packages = [
"OpenAPIv2",
@@ -438,7 +480,7 @@
[[projects]]
branch = "master"
digest = "1:a86d65bc23eea505cd9139178e4d889733928fe165c7a008f41eaab039edf9df"
digest = "1:790debb569e0ca4a39c168cae515a5f6b353229ca351c0b4208ef6964934aaed"
name = "github.com/gregjones/httpcache"
packages = [
".",
@@ -475,7 +517,7 @@
version = "v0.5.1"
[[projects]]
digest = "1:41933d387bfa3eaa6a82647914ed7044f7b8355764c24fb920892bc8c03ef0c3"
digest = "1:e4d4b786065b1879481dcfa5da9886f40fde00d6ab7dadea53d6d7dc943c4792"
name = "github.com/hpcloud/tail"
packages = [
".",
@@ -562,7 +604,7 @@
[[projects]]
branch = "kubesphere"
digest = "1:d73bdba7430fe5491bdcb463166389a7467f1cab2ac902c8d12f93890278a5b6"
digest = "1:7bcdcd306f2dccd6d8e0e25d861de0ce94b93b0971604c879afeaad8f5439a11"
name = "github.com/kiali/kiali"
packages = [
"business",
@@ -601,7 +643,7 @@
[[projects]]
branch = "master"
digest = "1:c9258bacbb5c016d8eb7154c656cdc3cbec688ce74d57d5d2c35d3b33f5c7cb0"
digest = "1:da3896234af34d707626fa39b1402cd480908966354a792c503486c4f5ef4de7"
name = "github.com/knative/pkg"
packages = [
"apis/istio",
@@ -635,7 +677,7 @@
[[projects]]
branch = "master"
digest = "1:68a59bc3410e2fd7b5341057d54ec3aedb5c4aff7d0c59b72ced7d5fe6b1b92c"
digest = "1:11728f1747eea824b5d8194f85b5a7c0af7be4487be66645604e8302967e54b1"
name = "github.com/kubernetes-sigs/application"
packages = [
"pkg/apis/app/v1beta1",
@@ -650,7 +692,7 @@
revision = "4ead7f1b87048b7717b3e474a21fdc07e6bce636"
[[projects]]
digest = "1:26e91e5a7f6be65b1ed0ae488ef156c35a69e3435fd5c4ccd7b4e9cd847997c5"
digest = "1:f97290db0238bf4d6394b527daa18391a80b5592f3db8d991f2013f70f64bd46"
name = "github.com/kubesphere/s2ioperator"
packages = [
"pkg/apis/devops/v1alpha1",
@@ -667,6 +709,14 @@
revision = "dc48c60d3ba316d8533688b556555567f9c78d51"
version = "v0.0.8"
[[projects]]
digest = "1:a0c81030b65e467a7128668f24707beeca9812a9b1ed38c6d5db5b967da4e3b8"
name = "github.com/kubesphere/sonargo"
packages = ["sonar"]
pruneopts = "NUT"
revision = "313ae64d680ed14ef12e806ab0dd3777240e908d"
version = "v0.0.2"
[[projects]]
branch = "master"
digest = "1:2d137c17dacc803b85c06b7a0cc9e9a1d68e3104e567caf27fea2fb067ef424e"
@@ -676,7 +726,7 @@
revision = "cd47fb39b79f867c6e4e5cd39cf7abd799f71670"
[[projects]]
digest = "1:7e8a4473ed7155b5a3e4b0c692c0407d8d242e878d067d4adb9530b077f6f4e5"
digest = "1:3a12648ef8bab4b7310adf715fe4d74fc1d52eb81f1df6e9fcb168770ffdbd6e"
name = "github.com/lucas-clemente/quic-go"
packages = [
".",
@@ -705,7 +755,7 @@
[[projects]]
branch = "master"
digest = "1:df495c9184b4e6cbb9d55652236dbcbe72c65a1c8b6469da50722628cea474e7"
digest = "1:0b14bf0c424137a702e793fe3570f41320cc3b8495d124e279a4c362c57f214b"
name = "github.com/mailru/easyjson"
packages = [
"buffer",
@@ -732,7 +782,7 @@
version = "v1.0.1"
[[projects]]
digest = "1:1499e521c77de0395b3b4136c9b65712f8cc3e12bcd1c4c397cdd0f73d458bdc"
digest = "1:0a36dc212d1b91a38b435accb11764fe6506c503cc6b75a014b4fd0bd9db44df"
name = "github.com/mholt/caddy"
packages = [
".",
@@ -827,7 +877,7 @@
version = "v0.1.0"
[[projects]]
digest = "1:26b3fcffe42d2ef2076af3a8321c8c39db44ff1f46aac3be20a9b64a46328928"
digest = "1:9cd0478666845c9fb37ce8458c8b455d73c0a74d1e4c7603be298d9e28df83a1"
name = "github.com/naoina/toml"
packages = [
".",
@@ -838,7 +888,7 @@
version = "v0.1.1"
[[projects]]
digest = "1:3c46171ee5eee66086897e1efca67b84bf552b1f80039d421068c90684868194"
digest = "1:a84acfd5bc2d90ec8d9e0c2f2728aa8f2a33512f1247a099be4330ef9b926094"
name = "github.com/onsi/ginkgo"
packages = [
".",
@@ -865,7 +915,7 @@
version = "v1.8.0"
[[projects]]
digest = "1:01797bb0a7bd98751c65dff3461b9f948e3be09c443d179468781720326b09fe"
digest = "1:ad29fb22a545681b5b5d0e07bc9fe20cbdaf3f954ffcad361017f3e34ac6b4ce"
name = "github.com/onsi/gomega"
packages = [
".",
@@ -896,7 +946,7 @@
version = "v1.0.0-rc1"
[[projects]]
digest = "1:cf7a1da29bb5ad81a30d8f13a97de4b45c7c78f1f7a0f5651ce35bf3bf88ff2b"
digest = "1:290add25d7ce226bce0d6880f38c4fbb7129346827a71f37293143cf9dade289"
name = "github.com/openshift/api"
packages = [
"apps/v1",
@@ -955,7 +1005,7 @@
version = "v1.0.0"
[[projects]]
digest = "1:e411054f54a332ecadd5fe5a4a68d092f36583e554037587da86da298cffc72d"
digest = "1:a6b0f01bc76800f17cb666c82e1dde9f219fa35cd6989a351a9f0ef8a5a7cbd6"
name = "github.com/prometheus/client_golang"
packages = [
"api",
@@ -970,14 +1020,14 @@
[[projects]]
branch = "master"
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
digest = "1:0f37e09b3e92aaeda5991581311f8dbf38944b36a3edec61cc2d1991f527554a"
name = "github.com/prometheus/client_model"
packages = ["go"]
pruneopts = "NUT"
revision = "fd36f4220a901265f90734c3183c5f0c91daa0b8"
[[projects]]
digest = "1:4e776079b966091d3e6e12ed2aaf728bea5cd1175ef88bb654e03adbf5d4f5d3"
digest = "1:718e954e70b5f876b6bf756173ddc35603be265423e8d80661b1882cb1bd9a91"
name = "github.com/prometheus/common"
packages = [
"expfmt",
@@ -990,7 +1040,7 @@
[[projects]]
branch = "master"
digest = "1:612e0e6b31740c56ca06dcc7a58ef57379190bcc9eb7f7ec7698ef480fddf1da"
digest = "1:2aa721eb10a93c6216e8b864b42e78c20f787aa5b11010f8cfba5f7a8891f13f"
name = "github.com/prometheus/procfs"
packages = [
".",
@@ -1003,7 +1053,7 @@
revision = "e56f2e22fc761e82a34aca553f6725e2aff4fe6c"
[[projects]]
digest = "1:e09ada96a5a41deda4748b1659cc8953961799e798aea557257b56baee4ecaf3"
digest = "1:9e5d599747d9210d7dee1d10efe147f344f99f9521bb3c6d557ab65f4c2cf4ef"
name = "github.com/rogpeppe/go-internal"
packages = [
"modfile",
@@ -1031,7 +1081,23 @@
version = "v1.0.0"
[[projects]]
digest = "1:90cf76d709ce9b057e7d75bd245bf7c1242d21ba4f908fb22c7a2a96d1dcc0ca"
branch = "master"
digest = "1:7d62418b0a0e438a72444d7bc4bd7f5c066acfbdf20dfa9e75edc64fe8540621"
name = "github.com/sony/sonyflake"
packages = ["."]
pruneopts = "NUT"
revision = "6d5bd61810093eae37d7d605d0cbdd845969b5b2"
[[projects]]
digest = "1:7aa2c655b92b09fc47c7c69b629dcacdca2a7a3bf1837d69d04f96b5c3ff8454"
name = "github.com/speps/go-hashids"
packages = ["."]
pruneopts = "NUT"
revision = "ad304c6ede07ca1f915c30a5510c45860aa165a7"
version = "v2.0.0"
[[projects]]
digest = "1:d0af1fc7035b6d64dfbe4af440d8c70e593ea44e60284d98b74787db004a7452"
name = "github.com/spf13/afero"
packages = [
".",
@@ -1042,7 +1108,7 @@
version = "v1.2.1"
[[projects]]
digest = "1:343d44e06621142ab09ae0c76c1799104cdfddd3ffb445d78b1adf8dc3ffaf3d"
digest = "1:2c3b60fc961b7ddca4336bb7bb39146cb73ea2ad73d4afc6b4ffea05571e712a"
name = "github.com/spf13/cobra"
packages = ["."]
pruneopts = "NUT"
@@ -1058,7 +1124,7 @@
version = "v1.0.3"
[[projects]]
digest = "1:89fd77d603a74a6540d60067debad9397865bf040955d907362c95d364baeba6"
digest = "1:8c262fc0329de35cbb1f1208ea1990446e0ea265171aafd523189ab9c587433b"
name = "github.com/src-d/gcfg"
packages = [
".",
@@ -1079,7 +1145,7 @@
version = "v0.1.1"
[[projects]]
digest = "1:4fec2fe9ce617252d634de83ca8dd5f9c7c391637540896542c80ab63f071f51"
digest = "1:5f1187480cbc19150e63c774ed8007e7e64c1cb919a5e9ff7b0a0e5e1900775e"
name = "github.com/stretchr/testify"
packages = [
"assert",
@@ -1098,7 +1164,7 @@
version = "v0.2.1"
[[projects]]
digest = "1:4ec126f1327fe6b348ccf2d4974c38ccf2628b10ad30be7b3be53f483db85149"
digest = "1:6ae48b637ad9c325f1eeb116f1780cc310fe93d524bc8a2eb78a350f21a35dff"
name = "github.com/xenolf/lego"
packages = [
"acme",
@@ -1139,7 +1205,7 @@
version = "v1.1.0"
[[projects]]
digest = "1:85674ac609b704fd4e9f463553b6ffc3a3527a993ae0ba550eb56beaabdfe094"
digest = "1:244145848a183fe658a95c682d379142595c79733d854a7d4076fa72fb1b9eb8"
name = "go.uber.org/zap"
packages = [
".",
@@ -1155,7 +1221,7 @@
[[projects]]
branch = "master"
digest = "1:a364cbf471c6403d39967b18ce1c11fb0d1e9a05a4aa66d5656cd45383f95ebe"
digest = "1:5cbcf748cfe80e3837b99eb98c37498c0eb4183b35c28659696731ead7a4dd5f"
name = "golang.org/x/crypto"
packages = [
"cast5",
@@ -1184,7 +1250,7 @@
[[projects]]
branch = "master"
digest = "1:3b0a30883b4833e6d256fac81b74cb287e97d8fb2ddc4e60974a990d5632401a"
digest = "1:4ef584461a8ab7d5e5883daca745b525450fe886a6839e583fbe24b65af527f1"
name = "golang.org/x/net"
packages = [
"bpf",
@@ -1209,7 +1275,7 @@
[[projects]]
branch = "master"
digest = "1:17ee74a4d9b6078611784b873cdbfe91892d2c73052c430724e66fcc015b6c7b"
digest = "1:cd20b2392f53dd5a6b3aff0ac551d02fae60274c673bf2b885e9a3ddadff5c4f"
name = "golang.org/x/oauth2"
packages = [
".",
@@ -1223,7 +1289,7 @@
[[projects]]
branch = "master"
digest = "1:a64cdf903b3f6c7496d32d8eee29604facb0e20fc022e3e69d6950d0917cafa5"
digest = "1:a243573e9643276548ac3219e2243ccb7b3abce72d3fe442170a0dbf64a997a2"
name = "golang.org/x/sys"
packages = [
"cpu",
@@ -1234,7 +1300,7 @@
revision = "fead79001313d15903fb4605b4a1b781532cd93e"
[[projects]]
digest = "1:2c4e3b27329379d7b5f889038b48086140ea60708fb92e4b19d6a775926ea30b"
digest = "1:0ebfea43f83a5127e01acb49438dffeb32416312c3b73fa87c7fa6e39f7e03bc"
name = "golang.org/x/text"
packages = [
"collate",
@@ -1279,7 +1345,7 @@
[[projects]]
branch = "master"
digest = "1:e46d8e20161401a9cf8765dfa428494a3492a0b56fe114156b7da792bf41ba78"
digest = "1:8f1c06104804a7f5dda99effae51188e8d7bf28900a4e8ef3e4b198cc3406f7e"
name = "golang.org/x/tools"
packages = [
"go/ast/astutil",
@@ -1299,7 +1365,7 @@
revision = "8b67d361bba210f5fbb3c1a0fc121e0847b10b57"
[[projects]]
digest = "1:55c735e000d1eef22b3c2f3226447489bafa33f0159dfece150279d2f9e25f7c"
digest = "1:faa5e95fa65f513292bea81c0baa5b8697e3a4c04b77a968327d9b73bfa9e8e0"
name = "google.golang.org/appengine"
packages = [
".",
@@ -1360,7 +1426,7 @@
version = "v2.1"
[[projects]]
digest = "1:d5547d77e1c9ca9850f3d868d29eed275742611eeae2b99bcd8a1f18f368b6e8"
digest = "1:1d8471ac1b112254af183f50adb0f41309cb1f83926befb09b7238357e90041c"
name = "gopkg.in/square/go-jose.v2"
packages = [
".",
@@ -1373,7 +1439,7 @@
version = "v2.3.0"
[[projects]]
digest = "1:1cf1388ec8c73b7ecc711d9f279ab631ea0a6964d1ccc32809a6be90c33fa2a0"
digest = "1:3a27ea3585e1517381a8a9f26dbbc1b02f646ffcf1f58db4c4713c3ad7ae7117"
name = "gopkg.in/src-d/go-billy.v4"
packages = [
".",
@@ -1387,7 +1453,7 @@
version = "v4.3.0"
[[projects]]
digest = "1:bcee186946d33b2e3befed808ee6ffe73387345807187e51790e3e148b5344c2"
digest = "1:388a60e173d00bf4256826ac6773a55ba384033e06b6593a89e4e4e402d208ec"
name = "gopkg.in/src-d/go-git.v4"
packages = [
".",
@@ -1461,7 +1527,7 @@
version = "v2.2.2"
[[projects]]
digest = "1:0ae5137dc94a40cd72fe4fcdf9db0c119d2d63df04af5958cfd514a2a21b3988"
digest = "1:54a81502271a4e19f45dabc7ac58b6ba06513c0fff6f1ce9c555bde0e5061113"
name = "k8s.io/api"
packages = [
"admission/v1beta1",
@@ -1503,7 +1569,7 @@
version = "kubernetes-1.13.1"
[[projects]]
digest = "1:09949d3a2ceff9874863e6dd454f553696fdfac4a8d35b931cc7fd8556f7b5ca"
digest = "1:62a3fc47167844f281a867f4a23a0d11aa3b944005d72271f2f3f560f98a28ca"
name = "k8s.io/apiextensions-apiserver"
packages = [
"pkg/apis/apiextensions",
@@ -1518,7 +1584,7 @@
version = "kubernetes-1.13.1"
[[projects]]
digest = "1:0278d2edffad91a4feb86b4255a57c7b13eeddc2fc892c8053436f90cfa6e9bd"
digest = "1:7040b3db74b73ed260ed93b5cd74c40d5476887f97f8b916014e2949410b3128"
name = "k8s.io/apimachinery"
packages = [
"pkg/api/equality",
@@ -1579,7 +1645,7 @@
version = "kubernetes-1.13.1"
[[projects]]
digest = "1:60980a58a47ddc5616bef02dbe42855e63a718fb6de108cd8fc108afa7d0af84"
digest = "1:6df68d1649c3ddb4d47c6ad78f260582d5e55a4a9653c6e30fbd35822fff0699"
name = "k8s.io/apiserver"
packages = [
"pkg/apis/audit",
@@ -1596,7 +1662,7 @@
version = "kubernetes-1.13.1"
[[projects]]
digest = "1:174302b5262613baa367ba2fca9f07870b24b41b6ab9e0cdf22798febfa7ee80"
digest = "1:356ad4c66929ad8d8abcf058b68a2c05a55912a53dfad819bc2b6d141a2d618e"
name = "k8s.io/client-go"
packages = [
"discovery",
@@ -1753,7 +1819,7 @@
version = "kubernetes-1.13.1"
[[projects]]
digest = "1:dc1ae99dcab96913d81ae970b1f7a7411a54199b14bfb17a7e86f9a56979c720"
digest = "1:8f29fd4c578cae70e2e1673656e19e9bae4896c72b458f42b824dfe03d7ea3b2"
name = "k8s.io/code-generator"
packages = [
"cmd/client-gen",
@@ -1772,7 +1838,7 @@
[[projects]]
branch = "master"
digest = "1:462e6076bc1fde8da364901873fde3a9e66160d853d766b797a657d66e4b2cc4"
digest = "1:8279bfdd72fb8eb68d03b5a43acbff4109a79725a2717d98e90f15915eefdbd0"
name = "k8s.io/gengo"
packages = [
"args",
@@ -1788,12 +1854,12 @@
revision = "b90029ef6cd877cb3f422d75b3a07707e3aac6b7"
[[projects]]
digest = "1:c263611800c3a97991dbcf9d3bc4de390f6224aaa8ca0a7226a9d734f65a416a"
digest = "1:9cc257b3c9ff6a0158c9c661ab6eebda1fe8a4a4453cd5c4044dc9a2ebfb992b"
name = "k8s.io/klog"
packages = ["."]
pruneopts = "NUT"
revision = "71442cd4037d612096940ceb0f3fec3f7fff66e0"
version = "v0.2.0"
revision = "a5bc97fbc634d635061f3146511332c7e313a55a"
version = "v0.1.0"
[[projects]]
branch = "master"
@@ -1804,7 +1870,7 @@
revision = "15615b16d372105f0c69ff47dfe7402926a65aaa"
[[projects]]
digest = "1:71c5cb5b157c506a5e0e94d186a03cddc007bae59c9ad5f460400e487aa28696"
digest = "1:ccabade367cf9af9bd03efe88d4c091b33e227bc4fb256ceff1b5e15211d4cea"
name = "k8s.io/kubernetes"
packages = [
"pkg/api/legacyscheme",
@@ -1834,7 +1900,6 @@
"pkg/util/metrics",
"pkg/util/net/sets",
"pkg/util/parsers",
"pkg/util/slice",
"pkg/util/taints",
]
pruneopts = "NUT"
@@ -1843,7 +1908,7 @@
[[projects]]
branch = "master"
digest = "1:5b4cb4a89696f012fc162d3fcb16aecfecf979ce794cf29c44822c9aa9f4319e"
digest = "1:68a6a10a10ee5f93fc5e0c63c8857165b472a0c45b6e482342d8d25b17837bd5"
name = "k8s.io/utils"
packages = ["pointer"]
pruneopts = "NUT"
@@ -1858,7 +1923,7 @@
revision = "4ead7f1b87048b7717b3e474a21fdc07e6bce636"
[[projects]]
digest = "1:2615e1a9c8353287333b45fd7334173bc6c8dac525ae5710dc2e3c80b7b3e6ba"
digest = "1:41e0826a5968b00d321ba7981be205017ca2bea433a8f85c01e2b3f4c62445eb"
name = "sigs.k8s.io/controller-runtime"
packages = [
"pkg/cache",
@@ -1903,7 +1968,7 @@
version = "v0.1.10"
[[projects]]
digest = "1:992675a6714d511089a0b7ffb7063d36e5423089cda610642de7a0cfbbf673ab"
digest = "1:170bb3fef81543de085fe9bcdfc97c8677563151b5badba8f6e8ebeabb73a310"
name = "sigs.k8s.io/controller-tools"
packages = [
"cmd/controller-gen",
@@ -1922,7 +1987,7 @@
version = "v0.1.9"
[[projects]]
digest = "1:237ad09ac561f3e3a2a2e9f3fee85fea98c0f41f381ac80562b05bdaa9fe89c6"
digest = "1:a9ab998a89bcd0ee9a5cf293943b79e2f889aa6a587b4f2ca3de113340e20418"
name = "sigs.k8s.io/testing_frameworks"
packages = [
"integration",
@@ -1945,15 +2010,20 @@
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/asaskevich/govalidator",
"github.com/dgrijalva/jwt-go",
"github.com/docker/docker/api/types",
"github.com/docker/docker/client",
"github.com/emicklei/go-restful",
"github.com/emicklei/go-restful-openapi",
"github.com/fatih/structs",
"github.com/go-ldap/ldap",
"github.com/go-openapi/spec",
"github.com/go-redis/redis",
"github.com/go-sql-driver/mysql",
"github.com/gocraft/dbr",
"github.com/gocraft/dbr/dialect",
"github.com/golang/example/stringutil",
"github.com/golang/glog",
"github.com/google/uuid",
"github.com/jinzhu/gorm",
@@ -1969,14 +2039,19 @@
"github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1",
"github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned",
"github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions",
"github.com/kubesphere/sonargo/sonar",
"github.com/mholt/caddy",
"github.com/mholt/caddy/caddy/caddymain",
"github.com/mholt/caddy/caddyhttp/httpserver",
"github.com/onsi/ginkgo",
"github.com/onsi/gomega",
"github.com/sony/sonyflake",
"github.com/speps/go-hashids",
"github.com/spf13/cobra",
"github.com/spf13/pflag",
"github.com/stretchr/testify/assert",
"golang.org/x/net/context",
"golang.org/x/net/html",
"gopkg.in/igm/sockjs-go.v2/sockjs",
"gopkg.in/src-d/go-git.v4",
"gopkg.in/src-d/go-git.v4/config",
@@ -2004,6 +2079,7 @@
"k8s.io/apimachinery/pkg/runtime/serializer",
"k8s.io/apimachinery/pkg/types",
"k8s.io/apimachinery/pkg/util/json",
"k8s.io/apimachinery/pkg/util/net",
"k8s.io/apimachinery/pkg/util/runtime",
"k8s.io/apimachinery/pkg/util/sets",
"k8s.io/apimachinery/pkg/util/wait",
@@ -2037,7 +2113,6 @@
"k8s.io/kubernetes/pkg/apis/core",
"k8s.io/kubernetes/pkg/controller",
"k8s.io/kubernetes/pkg/util/metrics",
"k8s.io/kubernetes/pkg/util/slice",
"sigs.k8s.io/application/pkg/controller/application",
"sigs.k8s.io/controller-runtime/pkg/client",
"sigs.k8s.io/controller-runtime/pkg/client/config",

View File

@@ -121,3 +121,11 @@ required = [
[[constraint]]
name = "gopkg.in/igm/sockjs-go.v2"
version = "2.0.0"
[[constraint]]
name = "github.com/gocraft/dbr"
revision = "a0fd650918f6287ffe111d1c7b66bb755ff3be4a"
[[constraint]]
name = "github.com/kubesphere/sonargo"
version = "0.0.2"

View File

@@ -30,10 +30,10 @@ import (
"kubesphere.io/kubesphere/pkg/apiserver/servicemesh/tracing"
"kubesphere.io/kubesphere/pkg/filter"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
logging "kubesphere.io/kubesphere/pkg/models/log"
"kubesphere.io/kubesphere/pkg/signals"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"kubesphere.io/kubesphere/pkg/simple/client/admin_jenkins"
"kubesphere.io/kubesphere/pkg/simple/client/devops_mysql"
"log"
"net/http"
)
@@ -79,13 +79,10 @@ func Run(s *options.ServerRunOptions) error {
}
}
initializeAdminJenkins()
initializeDevOpsDatabase()
initializeESClientConfig()
initializeServicemeshConfig(s)
err = initializeDatabase()
if err != nil {
return err
}
if s.GenericServerRunOptions.InsecurePort != 0 {
log.Printf("Server listening on %d.", s.GenericServerRunOptions.InsecurePort)
@@ -100,14 +97,12 @@ func Run(s *options.ServerRunOptions) error {
return err
}
func initializeDatabase() error {
db := mysql.Client()
if !db.HasTable(&models.WorkspaceDPBinding{}) {
if err := db.CreateTable(&models.WorkspaceDPBinding{}).Error; err != nil {
return err
}
}
return nil
func initializeAdminJenkins() {
admin_jenkins.Client()
}
func initializeDevOpsDatabase() {
devops_mysql.OpenDatabase()
}
func initializeServicemeshConfig(s *options.ServerRunOptions) {

View File

@@ -5,3 +5,5 @@
docker build -f build/ks-iam/Dockerfile -t kubespheredev/ks-account:latest .
docker build -f build/controller-manager/Dockerfile -t kubespheredev/ks-controller-manager:latest .
docker build -f ./pkg/db/Dockerfile -t kubespheredev/ks-devops:flyway ./pkg/db/

View File

@@ -7,3 +7,4 @@ docker push kubespheredev/ks-apigateway:latest
docker push kubespheredev/ks-apiserver:latest
docker push kubespheredev/ks-account:latest
docker push kubespheredev/ks-controller-manager:latest
docker push kubespheredev/ks-devops:flyway

View File

@@ -28,15 +28,15 @@ import (
"kubesphere.io/kubesphere/pkg/apiserver/logging"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/metrics"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/models/tenant"
"kubesphere.io/kubesphere/pkg/models/workspaces"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/elasticsearch"
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"net/http"
"strings"
@@ -242,17 +242,10 @@ func DeleteDevopsProject(req *restful.Request, resp *restful.Response) {
return
}
err = kubesphere.Client().DeleteDevopsProject(username, devops)
err, code := tenant.DeleteDevOpsProject(devops, username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
err = workspaces.UnBindDevopsProject(workspaceName, devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
resp.WriteHeaderAndEntity(code, errors.Wrap(err))
return
}
@@ -264,7 +257,7 @@ func CreateDevopsProject(req *restful.Request, resp *restful.Response) {
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
var devops models.DevopsProject
var devops devops.DevOpsProject
err := req.ReadEntity(&devops)
@@ -274,10 +267,10 @@ func CreateDevopsProject(req *restful.Request, resp *restful.Response) {
}
glog.Infoln("create workspace", username, workspaceName, devops)
project, err := workspaces.CreateDevopsProject(username, workspaceName, &devops)
project, err, code := tenant.CreateDevopsProject(username, workspaceName, &devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
resp.WriteHeaderAndEntity(code, errors.Wrap(err))
return
}

11
pkg/db/Dockerfile Normal file
View File

@@ -0,0 +1,11 @@
# Copyright 2017 The OpenPitrix Authors. All rights reserved.
# Use of this source code is governed by a Apache license
# that can be found in the LICENSE file.
FROM dhoer/flyway:5.1.4-mysql-8.0.11-alpine
RUN apk add --no-cache mysql-client
COPY ./schema /flyway/sql
COPY ./ddl /flyway/sql/ddl
COPY ./scripts /flyway/sql/ddl

109
pkg/db/condition.go Normal file
View File

@@ -0,0 +1,109 @@
/*
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 db
import (
"strings"
"github.com/gocraft/dbr"
)
const (
placeholder = "?"
)
type EqCondition struct {
dbr.Builder
Column string
Value interface{}
}
// Copy From vendor/github.com/gocraft/dbr/condition.go:36
func buildCmp(d dbr.Dialect, buf dbr.Buffer, pred string, column string, value interface{}) error {
buf.WriteString(d.QuoteIdent(column))
buf.WriteString(" ")
buf.WriteString(pred)
buf.WriteString(" ")
buf.WriteString(placeholder)
buf.WriteValue(value)
return nil
}
// And creates AND from a list of conditions
func And(cond ...dbr.Builder) dbr.Builder {
return dbr.And(cond...)
}
// Or creates OR from a list of conditions
func Or(cond ...dbr.Builder) dbr.Builder {
return dbr.Or(cond...)
}
func escape(str string) string {
return strings.Map(func(r rune) rune {
switch r {
case '%', '\'', '^', '[', ']', '!', '_':
return ' '
}
return r
}, str)
}
func Like(column string, value string) dbr.Builder {
value = "%" + strings.TrimSpace(escape(value)) + "%"
return dbr.BuildFunc(func(d dbr.Dialect, buf dbr.Buffer) error {
return buildCmp(d, buf, "LIKE", column, value)
})
}
// Eq is `=`.
// When value is nil, it will be translated to `IS NULL`.
// When value is a slice, it will be translated to `IN`.
// Otherwise it will be translated to `=`.
func Eq(column string, value interface{}) dbr.Builder {
return &EqCondition{
Builder: dbr.Eq(column, value),
Column: column,
Value: value,
}
}
// Neq is `!=`.
// When value is nil, it will be translated to `IS NOT NULL`.
// When value is a slice, it will be translated to `NOT IN`.
// Otherwise it will be translated to `!=`.
func Neq(column string, value interface{}) dbr.Builder {
return dbr.Neq(column, value)
}
// Gt is `>`.
func Gt(column string, value interface{}) dbr.Builder {
return dbr.Gt(column, value)
}
// Gte is '>='.
func Gte(column string, value interface{}) dbr.Builder {
return dbr.Gte(column, value)
}
// Lt is '<'.
func Lt(column string, value interface{}) dbr.Builder {
return dbr.Lt(column, value)
}
// Lte is `<=`.
func Lte(column string, value interface{}) dbr.Builder {
return dbr.Lte(column, value)
}

88
pkg/db/condition_test.go Normal file
View File

@@ -0,0 +1,88 @@
/*
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 db
import (
"testing"
"github.com/gocraft/dbr"
"github.com/gocraft/dbr/dialect"
"github.com/stretchr/testify/assert"
)
// Ref: https://github.com/gocraft/dbr/blob/5d59a8b3aa915660960329efb3af5513e7a0db07/condition_test.go
func TestCondition(t *testing.T) {
for _, test := range []struct {
cond dbr.Builder
query string
value []interface{}
}{
{
cond: Eq("col", 1),
query: "`col` = ?",
value: []interface{}{1},
},
{
cond: Eq("col", nil),
query: "`col` IS NULL",
value: nil,
},
{
cond: Eq("col", []int{}),
query: "0",
value: nil,
},
{
cond: Neq("col", 1),
query: "`col` != ?",
value: []interface{}{1},
},
{
cond: Neq("col", nil),
query: "`col` IS NOT NULL",
value: nil,
},
{
cond: Gt("col", 1),
query: "`col` > ?",
value: []interface{}{1},
},
{
cond: Gte("col", 1),
query: "`col` >= ?",
value: []interface{}{1},
},
{
cond: Lt("col", 1),
query: "`col` < ?",
value: []interface{}{1},
},
{
cond: Lte("col", 1),
query: "`col` <= ?",
value: []interface{}{1},
},
{
cond: And(Lt("a", 1), Or(Gt("b", 2), Neq("c", 3))),
query: "(`a` < ?) AND ((`b` > ?) OR (`c` != ?))",
value: []interface{}{1, 2, 3},
},
} {
buf := dbr.NewBuffer()
err := test.cond.Build(dialect.MySQL, buf)
assert.NoError(t, err)
assert.Equal(t, test.query, buf.String())
assert.Equal(t, test.value, buf.Value())
}
}

283
pkg/db/db.go Normal file
View File

@@ -0,0 +1,283 @@
/*
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 db
import (
"database/sql"
"fmt"
"strings"
_ "github.com/go-sql-driver/mysql"
"github.com/gocraft/dbr"
)
const (
DefaultSelectLimit = 200
)
func GetLimit(n uint64) uint64 {
if n < 0 {
n = 0
}
if n > DefaultSelectLimit {
n = DefaultSelectLimit
}
return n
}
func GetOffset(n uint64) uint64 {
if n < 0 {
n = 0
}
return n
}
type InsertHook func(query *InsertQuery)
type UpdateHook func(query *UpdateQuery)
type DeleteHook func(query *DeleteQuery)
type Database struct {
*dbr.Session
InsertHook InsertHook
UpdateHook UpdateHook
DeleteHook DeleteHook
}
type SelectQuery struct {
*dbr.SelectBuilder
JoinCount int // for join filter
}
type InsertQuery struct {
*dbr.InsertBuilder
Hook InsertHook
}
type DeleteQuery struct {
*dbr.DeleteBuilder
Hook DeleteHook
}
type UpdateQuery struct {
*dbr.UpdateBuilder
Hook UpdateHook
}
type UpsertQuery struct {
table string
*dbr.Session
whereConds map[string]string
upsertValues map[string]interface{}
}
// SelectQuery
// Example: Select().From().Where().Limit().Offset().OrderDir().Load()
// Select().From().Where().Limit().Offset().OrderDir().LoadOne()
// Select().From().Where().Count()
// SelectAll().From().Where().Limit().Offset().OrderDir().Load()
// SelectAll().From().Where().Limit().Offset().OrderDir().LoadOne()
// SelectAll().From().Where().Count()
func (db *Database) Select(columns ...string) *SelectQuery {
return &SelectQuery{db.Session.Select(columns...), 0}
}
func (db *Database) SelectBySql(query string, value ...interface{}) *SelectQuery {
return &SelectQuery{db.Session.SelectBySql(query, value...), 0}
}
func (db *Database) SelectAll(columns ...string) *SelectQuery {
return &SelectQuery{db.Session.Select("*"), 0}
}
func (b *SelectQuery) Join(table, on interface{}) *SelectQuery {
b.SelectBuilder.Join(table, on)
return b
}
func (b *SelectQuery) JoinAs(table string, alias string, on interface{}) *SelectQuery {
b.SelectBuilder.Join(dbr.I(table).As(alias), on)
return b
}
func (b *SelectQuery) From(table string) *SelectQuery {
b.SelectBuilder.From(table)
return b
}
func (b *SelectQuery) Where(query interface{}, value ...interface{}) *SelectQuery {
b.SelectBuilder.Where(query, value...)
return b
}
func (b *SelectQuery) GroupBy(col ...string) *SelectQuery {
b.SelectBuilder.GroupBy(col...)
return b
}
func (b *SelectQuery) Distinct() *SelectQuery {
b.SelectBuilder.Distinct()
return b
}
func (b *SelectQuery) Limit(n uint64) *SelectQuery {
n = GetLimit(n)
b.SelectBuilder.Limit(n)
return b
}
func (b *SelectQuery) Offset(n uint64) *SelectQuery {
n = GetLimit(n)
b.SelectBuilder.Offset(n)
return b
}
func (b *SelectQuery) OrderDir(col string, isAsc bool) *SelectQuery {
b.SelectBuilder.OrderDir(col, isAsc)
return b
}
func (b *SelectQuery) Load(value interface{}) (int, error) {
return b.SelectBuilder.Load(value)
}
func (b *SelectQuery) LoadOne(value interface{}) error {
return b.SelectBuilder.LoadOne(value)
}
func getColumns(dbrColumns []interface{}) string {
var columns []string
for _, column := range dbrColumns {
if c, ok := column.(string); ok {
columns = append(columns, c)
}
}
return strings.Join(columns, ", ")
}
func (b *SelectQuery) Count() (count uint32, err error) {
// cache SelectStmt
selectStmt := b.SelectStmt
limit := selectStmt.LimitCount
offset := selectStmt.OffsetCount
column := selectStmt.Column
isDistinct := selectStmt.IsDistinct
order := selectStmt.Order
b.SelectStmt.LimitCount = -1
b.SelectStmt.OffsetCount = -1
b.SelectStmt.Column = []interface{}{"COUNT(*)"}
b.SelectStmt.Order = []dbr.Builder{}
if isDistinct {
b.SelectStmt.Column = []interface{}{fmt.Sprintf("COUNT(DISTINCT %s)", getColumns(column))}
b.SelectStmt.IsDistinct = false
}
err = b.LoadOne(&count)
// fallback SelectStmt
selectStmt.LimitCount = limit
selectStmt.OffsetCount = offset
selectStmt.Column = column
selectStmt.IsDistinct = isDistinct
selectStmt.Order = order
b.SelectStmt = selectStmt
return
}
// InsertQuery
// Example: InsertInto().Columns().Record().Exec()
func (db *Database) InsertInto(table string) *InsertQuery {
return &InsertQuery{db.Session.InsertInto(table), db.InsertHook}
}
func (b *InsertQuery) Exec() (sql.Result, error) {
result, err := b.InsertBuilder.Exec()
if b.Hook != nil && err == nil {
defer b.Hook(b)
}
return result, err
}
func (b *InsertQuery) Columns(columns ...string) *InsertQuery {
b.InsertBuilder.Columns(columns...)
return b
}
func (b *InsertQuery) Record(structValue interface{}) *InsertQuery {
b.InsertBuilder.Record(structValue)
return b
}
// DeleteQuery
// Example: DeleteFrom().Where().Limit().Exec()
func (db *Database) DeleteFrom(table string) *DeleteQuery {
return &DeleteQuery{db.Session.DeleteFrom(table), db.DeleteHook}
}
func (b *DeleteQuery) Where(query interface{}, value ...interface{}) *DeleteQuery {
b.DeleteBuilder.Where(query, value...)
return b
}
func (b *DeleteQuery) Limit(n uint64) *DeleteQuery {
b.DeleteBuilder.Limit(n)
return b
}
func (b *DeleteQuery) Exec() (sql.Result, error) {
result, err := b.DeleteBuilder.Exec()
if b.Hook != nil && err == nil {
defer b.Hook(b)
}
return result, err
}
// UpdateQuery
// Example: Update().Set().Where().Exec()
func (db *Database) Update(table string) *UpdateQuery {
return &UpdateQuery{db.Session.Update(table), db.UpdateHook}
}
func (b *UpdateQuery) Exec() (sql.Result, error) {
result, err := b.UpdateBuilder.Exec()
if b.Hook != nil && err == nil {
defer b.Hook(b)
}
return result, err
}
func (b *UpdateQuery) Set(column string, value interface{}) *UpdateQuery {
b.UpdateBuilder.Set(column, value)
return b
}
func (b *UpdateQuery) SetMap(m map[string]interface{}) *UpdateQuery {
b.UpdateBuilder.SetMap(m)
return b
}
func (b *UpdateQuery) Where(query interface{}, value ...interface{}) *UpdateQuery {
b.UpdateBuilder.Where(query, value...)
return b
}
func (b *UpdateQuery) Limit(n uint64) *UpdateQuery {
b.UpdateBuilder.Limit(n)
return b
}

2
pkg/db/ddl/devops.sql Normal file
View File

@@ -0,0 +1,2 @@
CREATE DATABASE IF NOT EXISTS devops DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;
CREATE DATABASE IF NOT EXISTS kubesphere DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;

31
pkg/db/error.go Normal file
View File

@@ -0,0 +1,31 @@
/*
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 db
import (
"github.com/gocraft/dbr"
)
// package errors
var (
ErrNotFound = dbr.ErrNotFound
ErrNotSupported = dbr.ErrNotSupported
ErrTableNotSpecified = dbr.ErrTableNotSpecified
ErrColumnNotSpecified = dbr.ErrColumnNotSpecified
ErrInvalidPointer = dbr.ErrInvalidPointer
ErrPlaceholderCount = dbr.ErrPlaceholderCount
ErrInvalidSliceLength = dbr.ErrInvalidSliceLength
ErrCantConvertToTime = dbr.ErrCantConvertToTime
ErrInvalidTimestring = dbr.ErrInvalidTimestring
)

55
pkg/db/event.go Normal file
View File

@@ -0,0 +1,55 @@
/*
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 db
import (
"github.com/golang/glog"
)
// EventReceiver is a sentinel EventReceiver; use it if the caller doesn't supply one
type EventReceiver struct{}
// Event receives a simple notification when various events occur
func (n *EventReceiver) Event(eventName string) {
}
// EventKv receives a notification when various events occur along with
// optional key/value data
func (n *EventReceiver) EventKv(eventName string, kvs map[string]string) {
}
// EventErr receives a notification of an error if one occurs
func (n *EventReceiver) EventErr(eventName string, err error) error {
return err
}
// EventErrKv receives a notification of an error if one occurs along with
// optional key/value data
func (n *EventReceiver) EventErrKv(eventName string, err error, kvs map[string]string) error {
glog.Errorf("%+v", err)
glog.Errorf("%s: %+v", eventName, kvs)
return err
}
// Timing receives the time an event took to happen
func (n *EventReceiver) Timing(eventName string, nanoseconds int64) {
}
// TimingKv receives the time an event took to happen along with optional key/value data
func (n *EventReceiver) TimingKv(eventName string, nanoseconds int64, kvs map[string]string) {
// TODO: Change logger level to debug
glog.Infof("%s spend %.2fms: %+v", eventName, float32(nanoseconds)/1000000, kvs)
}

View File

@@ -0,0 +1,23 @@
CREATE TABLE project (
`project_id` VARCHAR(50) NOT NULL,
`name` VARCHAR(50) NOT NULL,
`description` TEXT NOT NULL,
`creator` VARCHAR(50) NOT NULL,
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`status` VARCHAR(50) NOT NULL,
`visibility` VARCHAR(50) NOT NULL,
`extra` TEXT NOT NULL,
PRIMARY KEY (`project_id`)
);
CREATE TABLE `project_membership` (
`username` VARCHAR(50) NOT NULL,
`project_id` VARCHAR(50) NOT NULL,
`role` VARCHAR(50) NOT NULL,
`status` VARCHAR(50) NOT NULL,
`grant_by` VARCHAR(50) NOT NULL,
PRIMARY KEY (`username`, `project_id`)
);

View File

@@ -0,0 +1,8 @@
CREATE TABLE `project_credential` (
`project_id` VARCHAR(50) NOT NULL,
`credential_id` VARCHAR(255) NOT NULL,
`domain` VARCHAR(255) NOT NULL,
`creator` VARCHAR(50) NOT NULL,
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`project_id`, `credential_id`, `domain`)
);

View File

@@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS `kubesphere.workspace_dp_bindings` (
`workspace` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`dev_ops_project` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`workspace`,`dev_ops_project`)
) ENGINE=InnoDB
ALTER TABLE kubesphere.workspace_dp_bindings
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE devops.project
ADD COLUMN workspace VARCHAR(255) NOT NULL DEFAULT '';
UPDATE devops.project t1
INNER JOIN kubesphere.workspace_dp_bindings t2 ON t1.project_id= t2.dev_ops_project
SET t1.workspace=t2.workspace;

17
pkg/db/scripts/ddl_init.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/sh
cd /flyway/sql/ddl
[ -n "$PASSWORD" ] && OPT="-p$(echo "$PASSWORD" | tr -d '\n')"
for F in $(ls *.sql)
do
echo "Start process $F"
mysql "$@" "$OPT" < "$F"
if [ $? -ne 0 ]; then
echo "Process $F failed"
return 1
else
echo "Process $F successful"
fi
done

24
pkg/gojenkins/README.md Normal file
View File

@@ -0,0 +1,24 @@
# Jenkins API Client for Go
## About
Jenkins is the most popular Open Source Continuous Integration system. This Library will help you interact with Jenkins in a more developer-friendly way.
Fork From https://github.com/bndr/gojenkins
These are some of the features that are currently implemented:
* Get information on test-results of completed/failed build
* Ability to query Nodes, and manipulate them. Start, Stop, set Offline.
* Ability to query Jobs, and manipulate them.
* Get Plugins, Builds, Artifacts, Fingerprints
* Validate Fingerprints of Artifacts
* Get Current Queue, Cancel Tasks
* etc. For all methods go to GoDoc Reference.
Add some features:
* Credentials Management
* Pipeline Model Converter
* RBAC control

View File

@@ -0,0 +1,4 @@
<table page-has-up="false" page-has-down="false" page-entry-newest="-9223372036854775805" page-entry-oldest="-9223372036854775807" class="pane hasPageData"><tr page-entry-id="-9223372036854775805" class="build-row single-line"><td class="build-row-cell"><div class="pane build-name"><div class="build-icon"><a href="/jenkins/job/j1/3/console" class="build-status-link"><img src="/jenkins/images/16x16/blue.png" alt="Success &gt; Console Output" tooltip="Success &gt; Console Output" style="width: 16px; height: 16px; " class="icon-blue icon-sm" /></a></div><a update-parent-class=".build-row" href="/jenkins/job/j1/3/" class="tip model-link inside build-link display-name">#3</a></div><div time="1484327939346" class="pane build-details"><a update-parent-class=".build-row" href="/jenkins/job/j1/3/" class="tip model-link inside build-link">Jan 13, 2017 9:18 AM</a></div><div class="pane build-controls"><div class="middle-align build-badge"></div></div><div class="left-bar"></div></td></tr><tr page-entry-id="-9223372036854775806" class="build-row single-line"><td class="build-row-cell"><div class="pane build-name"><div class="build-icon"><a href="/jenkins/job/j1/2/console" class="build-status-link"><img src="/jenkins/images/16x16/blue.png" alt="Success &gt; Console Output" tooltip="Success &gt; Console Output" style="width: 16px; height: 16px; " class="icon-blue icon-sm" /></a></div><a update-parent-class=".build-row" href="/jenkins/job/j1/2/" class="tip model-link inside build-link display-name">#2</a></div><div time="1484327935341" class="pane build-details"><a update-parent-class=".build-row" href="/jenkins/job/j1/2/" class="tip model-link inside build-link">Jan 13, 2017 9:18 AM</a></div><div class="pane build-controls"><div class="middle-align build-badge"></div></div><div class="left-bar"></div></td></tr><tr page-entry-id="-9223372036854775807" class="build-row single-line"><td class="build-row-cell"><div class="pane build-name"><div class="build-icon"><a href="/jenkins/job/j1/1/console" class="build-status-link"><img src="/jenkins/images/16x16/blue.png" alt="Success &gt; Console Output" tooltip="Success &gt; Console Output" style="width: 16px; height: 16px; " class="icon-blue icon-sm" /></a></div><a update-parent-class=".build-row" href="/jenkins/job/j1/1/" class="tip model-link inside build-link display-name">#1</a></div><div time="1484327924442" class="pane build-details"><a update-parent-class=".build-row" href="/jenkins/job/j1/1/" class="tip model-link inside build-link">Jan 13, 2017 9:18 AM</a></div><div class="pane build-controls"><div class="middle-align build-badge"></div></div><div class="left-bar"></div></td></tr></table>

View File

@@ -0,0 +1,27 @@
<?xml version='1.0' encoding='UTF-8'?>
<project>
<actions/>
<description>Some Job Description</description>
<keepDependencies>false</keepDependencies>
<properties>
<hudson.model.ParametersDefinitionProperty>
<parameterDefinitions>
<hudson.model.StringParameterDefinition>
<name>params1</name>
<description>description</description>
<defaultValue>defaultVal</defaultValue>
</hudson.model.StringParameterDefinition>
</parameterDefinitions>
</hudson.model.ParametersDefinitionProperty>
</properties>
<scm class="hudson.scm.NullSCM"/>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers class="vector"/>
<concurrentBuild>false</concurrentBuild>
<builders/>
<publishers/>
<buildWrappers/>
</project>

118
pkg/gojenkins/artifact.go Normal file
View File

@@ -0,0 +1,118 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
import (
"crypto/md5"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
)
// Represents an Artifact
type Artifact struct {
Jenkins *Jenkins
Build *Build
FileName string
Path string
}
// Get raw byte data of Artifact
func (a Artifact) GetData() ([]byte, error) {
var data string
response, err := a.Jenkins.Requester.Get(a.Path, &data, nil)
if err != nil {
return nil, err
}
code := response.StatusCode
if code != 200 {
Error.Printf("Jenkins responded with StatusCode: %d", code)
return nil, errors.New("Could not get File Contents")
}
return []byte(data), nil
}
// Save artifact to a specific path, using your own filename.
func (a Artifact) Save(path string) (bool, error) {
data, err := a.GetData()
if err != nil {
return false, errors.New("No Data received, not saving file.")
}
if _, err = os.Stat(path); err == nil {
Warning.Println("Local Copy already exists, Overwriting...")
}
err = ioutil.WriteFile(path, data, 0644)
a.validateDownload(path)
if err != nil {
return false, err
}
return true, nil
}
// Save Artifact to directory using Artifact filename.
func (a Artifact) SaveToDir(dir string) (bool, error) {
if _, err := os.Stat(dir); err != nil {
Error.Printf("can't save artifact: directory %s does not exist", dir)
return false, fmt.Errorf("can't save artifact: directory %s does not exist", dir)
}
saved, err := a.Save(path.Join(dir, a.FileName))
if err != nil {
return saved, nil
}
return saved, nil
}
// Compare Remote and local MD5
func (a Artifact) validateDownload(path string) (bool, error) {
localHash := a.getMD5local(path)
fp := FingerPrint{Jenkins: a.Jenkins, Base: "/fingerprint/", Id: localHash, Raw: new(FingerPrintResponse)}
valid, err := fp.ValidateForBuild(a.FileName, a.Build)
if err != nil {
return false, err
}
if !valid {
return false, errors.New("FingerPrint of the downloaded artifact could not be verified")
}
return true, nil
}
// Get Local MD5
func (a Artifact) getMD5local(path string) string {
h := md5.New()
localFile, err := os.Open(path)
if err != nil {
return ""
}
buffer := make([]byte, 2^20)
n, err := localFile.Read(buffer)
defer localFile.Close()
for err == nil {
io.WriteString(h, string(buffer[0:n]))
n, err = localFile.Read(buffer)
}
return fmt.Sprintf("%x", h.Sum(nil))
}

480
pkg/gojenkins/build.go Normal file
View File

@@ -0,0 +1,480 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
import (
"bytes"
"errors"
"net/http"
"net/url"
"regexp"
"strconv"
"time"
)
type Build struct {
Raw *BuildResponse
Job *Job
Jenkins *Jenkins
Base string
Depth int
}
type parameter struct {
Name string
Value string
}
type branch struct {
SHA1 string `json:",omitempty"`
Name string `json:",omitempty"`
}
type BuildRevision struct {
SHA1 string `json:"SHA1,omitempty"`
Branch []branch `json:"branch,omitempty"`
}
type Builds struct {
BuildNumber int64 `json:"buildNumber"`
BuildResult interface{} `json:"buildResult"`
Marked BuildRevision `json:"marked"`
Revision BuildRevision `json:"revision"`
}
type Culprit struct {
AbsoluteUrl string
FullName string
}
type GeneralObj struct {
Parameters []parameter `json:"parameters,omitempty"`
Causes []map[string]interface{} `json:"causes,omitempty"`
BuildsByBranchName map[string]Builds `json:"buildsByBranchName,omitempty"`
LastBuiltRevision *BuildRevision `json:"lastBuiltRevision,omitempty"`
RemoteUrls []string `json:"remoteUrls,omitempty"`
ScmName string `json:"scmName,omitempty"`
MercurialNodeName string `json:"mercurialNodeName,omitempty"`
MercurialRevisionNumber string `json:"mercurialRevisionNumber,omitempty"`
Subdir interface{} `json:"subdir,omitempty"`
ClassName string `json:"_class,omitempty"`
SonarTaskId string `json:"ceTaskId,omitempty"`
SonarServerUrl string `json:"serverUrl,omitempty"`
SonarDashboardUrl string `json:"sonarqubeDashboardUrl,omitempty"`
TotalCount int64 `json:",omitempty"`
UrlName string `json:",omitempty"`
}
type TestResult struct {
Duration int64 `json:"duration"`
Empty bool `json:"empty"`
FailCount int64 `json:"failCount"`
PassCount int64 `json:"passCount"`
SkipCount int64 `json:"skipCount"`
Suites []struct {
Cases []struct {
Age int64 `json:"age"`
ClassName string `json:"className"`
Duration int64 `json:"duration"`
ErrorDetails interface{} `json:"errorDetails"`
ErrorStackTrace interface{} `json:"errorStackTrace"`
FailedSince int64 `json:"failedSince"`
Name string `json:"name"`
Skipped bool `json:"skipped"`
SkippedMessage interface{} `json:"skippedMessage"`
Status string `json:"status"`
Stderr interface{} `json:"stderr"`
Stdout interface{} `json:"stdout"`
} `json:"cases"`
Duration int64 `json:"duration"`
ID interface{} `json:"id"`
Name string `json:"name"`
Stderr interface{} `json:"stderr"`
Stdout interface{} `json:"stdout"`
Timestamp interface{} `json:"timestamp"`
} `json:"suites"`
}
type BuildResponse struct {
Actions []GeneralObj
Artifacts []struct {
DisplayPath string `json:"displayPath"`
FileName string `json:"fileName"`
RelativePath string `json:"relativePath"`
} `json:"artifacts"`
Building bool `json:"building"`
BuiltOn string `json:"builtOn"`
ChangeSet struct {
Items []struct {
AffectedPaths []string `json:"affectedPaths"`
Author struct {
AbsoluteUrl string `json:"absoluteUrl"`
FullName string `json:"fullName"`
} `json:"author"`
Comment string `json:"comment"`
CommitID string `json:"commitId"`
Date string `json:"date"`
ID string `json:"id"`
Msg string `json:"msg"`
Paths []struct {
EditType string `json:"editType"`
File string `json:"file"`
} `json:"paths"`
Timestamp int64 `json:"timestamp"`
} `json:"items"`
Kind string `json:"kind"`
Revisions []struct {
Module string
Revision int
} `json:"revision"`
} `json:"changeSet"`
Culprits []Culprit `json:"culprits"`
Description interface{} `json:"description"`
Duration int64 `json:"duration"`
EstimatedDuration int64 `json:"estimatedDuration"`
Executor interface{} `json:"executor"`
FullDisplayName string `json:"fullDisplayName"`
ID string `json:"id"`
KeepLog bool `json:"keepLog"`
Number int64 `json:"number"`
QueueID int64 `json:"queueId"`
Result string `json:"result"`
Timestamp int64 `json:"timestamp"`
URL string `json:"url"`
MavenArtifacts interface{} `json:"mavenArtifacts"`
MavenVersionUsed string `json:"mavenVersionUsed"`
FingerPrint []FingerPrintResponse
Runs []struct {
Number int64
URL string
} `json:"runs"`
}
// Builds
func (b *Build) Info() *BuildResponse {
return b.Raw
}
func (b *Build) GetActions() []GeneralObj {
return b.Raw.Actions
}
func (b *Build) GetUrl() string {
return b.Raw.URL
}
func (b *Build) GetBuildNumber() int64 {
return b.Raw.Number
}
func (b *Build) GetResult() string {
return b.Raw.Result
}
func (b *Build) GetArtifacts() []Artifact {
artifacts := make([]Artifact, len(b.Raw.Artifacts))
for i, artifact := range b.Raw.Artifacts {
artifacts[i] = Artifact{
Jenkins: b.Jenkins,
Build: b,
FileName: artifact.FileName,
Path: b.Base + "/artifact/" + artifact.RelativePath,
}
}
return artifacts
}
func (b *Build) GetCulprits() []Culprit {
return b.Raw.Culprits
}
func (b *Build) Stop() (bool, error) {
if b.IsRunning() {
response, err := b.Jenkins.Requester.Post(b.Base+"/stop", nil, nil, nil)
if err != nil {
return false, err
}
if response.StatusCode != http.StatusOK {
return false, errors.New(strconv.Itoa(response.StatusCode))
}
}
return true, nil
}
func (b *Build) GetConsoleOutput() string {
url := b.Base + "/consoleText"
var content string
b.Jenkins.Requester.GetXML(url, &content, nil)
return content
}
func (b *Build) GetCauses() ([]map[string]interface{}, error) {
_, err := b.Poll()
if err != nil {
return nil, err
}
for _, a := range b.Raw.Actions {
if a.Causes != nil {
return a.Causes, nil
}
}
return nil, errors.New("No Causes")
}
func (b *Build) GetParameters() []parameter {
for _, a := range b.Raw.Actions {
if a.Parameters != nil {
return a.Parameters
}
}
return nil
}
func (b *Build) GetInjectedEnvVars() (map[string]string, error) {
var envVars struct {
EnvMap map[string]string `json:"envMap"`
}
endpoint := b.Base + "/injectedEnvVars"
_, err := b.Jenkins.Requester.GetJSON(endpoint, &envVars, nil)
if err != nil {
return envVars.EnvMap, err
}
return envVars.EnvMap, nil
}
func (b *Build) GetDownstreamBuilds() ([]*Build, error) {
result := make([]*Build, 0)
downstreamJobs, err := b.Job.GetDownstreamJobs()
if err != nil {
return nil, err
}
for _, job := range downstreamJobs {
allBuildIDs, err := job.GetAllBuildIds()
if err != nil {
return nil, err
}
for _, buildID := range allBuildIDs {
build, err := job.GetBuild(buildID.Number)
if err != nil {
return nil, err
}
upstreamBuild, _ := build.GetUpstreamBuild()
// cannot compare only id, it can be from different job
if b.GetUrl() == upstreamBuild.GetUrl() {
result = append(result, build)
break
}
}
}
return result, nil
}
func (b *Build) GetDownstreamJobNames() []string {
result := make([]string, 0)
downstreamJobs := b.Job.GetDownstreamJobsMetadata()
fingerprints := b.GetAllFingerPrints()
for _, fingerprint := range fingerprints {
for _, usage := range fingerprint.Raw.Usage {
for _, job := range downstreamJobs {
if job.Name == usage.Name {
result = append(result, job.Name)
}
}
}
}
return result
}
func (b *Build) GetAllFingerPrints() []*FingerPrint {
b.Poll(3)
result := make([]*FingerPrint, len(b.Raw.FingerPrint))
for i, f := range b.Raw.FingerPrint {
result[i] = &FingerPrint{Jenkins: b.Jenkins, Base: "/fingerprint/", Id: f.Hash, Raw: &f}
}
return result
}
func (b *Build) GetUpstreamJob() (*Job, error) {
causes, err := b.GetCauses()
if err != nil {
return nil, err
}
if len(causes) > 0 {
if job, ok := causes[0]["upstreamProject"]; ok {
return b.Jenkins.GetJob(job.(string))
}
}
return nil, errors.New("Unable to get Upstream Job")
}
func (b *Build) GetUpstreamBuildNumber() (int64, error) {
causes, err := b.GetCauses()
if err != nil {
return 0, err
}
if len(causes) > 0 {
if build, ok := causes[0]["upstreamBuild"]; ok {
switch t := build.(type) {
default:
return t.(int64), nil
case float64:
return int64(t), nil
}
}
}
return 0, nil
}
func (b *Build) GetUpstreamBuild() (*Build, error) {
job, err := b.GetUpstreamJob()
if err != nil {
return nil, err
}
if job != nil {
buildNumber, err := b.GetUpstreamBuildNumber()
if err == nil {
return job.GetBuild(buildNumber)
}
}
return nil, errors.New("Build not found")
}
func (b *Build) GetMatrixRuns() ([]*Build, error) {
_, err := b.Poll(0)
if err != nil {
return nil, err
}
runs := b.Raw.Runs
result := make([]*Build, len(b.Raw.Runs))
r, _ := regexp.Compile(`job/(.*?)/(.*?)/(\d+)/`)
for i, run := range runs {
result[i] = &Build{Jenkins: b.Jenkins, Job: b.Job, Raw: new(BuildResponse), Depth: 1, Base: "/" + r.FindString(run.URL)}
result[i].Poll()
}
return result, nil
}
func (b *Build) GetResultSet() (*TestResult, error) {
url := b.Base + "/testReport"
var report TestResult
_, err := b.Jenkins.Requester.GetJSON(url, &report, nil)
if err != nil {
return nil, err
}
return &report, nil
}
func (b *Build) GetTimestamp() time.Time {
msInt := int64(b.Raw.Timestamp)
return time.Unix(0, msInt*int64(time.Millisecond))
}
func (b *Build) GetDuration() int64 {
return b.Raw.Duration
}
func (b *Build) GetRevision() string {
vcs := b.Raw.ChangeSet.Kind
if vcs == "git" || vcs == "hg" {
for _, a := range b.Raw.Actions {
if a.LastBuiltRevision.SHA1 != "" {
return a.LastBuiltRevision.SHA1
}
if a.MercurialRevisionNumber != "" {
return a.MercurialRevisionNumber
}
}
} else if vcs == "svn" {
return strconv.Itoa(b.Raw.ChangeSet.Revisions[0].Revision)
}
return ""
}
func (b *Build) GetRevisionBranch() string {
vcs := b.Raw.ChangeSet.Kind
if vcs == "git" {
for _, a := range b.Raw.Actions {
if len(a.LastBuiltRevision.Branch) > 0 && a.LastBuiltRevision.Branch[0].SHA1 != "" {
return a.LastBuiltRevision.Branch[0].SHA1
}
}
} else {
panic("Not implemented")
}
return ""
}
func (b *Build) IsGood() bool {
return (!b.IsRunning() && b.Raw.Result == STATUS_SUCCESS)
}
func (b *Build) IsRunning() bool {
_, err := b.Poll()
if err != nil {
return false
}
return b.Raw.Building
}
func (b *Build) SetDescription(description string) error {
data := url.Values{}
data.Set("description", description)
_, err := b.Jenkins.Requester.Post(b.Base+"/submitDescription", bytes.NewBufferString(data.Encode()), nil, nil)
return err
}
func (b *Build) PauseToggle() error {
response, err := b.Jenkins.Requester.Post(b.Base+"/pause/toggle", nil, nil, nil)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
// Poll for current data. Optional parameter - depth.
// More about depth here: https://wiki.jenkins-ci.org/display/JENKINS/Remote+access+API
func (b *Build) Poll(options ...interface{}) (int, error) {
depth := "-1"
for _, o := range options {
switch v := o.(type) {
case string:
depth = v
case int:
depth = strconv.Itoa(v)
case int64:
depth = strconv.FormatInt(v, 10)
}
}
if depth == "-1" {
depth = strconv.Itoa(b.Depth)
}
qr := map[string]string{
"depth": depth,
}
response, err := b.Jenkins.Requester.GetJSON(b.Base, b.Raw, qr)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

View File

@@ -0,0 +1,109 @@
package gojenkins
import (
"io"
"strconv"
"strings"
"golang.org/x/net/html"
)
// Parse jenkins ajax response in order find the current jenkins build history
func parseBuildHistory(d io.Reader) []*History {
z := html.NewTokenizer(d)
depth := 0
buildRowCellDepth := -1
builds := make([]*History, 0)
var curBuild *History
for {
tt := z.Next()
switch tt {
case html.ErrorToken:
if z.Err() == io.EOF {
return builds
}
case html.SelfClosingTagToken:
tn, hasAttr := z.TagName()
// fmt.Println("START__", string(tn), hasAttr)
if hasAttr {
a := attr(z)
// <img src="/static/f2881562/images/16x16/red.png" alt="Failed &gt; Console Output" tooltip="Failed &gt; Console Output" style="width: 16px; height: 16px; " class="icon-red icon-sm" />
if string(tn) == "img" {
if hasCSSClass(a, "icon-sm") && buildRowCellDepth > -1 {
if alt, found := a["alt"]; found {
curBuild.BuildStatus = strings.Fields(alt)[0]
}
}
}
}
case html.StartTagToken:
depth++
tn, hasAttr := z.TagName()
// fmt.Println("START__", string(tn), hasAttr)
if hasAttr {
a := attr(z)
// <td class="build-row-cell">
if string(tn) == "td" {
if hasCSSClass(a, "build-row-cell") {
buildRowCellDepth = depth
curBuild = &History{}
builds = append(builds, curBuild)
}
}
// <a update-parent-class=".build-row" href="/job/appscode/job/43/job/build-binary/227/" class="tip model-link inside build-link display-name">#227</a>
if string(tn) == "a" {
if hasCSSClass(a, "build-link") && buildRowCellDepth > -1 {
if href, found := a["href"]; found {
parts := strings.Split(href, "/")
if num, err := strconv.Atoi(parts[len(parts)-2]); err == nil {
curBuild.BuildNumber = num
}
}
}
}
// <div time="1469024602546" class="pane build-details"> ... </div>
if string(tn) == "div" {
if hasCSSClass(a, "build-details") && buildRowCellDepth > -1 {
if t, found := a["time"]; found {
if msec, err := strconv.ParseInt(t, 10, 0); err == nil {
curBuild.BuildTimestamp = msec / 1000
}
}
}
}
}
case html.EndTagToken:
tn, _ := z.TagName()
if string(tn) == "td" && depth == buildRowCellDepth {
buildRowCellDepth = -1
curBuild = nil
}
depth--
}
}
}
func attr(z *html.Tokenizer) map[string]string {
a := make(map[string]string)
for {
k, v, more := z.TagAttr()
if k != nil && v != nil {
a[string(k)] = string(v)
}
if !more {
break
}
}
return a
}
func hasCSSClass(a map[string]string, className string) bool {
if classes, found := a["class"]; found {
for _, class := range strings.Fields(classes) {
if class == className {
return true
}
}
}
return false
}

View File

@@ -0,0 +1,20 @@
package gojenkins
const (
STATUS_FAIL = "FAIL"
STATUS_ERROR = "ERROR"
STATUS_ABORTED = "ABORTED"
STATUS_REGRESSION = "REGRESSION"
STATUS_SUCCESS = "SUCCESS"
STATUS_FIXED = "FIXED"
STATUS_PASSED = "PASSED"
RESULT_STATUS_FAILURE = "FAILURE"
RESULT_STATUS_FAILED = "FAILED"
RESULT_STATUS_SKIPPED = "SKIPPED"
STR_RE_SPLIT_VIEW = "(.*)/view/([^/]*)/?"
)
const (
GLOBAL_ROLE = "globalRoles"
PROJECT_ROLE = "projectRoles"
)

225
pkg/gojenkins/credential.go Normal file
View File

@@ -0,0 +1,225 @@
/*
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 gojenkins
const SSHCrenditalStaplerClass = "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"
const DirectSSHCrenditalStaplerClass = "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource"
const UsernamePassswordCredentialStaplerClass = "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl"
const SecretTextCredentialStaplerClass = "org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl"
const KubeconfigCredentialStaplerClass = "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials"
const DirectKubeconfigCredentialStaperClass = "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials$DirectEntryKubeconfigSource"
const GLOBALScope = "GLOBAL"
type CreateSshCredentialRequest struct {
Credentials SshCredential `json:"credentials"`
}
type CreateUsernamePasswordCredentialRequest struct {
Credentials UsernamePasswordCredential `json:"credentials"`
}
type CreateSecretTextCredentialRequest struct {
Credentials SecretTextCredential `json:"credentials"`
}
type CreateKubeconfigCredentialRequest struct {
Credentials KubeconfigCredential `json:"credentials"`
}
type UsernamePasswordCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Username string `json:"username"`
Password string `json:"password"`
Description string `json:"description"`
StaplerClass string `json:"stapler-class"`
}
type SshCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Username string `json:"username"`
Passphrase string `json:"passphrase"`
KeySource PrivateKeySource `json:"privateKeySource"`
Description string `json:"description"`
StaplerClass string `json:"stapler-class"`
}
type SecretTextCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Secret string `json:"secret"`
Description string `json:"description"`
StaplerClass string `json:"stapler-class"`
}
type KubeconfigCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Description string `json:"description"`
KubeconfigSource KubeconfigSource `json:"kubeconfigSource"`
StaplerClass string `json:"stapler-class"`
}
type PrivateKeySource struct {
StaplerClass string `json:"stapler-class"`
PrivateKey string `json:"privateKey"`
}
type KubeconfigSource struct {
StaplerClass string `json:"stapler-class"`
Content string `json:"content"`
}
type CredentialResponse struct {
Id string `json:"id"`
TypeName string `json:"typeName"`
DisplayName string `json:"displayName"`
Fingerprint *struct {
FileName string `json:"fileName,omitempty"`
Hash string `json:"hash,omitempty"`
Usage []*struct {
Name string `json:"name,omitempty"`
Ranges struct {
Ranges []*struct {
Start int `json:"start"`
End int `json:"end"`
} `json:"ranges"`
} `json:"ranges"`
} `json:"usage,omitempty"`
} `json:"fingerprint,omitempty"`
Description string `json:"description"`
Domain string `json:"domain"`
}
func NewCreateSshCredentialRequest(id, username, passphrase, privateKey, description string) *CreateSshCredentialRequest {
keySource := PrivateKeySource{
StaplerClass: DirectSSHCrenditalStaplerClass,
PrivateKey: privateKey,
}
sshCredential := SshCredential{
Scope: GLOBALScope,
Id: id,
Username: username,
Passphrase: passphrase,
KeySource: keySource,
Description: description,
StaplerClass: SSHCrenditalStaplerClass,
}
return &CreateSshCredentialRequest{
Credentials: sshCredential,
}
}
func NewCreateUsernamePasswordRequest(id, username, password, description string) *CreateUsernamePasswordCredentialRequest {
credential := UsernamePasswordCredential{
Scope: GLOBALScope,
Id: id,
Username: username,
Password: password,
Description: description,
StaplerClass: UsernamePassswordCredentialStaplerClass,
}
return &CreateUsernamePasswordCredentialRequest{
Credentials: credential,
}
}
func NewCreateSecretTextCredentialRequest(id, secret, description string) *CreateSecretTextCredentialRequest {
credential := SecretTextCredential{
Scope: GLOBALScope,
Id: id,
Secret: secret,
Description: description,
StaplerClass: SecretTextCredentialStaplerClass,
}
return &CreateSecretTextCredentialRequest{
Credentials: credential,
}
}
func NewCreateKubeconfigCredentialRequest(id, content, description string) *CreateKubeconfigCredentialRequest {
credentialSource := KubeconfigSource{
StaplerClass: DirectKubeconfigCredentialStaperClass,
Content: content,
}
credential := KubeconfigCredential{
Scope: GLOBALScope,
Id: id,
Description: description,
KubeconfigSource: credentialSource,
StaplerClass: KubeconfigCredentialStaplerClass,
}
return &CreateKubeconfigCredentialRequest{
credential,
}
}
func NewSshCredential(id, username, passphrase, privateKey, description string) *SshCredential {
keySource := PrivateKeySource{
StaplerClass: DirectSSHCrenditalStaplerClass,
PrivateKey: privateKey,
}
return &SshCredential{
Scope: GLOBALScope,
Id: id,
Username: username,
Passphrase: passphrase,
KeySource: keySource,
Description: description,
StaplerClass: SSHCrenditalStaplerClass,
}
}
func NewUsernamePasswordCredential(id, username, password, description string) *UsernamePasswordCredential {
return &UsernamePasswordCredential{
Scope: GLOBALScope,
Id: id,
Username: username,
Password: password,
Description: description,
StaplerClass: UsernamePassswordCredentialStaplerClass,
}
}
func NewSecretTextCredential(id, secret, description string) *SecretTextCredential {
return &SecretTextCredential{
Scope: GLOBALScope,
Id: id,
Secret: secret,
Description: description,
StaplerClass: SecretTextCredentialStaplerClass,
}
}
func NewKubeconfigCredential(id, content, description string) *KubeconfigCredential {
credentialSource := KubeconfigSource{
StaplerClass: DirectKubeconfigCredentialStaperClass,
Content: content,
}
return &KubeconfigCredential{
Scope: GLOBALScope,
Id: id,
Description: description,
KubeconfigSource: credentialSource,
StaplerClass: KubeconfigCredentialStaplerClass,
}
}

44
pkg/gojenkins/executor.go Normal file
View File

@@ -0,0 +1,44 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
type Executor struct {
Raw *ExecutorResponse
Jenkins *Jenkins
}
type ViewData struct {
Name string `json:"name"`
URL string `json:"url"`
}
type ExecutorResponse struct {
AssignedLabels []struct{} `json:"assignedLabels"`
Description interface{} `json:"description"`
Jobs []InnerJob `json:"jobs"`
Mode string `json:"mode"`
NodeDescription string `json:"nodeDescription"`
NodeName string `json:"nodeName"`
NumExecutors int64 `json:"numExecutors"`
OverallLoad struct{} `json:"overallLoad"`
PrimaryView struct {
Name string `json:"name"`
URL string `json:"url"`
} `json:"primaryView"`
QuietingDown bool `json:"quietingDown"`
SlaveAgentPort int64 `json:"slaveAgentPort"`
UnlabeledLoad struct{} `json:"unlabeledLoad"`
UseCrumbs bool `json:"useCrumbs"`
UseSecurity bool `json:"useSecurity"`
Views []ViewData `json:"views"`
}

View File

@@ -0,0 +1,95 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
import (
"errors"
"fmt"
)
type FingerPrint struct {
Jenkins *Jenkins
Base string
Id string
Raw *FingerPrintResponse
}
type FingerPrintResponse struct {
FileName string `json:"fileName"`
Hash string `json:"hash"`
Original struct {
Name string
Number int64
} `json:"original"`
Timestamp int64 `json:"timestamp"`
Usage []struct {
Name string `json:"name"`
Ranges struct {
Ranges []struct {
End int64 `json:"end"`
Start int64 `json:"start"`
} `json:"ranges"`
} `json:"ranges"`
} `json:"usage"`
}
func (f FingerPrint) Valid() (bool, error) {
status, err := f.Poll()
if err != nil {
return false, err
}
if status != 200 || f.Raw.Hash != f.Id {
return false, fmt.Errorf("Jenkins says %s is Invalid or the Status is unknown", f.Id)
}
return true, nil
}
func (f FingerPrint) ValidateForBuild(filename string, build *Build) (bool, error) {
valid, err := f.Valid()
if err != nil {
return false, err
}
if valid {
return true, nil
}
if f.Raw.FileName != filename {
return false, errors.New("Filename does not Match")
}
if build != nil && f.Raw.Original.Name == build.Job.GetName() &&
f.Raw.Original.Number == build.GetBuildNumber() {
return true, nil
}
return false, nil
}
func (f FingerPrint) GetInfo() (*FingerPrintResponse, error) {
_, err := f.Poll()
if err != nil {
return nil, err
}
return f.Raw, nil
}
func (f FingerPrint) Poll() (int, error) {
response, err := f.Jenkins.Requester.GetJSON(f.Base+f.Id, f.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

77
pkg/gojenkins/folder.go Normal file
View File

@@ -0,0 +1,77 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
import (
"errors"
"strconv"
"strings"
)
type Folder struct {
Raw *FolderResponse
Jenkins *Jenkins
Base string
}
type FolderResponse struct {
Actions []GeneralObj
Description string `json:"description"`
DisplayName string `json:"displayName"`
Name string `json:"name"`
URL string `json:"url"`
Jobs []InnerJob `json:"jobs"`
PrimaryView *ViewData `json:"primaryView"`
Views []ViewData `json:"views"`
}
func (f *Folder) parentBase() string {
return f.Base[:strings.LastIndex(f.Base, "/job")]
}
func (f *Folder) GetName() string {
return f.Raw.Name
}
func (f *Folder) Create(name, description string) (*Folder, error) {
mode := "com.cloudbees.hudson.plugins.folder.Folder"
data := map[string]string{
"name": name,
"mode": mode,
"Submit": "OK",
"json": makeJson(map[string]string{
"name": name,
"mode": mode,
"description": description,
}),
}
r, err := f.Jenkins.Requester.Post(f.parentBase()+"/createItem", nil, f.Raw, data)
if err != nil {
return nil, err
}
if r.StatusCode == 200 {
f.Poll()
return f, nil
}
return nil, errors.New(strconv.Itoa(r.StatusCode))
}
func (f *Folder) Poll() (int, error) {
response, err := f.Jenkins.Requester.GetJSON(f.Base, f.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

1067
pkg/gojenkins/jenkins.go Normal file

File diff suppressed because it is too large Load Diff

529
pkg/gojenkins/job.go Normal file
View File

@@ -0,0 +1,529 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/url"
"path"
"strconv"
"strings"
)
type Job struct {
Raw *JobResponse
Jenkins *Jenkins
Base string
}
type JobBuild struct {
Number int64
URL string
}
type JobBuildStatus struct {
Number int64
Building bool
Result string
}
type InnerJob struct {
Name string `json:"name"`
Url string `json:"url"`
Color string `json:"color"`
}
type ParameterDefinition struct {
DefaultParameterValue struct {
Name string `json:"name"`
Value interface{} `json:"value"`
} `json:"defaultParameterValue"`
Description string `json:"description"`
Name string `json:"name"`
Type string `json:"type"`
}
type JobResponse struct {
Class string `json:"_class"`
Actions []GeneralObj
Buildable bool `json:"buildable"`
Builds []JobBuild
Color string `json:"color"`
ConcurrentBuild bool `json:"concurrentBuild"`
Description string `json:"description"`
DisplayName string `json:"displayName"`
DisplayNameOrNull interface{} `json:"displayNameOrNull"`
DownstreamProjects []InnerJob `json:"downstreamProjects"`
FirstBuild JobBuild
HealthReport []struct {
Description string `json:"description"`
IconClassName string `json:"iconClassName"`
IconUrl string `json:"iconUrl"`
Score int64 `json:"score"`
} `json:"healthReport"`
InQueue bool `json:"inQueue"`
KeepDependencies bool `json:"keepDependencies"`
LastBuild JobBuild `json:"lastBuild"`
LastCompletedBuild JobBuild `json:"lastCompletedBuild"`
LastFailedBuild JobBuild `json:"lastFailedBuild"`
LastStableBuild JobBuild `json:"lastStableBuild"`
LastSuccessfulBuild JobBuild `json:"lastSuccessfulBuild"`
LastUnstableBuild JobBuild `json:"lastUnstableBuild"`
LastUnsuccessfulBuild JobBuild `json:"lastUnsuccessfulBuild"`
Name string `json:"name"`
SubJobs []InnerJob `json:"subJobs"`
NextBuildNumber int64 `json:"nextBuildNumber"`
Property []struct {
ParameterDefinitions []ParameterDefinition `json:"parameterDefinitions"`
} `json:"property"`
QueueItem interface{} `json:"queueItem"`
Scm struct{} `json:"scm"`
UpstreamProjects []InnerJob `json:"upstreamProjects"`
URL string `json:"url"`
Jobs []InnerJob `json:"jobs"`
PrimaryView *ViewData `json:"primaryView"`
Views []ViewData `json:"views"`
}
func (j *Job) parentBase() string {
return j.Base[:strings.LastIndex(j.Base, "/job/")]
}
type History struct {
BuildNumber int
BuildStatus string
BuildTimestamp int64
}
func (j *Job) GetName() string {
return j.Raw.Name
}
func (j *Job) GetDescription() string {
return j.Raw.Description
}
func (j *Job) GetDetails() *JobResponse {
return j.Raw
}
func (j *Job) GetBuild(id int64) (*Build, error) {
build := Build{Jenkins: j.Jenkins, Job: j, Raw: new(BuildResponse), Depth: 1, Base: "/job/" + j.GetName() + "/" + strconv.FormatInt(id, 10)}
status, err := build.Poll()
if err != nil {
return nil, err
}
if status == 200 {
return &build, nil
}
return nil, errors.New(strconv.Itoa(status))
}
func (j *Job) getBuildByType(buildType string) (*Build, error) {
allowed := map[string]JobBuild{
"lastStableBuild": j.Raw.LastStableBuild,
"lastSuccessfulBuild": j.Raw.LastSuccessfulBuild,
"lastBuild": j.Raw.LastBuild,
"lastCompletedBuild": j.Raw.LastCompletedBuild,
"firstBuild": j.Raw.FirstBuild,
"lastFailedBuild": j.Raw.LastFailedBuild,
}
number := ""
if val, ok := allowed[buildType]; ok {
number = strconv.FormatInt(val.Number, 10)
} else {
panic("No Such Build")
}
build := Build{
Jenkins: j.Jenkins,
Depth: 1,
Job: j,
Raw: new(BuildResponse),
Base: j.Base + "/" + number}
status, err := build.Poll()
if err != nil {
return nil, err
}
if status == 200 {
return &build, nil
}
return nil, errors.New(strconv.Itoa(status))
}
func (j *Job) GetLastSuccessfulBuild() (*Build, error) {
return j.getBuildByType("lastSuccessfulBuild")
}
func (j *Job) GetFirstBuild() (*Build, error) {
return j.getBuildByType("firstBuild")
}
func (j *Job) GetLastBuild() (*Build, error) {
return j.getBuildByType("lastBuild")
}
func (j *Job) GetLastStableBuild() (*Build, error) {
return j.getBuildByType("lastStableBuild")
}
func (j *Job) GetLastFailedBuild() (*Build, error) {
return j.getBuildByType("lastFailedBuild")
}
func (j *Job) GetLastCompletedBuild() (*Build, error) {
return j.getBuildByType("lastCompletedBuild")
}
// Returns All Builds with Number and URL
func (j *Job) GetAllBuildIds() ([]JobBuild, error) {
var buildsResp struct {
Builds []JobBuild `json:"allBuilds"`
}
_, err := j.Jenkins.Requester.GetJSON(j.Base, &buildsResp, map[string]string{"tree": "allBuilds[number,url]"})
if err != nil {
return nil, err
}
return buildsResp.Builds, nil
}
func (j *Job) GetAllBuildStatus() ([]JobBuildStatus, error) {
var buildsResp struct {
Builds []JobBuildStatus `json:"allBuilds"`
}
_, err := j.Jenkins.Requester.GetJSON(j.Base, &buildsResp, map[string]string{"tree": "allBuilds[number,building,result]"})
if err != nil {
return nil, err
}
return buildsResp.Builds, nil
}
func (j *Job) GetSubJobsMetadata() []InnerJob {
return j.Raw.SubJobs
}
func (j *Job) GetUpstreamJobsMetadata() []InnerJob {
return j.Raw.UpstreamProjects
}
func (j *Job) GetDownstreamJobsMetadata() []InnerJob {
return j.Raw.DownstreamProjects
}
func (j *Job) GetSubJobs() ([]*Job, error) {
jobs := make([]*Job, len(j.Raw.SubJobs))
for i, job := range j.Raw.SubJobs {
ji, err := j.Jenkins.GetSubJob(j.GetName(), job.Name)
if err != nil {
return nil, err
}
jobs[i] = ji
}
return jobs, nil
}
func (j *Job) GetInnerJobsMetadata() []InnerJob {
return j.Raw.Jobs
}
func (j *Job) GetUpstreamJobs() ([]*Job, error) {
jobs := make([]*Job, len(j.Raw.UpstreamProjects))
for i, job := range j.Raw.UpstreamProjects {
ji, err := j.Jenkins.GetJob(job.Name)
if err != nil {
return nil, err
}
jobs[i] = ji
}
return jobs, nil
}
func (j *Job) GetDownstreamJobs() ([]*Job, error) {
jobs := make([]*Job, len(j.Raw.DownstreamProjects))
for i, job := range j.Raw.DownstreamProjects {
ji, err := j.Jenkins.GetJob(job.Name)
if err != nil {
return nil, err
}
jobs[i] = ji
}
return jobs, nil
}
func (j *Job) GetInnerJob(id string) (*Job, error) {
job := Job{Jenkins: j.Jenkins, Raw: new(JobResponse), Base: j.Base + "/job/" + id}
status, err := job.Poll()
if err != nil {
return nil, err
}
if status == 200 {
return &job, nil
}
return nil, errors.New(strconv.Itoa(status))
}
func (j *Job) GetInnerJobs() ([]*Job, error) {
jobs := make([]*Job, len(j.Raw.Jobs))
for i, job := range j.Raw.Jobs {
ji, err := j.GetInnerJob(job.Name)
if err != nil {
return nil, err
}
jobs[i] = ji
}
return jobs, nil
}
func (j *Job) Enable() (bool, error) {
resp, err := j.Jenkins.Requester.Post(j.Base+"/enable", nil, nil, nil)
if err != nil {
return false, err
}
if resp.StatusCode != 200 {
return false, errors.New(strconv.Itoa(resp.StatusCode))
}
return true, nil
}
func (j *Job) Disable() (bool, error) {
resp, err := j.Jenkins.Requester.Post(j.Base+"/disable", nil, nil, nil)
if err != nil {
return false, err
}
if resp.StatusCode != 200 {
return false, errors.New(strconv.Itoa(resp.StatusCode))
}
return true, nil
}
func (j *Job) Delete() (bool, error) {
resp, err := j.Jenkins.Requester.Post(j.Base+"/doDelete", nil, nil, nil)
if err != nil {
return false, err
}
if resp.StatusCode != 200 {
return false, errors.New(strconv.Itoa(resp.StatusCode))
}
return true, nil
}
func (j *Job) Rename(name string) (bool, error) {
data := url.Values{}
data.Set("newName", name)
_, err := j.Jenkins.Requester.Post(j.Base+"/doRename", bytes.NewBufferString(data.Encode()), nil, nil)
if err != nil {
return false, err
}
j.Base = "/job/" + name
j.Poll()
return true, nil
}
func (j *Job) Create(config string, qr ...interface{}) (*Job, error) {
var querystring map[string]string
if len(qr) > 0 {
querystring = qr[0].(map[string]string)
}
resp, err := j.Jenkins.Requester.PostXML(j.parentBase()+"/createItem", config, j.Raw, querystring)
if err != nil {
return nil, err
}
if resp.StatusCode == 200 {
j.Poll()
return j, nil
}
return nil, errors.New(strconv.Itoa(resp.StatusCode))
}
func (j *Job) Copy(destinationName string) (*Job, error) {
qr := map[string]string{"name": destinationName, "from": j.GetName(), "mode": "copy"}
resp, err := j.Jenkins.Requester.Post(j.parentBase()+"/createItem", nil, nil, qr)
if err != nil {
return nil, err
}
if resp.StatusCode == 200 {
newJob := &Job{Jenkins: j.Jenkins, Raw: new(JobResponse), Base: "/job/" + destinationName}
_, err := newJob.Poll()
if err != nil {
return nil, err
}
return newJob, nil
}
return nil, errors.New(strconv.Itoa(resp.StatusCode))
}
func (j *Job) UpdateConfig(config string) error {
var querystring map[string]string
resp, err := j.Jenkins.Requester.PostXML(j.Base+"/config.xml", config, nil, querystring)
if err != nil {
return err
}
if resp.StatusCode == 200 {
j.Poll()
return nil
}
return errors.New(strconv.Itoa(resp.StatusCode))
}
func (j *Job) GetConfig() (string, error) {
var data string
_, err := j.Jenkins.Requester.GetXML(j.Base+"/config.xml", &data, nil)
if err != nil {
return "", err
}
return data, nil
}
func (j *Job) GetParameters() ([]ParameterDefinition, error) {
_, err := j.Poll()
if err != nil {
return nil, err
}
var parameters []ParameterDefinition
for _, property := range j.Raw.Property {
parameters = append(parameters, property.ParameterDefinitions...)
}
return parameters, nil
}
func (j *Job) IsQueued() (bool, error) {
if _, err := j.Poll(); err != nil {
return false, err
}
return j.Raw.InQueue, nil
}
func (j *Job) IsRunning() (bool, error) {
if _, err := j.Poll(); err != nil {
return false, err
}
lastBuild, err := j.GetLastBuild()
if err != nil {
return false, err
}
return lastBuild.IsRunning(), nil
}
func (j *Job) IsEnabled() (bool, error) {
if _, err := j.Poll(); err != nil {
return false, err
}
return j.Raw.Color != "disabled", nil
}
func (j *Job) HasQueuedBuild() {
panic("Not Implemented yet")
}
func (j *Job) InvokeSimple(params map[string]string) (int64, error) {
endpoint := "/build"
parameters, err := j.GetParameters()
if err != nil {
return 0, err
}
if len(parameters) > 0 {
endpoint = "/buildWithParameters"
}
data := url.Values{}
for k, v := range params {
data.Set(k, v)
}
resp, err := j.Jenkins.Requester.Post(j.Base+endpoint, bytes.NewBufferString(data.Encode()), nil, nil)
if err != nil {
return 0, err
}
if resp.StatusCode != 200 && resp.StatusCode != 201 {
return 0, errors.New("Could not invoke job " + j.GetName())
}
location := resp.Header.Get("Location")
if location == "" {
return 0, errors.New("Don't have key \"Location\" in response of header")
}
u, err := url.Parse(location)
if err != nil {
return 0, err
}
number, err := strconv.ParseInt(path.Base(u.Path), 10, 64)
if err != nil {
return 0, err
}
return number, nil
}
func (j *Job) Invoke(files []string, skipIfRunning bool, params map[string]string, cause string, securityToken string) (bool, error) {
isRunning, err := j.IsRunning()
if err != nil {
return false, err
}
if isRunning && skipIfRunning {
return false, fmt.Errorf("Will not request new build because %s is already running", j.GetName())
}
base := "/build"
// If parameters are specified - url is /builWithParameters
if params != nil {
base = "/buildWithParameters"
} else {
params = make(map[string]string)
}
// If files are specified - url is /build
if files != nil {
base = "/build"
}
reqParams := map[string]string{}
buildParams := map[string]string{}
if securityToken != "" {
reqParams["token"] = securityToken
}
buildParams["json"] = string(makeJson(params))
b, _ := json.Marshal(buildParams)
resp, err := j.Jenkins.Requester.PostFiles(j.Base+base, bytes.NewBuffer(b), nil, reqParams, files)
if err != nil {
return false, err
}
if resp.StatusCode == 200 || resp.StatusCode == 201 {
return true, nil
}
return false, errors.New(strconv.Itoa(resp.StatusCode))
}
func (j *Job) Poll() (int, error) {
response, err := j.Jenkins.Requester.GetJSON(j.Base, j.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}
func (j *Job) History() ([]*History, error) {
resp, err := j.Jenkins.Requester.Get(j.Base+"/buildHistory/ajax", nil, nil)
if err != nil {
return nil, err
}
return parseBuildHistory(resp.Body), nil
}

62
pkg/gojenkins/label.go Normal file
View File

@@ -0,0 +1,62 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
type Label struct {
Raw *LabelResponse
Jenkins *Jenkins
Base string
}
type MODE string
const (
NORMAL MODE = "NORMAL"
EXCLUSIVE = "EXCLUSIVE"
)
type LabelNode struct {
NodeName string `json:"nodeName"`
NodeDescription string `json:"nodeDescription"`
NumExecutors int64 `json:"numExecutors"`
Mode string `json:"mode"`
Class string `json:"_class"`
}
type LabelResponse struct {
Name string `json:"name"`
Description string `json:"description"`
Nodes []LabelNode `json:"nodes"`
Offline bool `json:"offline"`
IdleExecutors int64 `json:"idleExecutors"`
BusyExecutors int64 `json:"busyExecutors"`
TotalExecutors int64 `json:"totalExecutors"`
}
func (l *Label) GetName() string {
return l.Raw.Name
}
func (l *Label) GetNodes() []LabelNode {
return l.Raw.Nodes
}
func (l *Label) Poll() (int, error) {
response, err := l.Jenkins.Requester.GetJSON(l.Base, l.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

230
pkg/gojenkins/node.go Normal file
View File

@@ -0,0 +1,230 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
import "errors"
// Nodes
type Computers struct {
BusyExecutors int `json:"busyExecutors"`
Computers []*NodeResponse `json:"computer"`
DisplayName string `json:"displayName"`
TotalExecutors int `json:"totalExecutors"`
}
type Node struct {
Raw *NodeResponse
Jenkins *Jenkins
Base string
}
type NodeResponse struct {
Actions []interface{} `json:"actions"`
DisplayName string `json:"displayName"`
Executors []struct {
CurrentExecutable struct {
Number int `json:"number"`
URL string `json:"url"`
SubBuilds []struct {
Abort bool `json:"abort"`
Build interface{} `json:"build"`
BuildNumber int `json:"buildNumber"`
Duration string `json:"duration"`
Icon string `json:"icon"`
JobName string `json:"jobName"`
ParentBuildNumber int `json:"parentBuildNumber"`
ParentJobName string `json:"parentJobName"`
PhaseName string `json:"phaseName"`
Result string `json:"result"`
Retry bool `json:"retry"`
URL string `json:"url"`
} `json:"subBuilds"`
} `json:"currentExecutable"`
} `json:"executors"`
Icon string `json:"icon"`
IconClassName string `json:"iconClassName"`
Idle bool `json:"idle"`
JnlpAgent bool `json:"jnlpAgent"`
LaunchSupported bool `json:"launchSupported"`
LoadStatistics struct{} `json:"loadStatistics"`
ManualLaunchAllowed bool `json:"manualLaunchAllowed"`
MonitorData struct {
Hudson_NodeMonitors_ArchitectureMonitor interface{} `json:"hudson.node_monitors.ArchitectureMonitor"`
Hudson_NodeMonitors_ClockMonitor interface{} `json:"hudson.node_monitors.ClockMonitor"`
Hudson_NodeMonitors_DiskSpaceMonitor interface{} `json:"hudson.node_monitors.DiskSpaceMonitor"`
Hudson_NodeMonitors_ResponseTimeMonitor struct {
Average int64 `json:"average"`
} `json:"hudson.node_monitors.ResponseTimeMonitor"`
Hudson_NodeMonitors_SwapSpaceMonitor interface{} `json:"hudson.node_monitors.SwapSpaceMonitor"`
Hudson_NodeMonitors_TemporarySpaceMonitor interface{} `json:"hudson.node_monitors.TemporarySpaceMonitor"`
} `json:"monitorData"`
NumExecutors int64 `json:"numExecutors"`
Offline bool `json:"offline"`
OfflineCause struct{} `json:"offlineCause"`
OfflineCauseReason string `json:"offlineCauseReason"`
OneOffExecutors []interface{} `json:"oneOffExecutors"`
TemporarilyOffline bool `json:"temporarilyOffline"`
}
func (n *Node) Info() (*NodeResponse, error) {
_, err := n.Poll()
if err != nil {
return nil, err
}
return n.Raw, nil
}
func (n *Node) GetName() string {
return n.Raw.DisplayName
}
func (n *Node) Delete() (bool, error) {
resp, err := n.Jenkins.Requester.Post(n.Base+"/doDelete", nil, nil, nil)
if err != nil {
return false, err
}
return resp.StatusCode == 200, nil
}
func (n *Node) IsOnline() (bool, error) {
_, err := n.Poll()
if err != nil {
return false, err
}
return !n.Raw.Offline, nil
}
func (n *Node) IsTemporarilyOffline() (bool, error) {
_, err := n.Poll()
if err != nil {
return false, err
}
return n.Raw.TemporarilyOffline, nil
}
func (n *Node) IsIdle() (bool, error) {
_, err := n.Poll()
if err != nil {
return false, err
}
return n.Raw.Idle, nil
}
func (n *Node) IsJnlpAgent() (bool, error) {
_, err := n.Poll()
if err != nil {
return false, err
}
return n.Raw.JnlpAgent, nil
}
func (n *Node) SetOnline() (bool, error) {
_, err := n.Poll()
if err != nil {
return false, err
}
if n.Raw.Offline && !n.Raw.TemporarilyOffline {
return false, errors.New("Node is Permanently offline, can't bring it up")
}
if n.Raw.Offline && n.Raw.TemporarilyOffline {
return n.ToggleTemporarilyOffline()
}
return true, nil
}
func (n *Node) SetOffline(options ...interface{}) (bool, error) {
if !n.Raw.Offline {
return n.ToggleTemporarilyOffline(options...)
}
return false, errors.New("Node already Offline")
}
func (n *Node) ToggleTemporarilyOffline(options ...interface{}) (bool, error) {
state_before, err := n.IsTemporarilyOffline()
if err != nil {
return false, err
}
qr := map[string]string{"offlineMessage": "requested from gojenkins"}
if len(options) > 0 {
qr["offlineMessage"] = options[0].(string)
}
_, err = n.Jenkins.Requester.Post(n.Base+"/toggleOffline", nil, nil, qr)
if err != nil {
return false, err
}
new_state, err := n.IsTemporarilyOffline()
if err != nil {
return false, err
}
if state_before == new_state {
return false, errors.New("Node state not changed")
}
return true, nil
}
func (n *Node) Poll() (int, error) {
response, err := n.Jenkins.Requester.GetJSON(n.Base, n.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}
func (n *Node) LaunchNodeBySSH() (int, error) {
qr := map[string]string{
"json": "",
"Submit": "Launch slave agent",
}
response, err := n.Jenkins.Requester.Post(n.Base+"/launchSlaveAgent", nil, nil, qr)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}
func (n *Node) Disconnect() (int, error) {
qr := map[string]string{
"offlineMessage": "",
"json": makeJson(map[string]string{"offlineMessage": ""}),
"Submit": "Yes",
}
response, err := n.Jenkins.Requester.Post(n.Base+"/doDisconnect", nil, nil, qr)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}
func (n *Node) GetLogText() (string, error) {
var log string
_, err := n.Jenkins.Requester.Post(n.Base+"/log", nil, nil, nil)
if err != nil {
return "", err
}
qr := map[string]string{"start": "0"}
_, err = n.Jenkins.Requester.GetJSON(n.Base+"/logText/progressiveHtml/", &log, qr)
if err != nil {
return "", nil
}
return log, nil
}

View File

@@ -0,0 +1,162 @@
/*
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 gojenkins
import (
"errors"
"net/http"
"strconv"
)
type ValidateJenkinsfileResponse struct {
Status string `json:"status"`
Data struct {
Result string `json:"result"`
Errors []map[string]interface{} `json:"errors"`
} `json:"data"`
}
type ValidatePipelineJsonResponse struct {
Status string `json:"status"`
Data struct {
Result string `json:"result"`
Errors []map[string]interface{} `json:"errors"`
}
}
type PipelineJsonToJenkinsfileResponse struct {
Status string `json:"status"`
Data struct {
Result string `json:"result"`
Errors []map[string]interface{} `json:"errors"`
Jenkinsfile string `json:"jenkinsfile"`
} `json:"data"`
}
type JenkinsfileToPipelineJsonResponse struct {
Status string `json:"status"`
Data struct {
Result string `json:"result"`
Errors []map[string]interface{} `json:"errors"`
Json map[string]interface{} `json:"json"`
} `json:"data"`
}
type StepJsonToJenkinsfileResponse struct {
Status string `json:"status"`
Data struct {
Result string `json:"result"`
Errors []map[string]interface{} `json:"errors"`
Jenkinsfile string `json:"jenkinsfile"`
} `json:"data"`
}
type StepsJenkinsfileToJsonResponse struct {
Status string `json:"status"`
Data struct {
Result string `json:"result"`
Errors []map[string]interface{} `json:"errors"`
Json []map[string]interface{} `json:"json"`
} `json:"data"`
}
func (j *Jenkins) ValidateJenkinsfile(jenkinsfile string) (*ValidateJenkinsfileResponse, error) {
responseStrut := &ValidateJenkinsfileResponse{}
query := map[string]string{
"jenkinsfile": jenkinsfile,
}
response, err := j.Requester.PostForm("/pipeline-model-converter/validateJenkinsfile", nil, responseStrut, query)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseStrut, nil
}
func (j *Jenkins) ValidatePipelineJson(json string) (*ValidatePipelineJsonResponse, error) {
responseStruct := &ValidatePipelineJsonResponse{}
query := map[string]string{
"json": json,
}
response, err := j.Requester.PostForm("/pipeline-model-converter/validateJson", nil, responseStruct, query)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseStruct, nil
}
func (j *Jenkins) PipelineJsonToJenkinsfile(json string) (*PipelineJsonToJenkinsfileResponse, error) {
responseStrut := &PipelineJsonToJenkinsfileResponse{}
query := map[string]string{
"json": json,
}
response, err := j.Requester.PostForm("/pipeline-model-converter/toJenkinsfile", nil, responseStrut, query)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseStrut, nil
}
func (j *Jenkins) JenkinsfileToPipelineJson(jenkinsfile string) (*JenkinsfileToPipelineJsonResponse, error) {
responseStrut := &JenkinsfileToPipelineJsonResponse{}
query := map[string]string{
"jenkinsfile": jenkinsfile,
}
response, err := j.Requester.PostForm("/pipeline-model-converter/toJson", nil, responseStrut, query)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseStrut, nil
}
func (j *Jenkins) StepsJsonToJenkinsfile(json string) (*StepJsonToJenkinsfileResponse, error) {
responseStrut := &StepJsonToJenkinsfileResponse{}
query := map[string]string{
"json": json,
}
response, err := j.Requester.PostForm("/pipeline-model-converter/stepsToJenkinsfile", nil, responseStrut, query)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseStrut, nil
}
func (j *Jenkins) StepsJenkinsfileToJson(jenkinsfile string) (*StepsJenkinsfileToJsonResponse, error) {
responseStrut := &StepsJenkinsfileToJsonResponse{}
query := map[string]string{
"jenkinsfile": jenkinsfile,
}
response, err := j.Requester.PostForm("/pipeline-model-converter/stepsToJson", nil, responseStrut, query)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseStrut, nil
}

75
pkg/gojenkins/plugin.go Normal file
View File

@@ -0,0 +1,75 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
import (
"strconv"
)
type Plugins struct {
Jenkins *Jenkins
Raw *PluginResponse
Base string
Depth int
}
type PluginResponse struct {
Plugins []Plugin `json:"plugins"`
}
type Plugin struct {
Active bool `json:"active"`
BackupVersion interface{} `json:"backupVersion"`
Bundled bool `json:"bundled"`
Deleted bool `json:"deleted"`
Dependencies []struct {
Optional string `json:"optional"`
ShortName string `json:"shortname"`
Version string `json:"version"`
} `json:"dependencies"`
Downgradable bool `json:"downgradable"`
Enabled bool `json:"enabled"`
HasUpdate bool `json:"hasUpdate"`
LongName string `json:"longName"`
Pinned bool `json:"pinned"`
ShortName string `json:"shortName"`
SupportsDynamicLoad string `json:"supportsDynamicLoad"`
URL string `json:"url"`
Version string `json:"version"`
}
func (p *Plugins) Count() int {
return len(p.Raw.Plugins)
}
func (p *Plugins) Contains(name string) *Plugin {
for _, p := range p.Raw.Plugins {
if p.LongName == name || p.ShortName == name {
return &p
}
}
return nil
}
func (p *Plugins) Poll() (int, error) {
qr := map[string]string{
"depth": strconv.Itoa(p.Depth),
}
response, err := p.Jenkins.Requester.GetJSON(p.Base, p.Raw, qr)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

158
pkg/gojenkins/queue.go Normal file
View File

@@ -0,0 +1,158 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
import (
"strconv"
)
type Queue struct {
Jenkins *Jenkins
Raw *queueResponse
Base string
}
type queueResponse struct {
Items []taskResponse
}
type Task struct {
Raw *taskResponse
Jenkins *Jenkins
Queue *Queue
}
type taskResponse struct {
Actions []generalAction `json:"actions"`
Blocked bool `json:"blocked"`
Buildable bool `json:"buildable"`
BuildableStartMilliseconds int64 `json:"buildableStartMilliseconds"`
ID int64 `json:"id"`
InQueueSince int64 `json:"inQueueSince"`
Params string `json:"params"`
Pending bool `json:"pending"`
Stuck bool `json:"stuck"`
Task struct {
Color string `json:"color"`
Name string `json:"name"`
URL string `json:"url"`
} `json:"task"`
URL string `json:"url"`
Why string `json:"why"`
}
type generalAction struct {
Causes []map[string]interface{}
Parameters []parameter
}
type QueueItemResponse struct {
Actions []generalAction `json:"actions"`
Blocked bool `json:"blocked"`
Buildable bool `json:"buildable"`
ID int64 `json:"id"`
InQueueSince int64 `json:"inQueueSince"`
Params string `json:"params"`
Stuck bool `json:"stuck"`
Task struct {
Color string `json:"color"`
Name string `json:"name"`
URL string `json:"url"`
} `json:"task"`
URL string `json:"url"`
Cancelled bool `json:"cancelled"`
Why string `json:"why"`
Executable struct {
Number int64 `json:"number"`
Url string `json:"url"`
} `json:"executable"`
}
func (q *Queue) Tasks() []*Task {
tasks := make([]*Task, len(q.Raw.Items))
for i, t := range q.Raw.Items {
tasks[i] = &Task{Jenkins: q.Jenkins, Queue: q, Raw: &t}
}
return tasks
}
func (q *Queue) GetTaskById(id int64) *Task {
for _, t := range q.Raw.Items {
if t.ID == id {
return &Task{Jenkins: q.Jenkins, Queue: q, Raw: &t}
}
}
return nil
}
func (q *Queue) GetTasksForJob(name string) []*Task {
tasks := make([]*Task, 0)
for _, t := range q.Raw.Items {
if t.Task.Name == name {
tasks = append(tasks, &Task{Jenkins: q.Jenkins, Queue: q, Raw: &t})
}
}
return tasks
}
func (q *Queue) CancelTask(id int64) (bool, error) {
task := q.GetTaskById(id)
return task.Cancel()
}
func (t *Task) Cancel() (bool, error) {
qr := map[string]string{
"id": strconv.FormatInt(t.Raw.ID, 10),
}
response, err := t.Jenkins.Requester.Post(t.Jenkins.GetQueueUrl()+"/cancelItem", nil, t.Raw, qr)
if err != nil {
return false, err
}
return response.StatusCode == 200, nil
}
func (t *Task) GetJob() (*Job, error) {
return t.Jenkins.GetJob(t.Raw.Task.Name)
}
func (t *Task) GetWhy() string {
return t.Raw.Why
}
func (t *Task) GetParameters() []parameter {
for _, a := range t.Raw.Actions {
if a.Parameters != nil {
return a.Parameters
}
}
return nil
}
func (t *Task) GetCauses() []map[string]interface{} {
for _, a := range t.Raw.Actions {
if a.Causes != nil {
return a.Causes
}
}
return nil
}
func (q *Queue) Poll() (int, error) {
response, err := q.Jenkins.Requester.GetJSON(q.Base, q.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

468
pkg/gojenkins/request.go Normal file
View File

@@ -0,0 +1,468 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
)
// Request Methods
type APIRequest struct {
Method string
Endpoint string
Payload io.Reader
Headers http.Header
Suffix string
}
func (ar *APIRequest) SetHeader(key string, value string) *APIRequest {
ar.Headers.Set(key, value)
return ar
}
func NewAPIRequest(method string, endpoint string, payload io.Reader) *APIRequest {
var headers = http.Header{}
var suffix string
ar := &APIRequest{method, endpoint, payload, headers, suffix}
return ar
}
type Requester struct {
Base string
BasicAuth *BasicAuth
Client *http.Client
CACert []byte
SslVerify bool
connControl chan struct{}
}
func (r *Requester) SetCrumb(ar *APIRequest) error {
crumbData := map[string]string{}
response, err := r.GetJSON("/crumbIssuer/api/json", &crumbData, nil)
if err != nil {
jenkinsError, ok := err.(*ErrorResponse)
if ok && jenkinsError.Response.StatusCode == http.StatusNotFound {
return nil
}
return err
}
if response.StatusCode == 200 && crumbData["crumbRequestField"] != "" {
ar.SetHeader(crumbData["crumbRequestField"], crumbData["crumb"])
}
return nil
}
func (r *Requester) PostJSON(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
ar := NewAPIRequest("POST", endpoint, payload)
if err := r.SetCrumb(ar); err != nil {
return nil, err
}
ar.SetHeader("Content-Type", "application/x-www-form-urlencoded")
ar.Suffix = "api/json"
return r.Do(ar, &responseStruct, querystring)
}
func (r *Requester) Post(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
ar := NewAPIRequest("POST", endpoint, payload)
if err := r.SetCrumb(ar); err != nil {
return nil, err
}
ar.SetHeader("Content-Type", "application/x-www-form-urlencoded")
ar.Suffix = ""
return r.Do(ar, &responseStruct, querystring)
}
func (r *Requester) PostForm(endpoint string, payload io.Reader, responseStruct interface{}, formString map[string]string) (*http.Response, error) {
ar := NewAPIRequest("POST", endpoint, payload)
if err := r.SetCrumb(ar); err != nil {
return nil, err
}
ar.SetHeader("Content-Type", "application/x-www-form-urlencoded")
ar.Suffix = ""
return r.DoPostForm(ar, &responseStruct, formString)
}
func (r *Requester) PostFiles(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string, files []string) (*http.Response, error) {
ar := NewAPIRequest("POST", endpoint, payload)
if err := r.SetCrumb(ar); err != nil {
return nil, err
}
return r.Do(ar, &responseStruct, querystring, files)
}
func (r *Requester) PostXML(endpoint string, xml string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
payload := bytes.NewBuffer([]byte(xml))
ar := NewAPIRequest("POST", endpoint, payload)
if err := r.SetCrumb(ar); err != nil {
return nil, err
}
ar.SetHeader("Content-Type", "application/xml;charset=utf-8")
ar.Suffix = ""
return r.Do(ar, &responseStruct, querystring)
}
func (r *Requester) GetJSON(endpoint string, responseStruct interface{}, query map[string]string) (*http.Response, error) {
ar := NewAPIRequest("GET", endpoint, nil)
ar.SetHeader("Content-Type", "application/json")
ar.Suffix = "api/json"
return r.Do(ar, &responseStruct, query)
}
func (r *Requester) GetXML(endpoint string, responseStruct interface{}, query map[string]string) (*http.Response, error) {
ar := NewAPIRequest("GET", endpoint, nil)
ar.SetHeader("Content-Type", "application/xml")
ar.Suffix = ""
return r.Do(ar, responseStruct, query)
}
func (r *Requester) Get(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
ar := NewAPIRequest("GET", endpoint, nil)
ar.Suffix = ""
return r.Do(ar, responseStruct, querystring)
}
func (r *Requester) GetHtml(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
ar := NewAPIRequest("GET", endpoint, nil)
ar.Suffix = ""
return r.DoGet(ar, responseStruct, querystring)
}
func (r *Requester) SetClient(client *http.Client) *Requester {
r.Client = client
return r
}
//Add auth on redirect if required.
func (r *Requester) redirectPolicyFunc(req *http.Request, via []*http.Request) error {
if r.BasicAuth != nil {
req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password)
}
return nil
}
func (r *Requester) DoGet(ar *APIRequest, responseStruct interface{}, options ...interface{}) (*http.Response, error) {
fileUpload := false
var files []string
URL, err := url.Parse(r.Base + ar.Endpoint + ar.Suffix)
if err != nil {
return nil, err
}
for _, o := range options {
switch v := o.(type) {
case map[string]string:
querystring := make(url.Values)
for key, val := range v {
querystring.Set(key, val)
}
URL.RawQuery = querystring.Encode()
case []string:
fileUpload = true
files = v
}
}
var req *http.Request
if fileUpload {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
for _, file := range files {
fileData, err := os.Open(file)
if err != nil {
Error.Println(err.Error())
return nil, err
}
part, err := writer.CreateFormFile("file", filepath.Base(file))
if err != nil {
Error.Println(err.Error())
return nil, err
}
if _, err = io.Copy(part, fileData); err != nil {
return nil, err
}
defer fileData.Close()
}
var params map[string]string
json.NewDecoder(ar.Payload).Decode(&params)
for key, val := range params {
if err = writer.WriteField(key, val); err != nil {
return nil, err
}
}
if err = writer.Close(); err != nil {
return nil, err
}
req, err = http.NewRequest(ar.Method, URL.String(), body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", writer.FormDataContentType())
} else {
req, err = http.NewRequest(ar.Method, URL.String(), ar.Payload)
if err != nil {
return nil, err
}
}
if r.BasicAuth != nil {
req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password)
}
req.Close = true
req.Header.Add("Accept", "*/*")
for k := range ar.Headers {
req.Header.Add(k, ar.Headers.Get(k))
}
r.connControl <- struct{}{}
if response, err := r.Client.Do(req); err != nil {
<-r.connControl
return nil, err
} else {
<-r.connControl
errorText := response.Header.Get("X-Error")
if errorText != "" {
return nil, errors.New(errorText)
}
err := CheckResponse(response)
if err != nil {
return nil, err
}
switch responseStruct.(type) {
case *string:
return r.ReadRawResponse(response, responseStruct)
default:
return r.ReadJSONResponse(response, responseStruct)
}
}
}
func (r *Requester) Do(ar *APIRequest, responseStruct interface{}, options ...interface{}) (*http.Response, error) {
if !strings.HasSuffix(ar.Endpoint, "/") && ar.Method != "POST" {
ar.Endpoint += "/"
}
fileUpload := false
var files []string
URL, err := url.Parse(r.Base + ar.Endpoint + ar.Suffix)
if err != nil {
return nil, err
}
for _, o := range options {
switch v := o.(type) {
case map[string]string:
querystring := make(url.Values)
for key, val := range v {
querystring.Set(key, val)
}
URL.RawQuery = querystring.Encode()
case []string:
fileUpload = true
files = v
}
}
var req *http.Request
if fileUpload {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
for _, file := range files {
fileData, err := os.Open(file)
if err != nil {
Error.Println(err.Error())
return nil, err
}
part, err := writer.CreateFormFile("file", filepath.Base(file))
if err != nil {
Error.Println(err.Error())
return nil, err
}
if _, err = io.Copy(part, fileData); err != nil {
return nil, err
}
defer fileData.Close()
}
var params map[string]string
json.NewDecoder(ar.Payload).Decode(&params)
for key, val := range params {
if err = writer.WriteField(key, val); err != nil {
return nil, err
}
}
if err = writer.Close(); err != nil {
return nil, err
}
req, err = http.NewRequest(ar.Method, URL.String(), body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", writer.FormDataContentType())
} else {
req, err = http.NewRequest(ar.Method, URL.String(), ar.Payload)
if err != nil {
return nil, err
}
}
if r.BasicAuth != nil {
req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password)
}
req.Close = true
req.Header.Add("Accept", "*/*")
for k := range ar.Headers {
req.Header.Add(k, ar.Headers.Get(k))
}
r.connControl <- struct{}{}
if response, err := r.Client.Do(req); err != nil {
<-r.connControl
return nil, err
} else {
<-r.connControl
errorText := response.Header.Get("X-Error")
if errorText != "" {
return nil, errors.New(errorText)
}
err := CheckResponse(response)
if err != nil {
return nil, err
}
switch responseStruct.(type) {
case *string:
return r.ReadRawResponse(response, responseStruct)
default:
return r.ReadJSONResponse(response, responseStruct)
}
}
}
func (r *Requester) DoPostForm(ar *APIRequest, responseStruct interface{}, form map[string]string) (*http.Response, error) {
if !strings.HasSuffix(ar.Endpoint, "/") && ar.Method != "POST" {
ar.Endpoint += "/"
}
URL, err := url.Parse(r.Base + ar.Endpoint + ar.Suffix)
if err != nil {
return nil, err
}
formValue := make(url.Values)
for k, v := range form {
formValue.Set(k, v)
}
req, err := http.NewRequest("POST", URL.String(), strings.NewReader(formValue.Encode()))
if r.BasicAuth != nil {
req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password)
}
req.Close = true
req.Header.Add("Accept", "*/*")
for k := range ar.Headers {
req.Header.Add(k, ar.Headers.Get(k))
}
r.connControl <- struct{}{}
if response, err := r.Client.Do(req); err != nil {
<-r.connControl
return nil, err
} else {
<-r.connControl
errorText := response.Header.Get("X-Error")
if errorText != "" {
return nil, errors.New(errorText)
}
err := CheckResponse(response)
if err != nil {
return nil, err
}
switch responseStruct.(type) {
case *string:
return r.ReadRawResponse(response, responseStruct)
default:
return r.ReadJSONResponse(response, responseStruct)
}
}
}
func (r *Requester) ReadRawResponse(response *http.Response, responseStruct interface{}) (*http.Response, error) {
defer response.Body.Close()
content, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
if str, ok := responseStruct.(*string); ok {
*str = string(content)
} else {
return nil, fmt.Errorf("Could not cast responseStruct to *string")
}
return response, nil
}
func (r *Requester) ReadJSONResponse(response *http.Response, responseStruct interface{}) (*http.Response, error) {
defer response.Body.Close()
err := json.NewDecoder(response.Body).Decode(responseStruct)
if err != nil && err.Error() == "EOF" {
return response, nil
}
return response, nil
}
type ErrorResponse struct {
Body []byte
Response *http.Response
Message string
}
func (e *ErrorResponse) Error() string {
u := fmt.Sprintf("%s://%s%s", e.Response.Request.URL.Scheme, e.Response.Request.URL.Host, e.Response.Request.URL.RequestURI())
return fmt.Sprintf("%s %s: %d %s", e.Response.Request.Method, u, e.Response.StatusCode, e.Message)
}
func CheckResponse(r *http.Response) error {
switch r.StatusCode {
case http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent, http.StatusFound, http.StatusNotModified:
return nil
}
defer r.Body.Close()
errorResponse := &ErrorResponse{Response: r}
data, err := ioutil.ReadAll(r.Body)
if err == nil && data != nil {
errorResponse.Body = data
errorResponse.Message = string(data)
}
return errorResponse
}

204
pkg/gojenkins/role.go Normal file
View File

@@ -0,0 +1,204 @@
package gojenkins
import (
"errors"
"net/http"
"reflect"
"strconv"
"strings"
)
type GlobalRoleResponse struct {
RoleName string `json:"roleName"`
PermissionIds GlobalPermissionIds `json:"permissionIds"`
}
type GlobalRole struct {
Jenkins *Jenkins
Raw GlobalRoleResponse
}
type GlobalPermissionIds struct {
Administer bool `json:"hudson.model.Hudson.Administer"`
GlobalRead bool `json:"hudson.model.Hudson.Read"`
CredentialCreate bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.Create"`
CredentialUpdate bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.Update"`
CredentialView bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.View"`
CredentialDelete bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.Delete"`
CredentialManageDomains bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.ManageDomains"`
SlaveCreate bool `json:"hudson.model.Computer.Create"`
SlaveConfigure bool `json:"hudson.model.Computer.Configure"`
SlaveDelete bool `json:"hudson.model.Computer.Delete"`
SlaveBuild bool `json:"hudson.model.Computer.Build"`
SlaveConnect bool `json:"hudson.model.Computer.Connect"`
SlaveDisconnect bool `json:"hudson.model.Computer.Disconnect"`
ItemBuild bool `json:"hudson.model.Item.Build"`
ItemCreate bool `json:"hudson.model.Item.Create"`
ItemRead bool `json:"hudson.model.Item.Read"`
ItemConfigure bool `json:"hudson.model.Item.Configure"`
ItemCancel bool `json:"hudson.model.Item.Cancel"`
ItemMove bool `json:"hudson.model.Item.Move"`
ItemDiscover bool `json:"hudson.model.Item.Discover"`
ItemWorkspace bool `json:"hudson.model.Item.Workspace"`
ItemDelete bool `json:"hudson.model.Item.Delete"`
RunUpdate bool `json:"hudson.model.Run.Update"`
RunDelete bool `json:"hudson.model.Run.Delete"`
ViewCreate bool `json:"hudson.model.View.Create"`
ViewConfigure bool `json:"hudson.model.View.Configure"`
ViewRead bool `json:"hudson.model.View.Read"`
ViewDelete bool `json:"hudson.model.View.Delete"`
SCMTag bool `json:"hudson.scm.SCM.Tag"`
}
type ProjectRole struct {
Jenkins *Jenkins
Raw ProjectRoleResponse
}
type ProjectRoleResponse struct {
RoleName string `json:"roleName"`
PermissionIds ProjectPermissionIds `json:"permissionIds"`
Pattern string `json:"pattern"`
}
type ProjectPermissionIds struct {
CredentialCreate bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.Create"`
CredentialUpdate bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.Update"`
CredentialView bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.View"`
CredentialDelete bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.Delete"`
CredentialManageDomains bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.ManageDomains"`
ItemBuild bool `json:"hudson.model.Item.Build"`
ItemCreate bool `json:"hudson.model.Item.Create"`
ItemRead bool `json:"hudson.model.Item.Read"`
ItemConfigure bool `json:"hudson.model.Item.Configure"`
ItemCancel bool `json:"hudson.model.Item.Cancel"`
ItemMove bool `json:"hudson.model.Item.Move"`
ItemDiscover bool `json:"hudson.model.Item.Discover"`
ItemWorkspace bool `json:"hudson.model.Item.Workspace"`
ItemDelete bool `json:"hudson.model.Item.Delete"`
RunUpdate bool `json:"hudson.model.Run.Update"`
RunDelete bool `json:"hudson.model.Run.Delete"`
RunReplay bool `json:"hudson.model.Run.Replay"`
SCMTag bool `json:"hudson.scm.SCM.Tag"`
}
func (j *GlobalRole) Update(ids GlobalPermissionIds) error {
var idArray []string
values := reflect.ValueOf(ids)
for i := 0; i < values.NumField(); i++ {
field := values.Field(i)
if field.Bool() {
idArray = append(idArray, values.Type().Field(i).Tag.Get("json"))
}
}
param := map[string]string{
"roleName": j.Raw.RoleName,
"type": GLOBAL_ROLE,
"permissionIds": strings.Join(idArray, ","),
"overwrite": strconv.FormatBool(true),
}
responseString := ""
response, err := j.Jenkins.Requester.Post("/role-strategy/strategy/addRole", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
func (j *GlobalRole) AssignRole(sid string) error {
param := map[string]string{
"type": GLOBAL_ROLE,
"roleName": j.Raw.RoleName,
"sid": sid,
}
responseString := ""
response, err := j.Jenkins.Requester.Post("/role-strategy/strategy/assignRole", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
func (j *GlobalRole) UnAssignRole(sid string) error {
param := map[string]string{
"type": GLOBAL_ROLE,
"roleName": j.Raw.RoleName,
"sid": sid,
}
responseString := ""
response, err := j.Jenkins.Requester.Post("/role-strategy/strategy/unassignRole", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
func (j *ProjectRole) Update(pattern string, ids ProjectPermissionIds) error {
var idArray []string
values := reflect.ValueOf(ids)
for i := 0; i < values.NumField(); i++ {
field := values.Field(i)
if field.Bool() {
idArray = append(idArray, values.Type().Field(i).Tag.Get("json"))
}
}
param := map[string]string{
"roleName": j.Raw.RoleName,
"type": PROJECT_ROLE,
"permissionIds": strings.Join(idArray, ","),
"overwrite": strconv.FormatBool(true),
"pattern": pattern,
}
responseString := ""
response, err := j.Jenkins.Requester.Post("/role-strategy/strategy/addRole", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
func (j *ProjectRole) AssignRole(sid string) error {
param := map[string]string{
"type": PROJECT_ROLE,
"roleName": j.Raw.RoleName,
"sid": sid,
}
responseString := ""
response, err := j.Jenkins.Requester.Post("/role-strategy/strategy/assignRole", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
func (j *ProjectRole) UnAssignRole(sid string) error {
param := map[string]string{
"type": PROJECT_ROLE,
"roleName": j.Raw.RoleName,
"sid": sid,
}
responseString := ""
response, err := j.Jenkins.Requester.Post("/role-strategy/strategy/unassignRole", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}

61
pkg/gojenkins/utils.go Normal file
View File

@@ -0,0 +1,61 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
import (
"encoding/json"
"strings"
"time"
"unicode/utf8"
)
func makeJson(data interface{}) string {
str, err := json.Marshal(data)
if err != nil {
return ""
}
return string(json.RawMessage(str))
}
func Reverse(s string) string {
size := len(s)
buf := make([]byte, size)
for start := 0; start < size; {
r, n := utf8.DecodeRuneInString(s[start:])
start += n
utf8.EncodeRune(buf[size-start:], r)
}
return string(buf)
}
type JenkinsBlueTime time.Time
func (t *JenkinsBlueTime) UnmarshalJSON(b []byte) error {
if b == nil || strings.Trim(string(b), "\"") == "null" {
*t = JenkinsBlueTime(time.Time{})
return nil
}
j, err := time.Parse("2006-01-02T15:04:05.000-0700", strings.Trim(string(b), "\""))
if err != nil {
return err
}
*t = JenkinsBlueTime(j)
return nil
}
func (t JenkinsBlueTime) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(t))
}

View File

@@ -0,0 +1,21 @@
package utils
import (
"github.com/asaskevich/govalidator"
"kubesphere.io/kubesphere/pkg/gojenkins"
"net/http"
"strconv"
)
func GetJenkinsStatusCode(jenkinsErr error) int {
if code, err := strconv.Atoi(jenkinsErr.Error()); err == nil {
message := http.StatusText(code)
if !govalidator.IsNull(message) {
return code
}
}
if jErr, ok := jenkinsErr.(*gojenkins.ErrorResponse); ok {
return jErr.Response.StatusCode
}
return http.StatusInternalServerError
}

94
pkg/gojenkins/views.go Normal file
View File

@@ -0,0 +1,94 @@
// Copyright 2015 Vadim Kravcenko
//
// 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 gojenkins
import (
"errors"
"strconv"
)
type View struct {
Raw *ViewResponse
Jenkins *Jenkins
Base string
}
type ViewResponse struct {
Description string `json:"description"`
Jobs []InnerJob `json:"jobs"`
Name string `json:"name"`
Property []interface{} `json:"property"`
URL string `json:"url"`
}
var (
LIST_VIEW = "hudson.model.ListView"
NESTED_VIEW = "hudson.plugins.nested_view.NestedView"
MY_VIEW = "hudson.model.MyView"
DASHBOARD_VIEW = "hudson.plugins.view.dashboard.Dashboard"
PIPELINE_VIEW = "au.com.centrumsystems.hudson.plugin.buildpipeline.BuildPipelineView"
)
// Returns True if successfully added Job, otherwise false
func (v *View) AddJob(name string) (bool, error) {
url := "/addJobToView"
qr := map[string]string{"name": name}
resp, err := v.Jenkins.Requester.Post(v.Base+url, nil, nil, qr)
if err != nil {
return false, err
}
if resp.StatusCode == 200 {
return true, nil
}
return false, errors.New(strconv.Itoa(resp.StatusCode))
}
// Returns True if successfully deleted Job, otherwise false
func (v *View) DeleteJob(name string) (bool, error) {
url := "/removeJobFromView"
qr := map[string]string{"name": name}
resp, err := v.Jenkins.Requester.Post(v.Base+url, nil, nil, qr)
if err != nil {
return false, err
}
if resp.StatusCode == 200 {
return true, nil
}
return false, errors.New(strconv.Itoa(resp.StatusCode))
}
func (v *View) GetDescription() string {
return v.Raw.Description
}
func (v *View) GetJobs() []InnerJob {
return v.Raw.Jobs
}
func (v *View) GetName() string {
return v.Raw.Name
}
func (v *View) GetUrl() string {
return v.Raw.URL
}
func (v *View) Poll() (int, error) {
response, err := v.Jenkins.Requester.GetJSON(v.Base, v.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

View File

@@ -0,0 +1,61 @@
package devops
import (
"github.com/fatih/structs"
"kubesphere.io/kubesphere/pkg/utils/stringutils"
)
func GetColumnsFromStruct(s interface{}) []string {
names := structs.Names(s)
for i, name := range names {
names[i] = stringutils.CamelCaseToUnderscore(name)
}
return names
}
func GetColumnsFromStructWithPrefix(prefix string, s interface{}) []string {
names := structs.Names(s)
for i, name := range names {
names[i] = WithPrefix(prefix, stringutils.CamelCaseToUnderscore(name))
}
return names
}
func WithPrefix(prefix, str string) string {
return prefix + "." + str
}
const (
StatusActive = "active"
StatusDeleted = "deleted"
StatusDeleting = "deleting"
StatusFailed = "failed"
StatusPending = "pending"
StatusWorking = "working"
StatusSuccessful = "successful"
)
const (
StatusColumn = "status"
StatusTimeColumn = "status_time"
)
const (
VisibilityPrivate = "private"
VisibilityPublic = "public"
)
const (
KS_ADMIN = "admin"
)
const (
ProjectOwner = "owner"
ProjectMaintainer = "maintainer"
ProjectDeveloper = "developer"
ProjectReporter = "reporter"
)
const (
JenkinsAllUserRoleName = "kubesphere-user"
)

View File

@@ -0,0 +1,28 @@
package devops
const (
DevOpsProjectMembershipTableName = "project_membership"
DevOpsProjectMembershipUsernameColumn = "project_membership.username"
DevOpsProjectMembershipProjectIdColumn = "project_membership.project_id"
DevOpsProjectMembershipRoleColumn = "project_membership.role"
)
type DevOpsProjectMembership struct {
Username string `json:"username"`
ProjectId string `json:"project_id" db:"project_id"`
Role string `json:"role"`
Status string `json:"status"`
GrantBy string `json:"grand_by,omitempty"`
}
var DevOpsProjectMembershipColumns = GetColumnsFromStruct(&DevOpsProjectMembership{})
func NewDevOpsProjectMemberShip(username, projectId, role, grantBy string) *DevOpsProjectMembership {
return &DevOpsProjectMembership{
Username: username,
ProjectId: projectId,
Role: role,
Status: StatusActive,
GrantBy: grantBy,
}
}

View File

@@ -0,0 +1,45 @@
package devops
import (
"kubesphere.io/kubesphere/pkg/utils/idutils"
"time"
)
var DevOpsProjectColumns = GetColumnsFromStruct(&DevOpsProject{})
const (
DevOpsProjectTableName = "project"
DevOpsProjectPrefix = "project-"
DevOpsProjectDescriptionColumn = "description"
DevOpsProjectIdColumn = "project.project_id"
DevOpsProjectNameColumn = "project.name"
DevOpsProjectExtraColumn = "project.extra"
DevOpsProjectWorkSpaceColumn = "project.workspace"
DevOpsProjectCreateTimeColumn = "project.create_time"
)
type DevOpsProject struct {
ProjectId string `json:"project_id" db:"project_id"`
Name string `json:"name"`
Description string `json:"description"`
Creator string `json:"creator"`
CreateTime time.Time `json:"create_time"`
Status string `json:"status"`
Visibility string `json:"visibility"`
Extra string `json:"extra"`
Workspace string `json:"workspace"`
}
func NewDevOpsProject(name, description, creator, extra, workspace string) *DevOpsProject {
return &DevOpsProject{
ProjectId: idutils.GetUuid(DevOpsProjectPrefix),
Name: name,
Description: description,
Creator: creator,
CreateTime: time.Now(),
Status: StatusActive,
Visibility: VisibilityPrivate,
Extra: extra,
Workspace: workspace,
}
}

View File

@@ -18,73 +18,469 @@
package tenant
import (
"fmt"
"github.com/gocraft/dbr"
"github.com/golang/glog"
"kubesphere.io/kubesphere/pkg/db"
"kubesphere.io/kubesphere/pkg/gojenkins"
"kubesphere.io/kubesphere/pkg/gojenkins/utils"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"sort"
"strings"
"kubesphere.io/kubesphere/pkg/simple/client/admin_jenkins"
"kubesphere.io/kubesphere/pkg/simple/client/devops_mysql"
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
"net/http"
"sync"
)
const (
ProjectOwner = "owner"
ProjectMaintainer = "maintainer"
ProjectDeveloper = "developer"
ProjectReporter = "reporter"
)
var AllRoleSlice = []string{ProjectDeveloper, ProjectReporter, ProjectMaintainer, ProjectOwner}
var JenkinsOwnerProjectPermissionIds = &gojenkins.ProjectPermissionIds{
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: true,
ItemCreate: true,
ItemDelete: true,
ItemDiscover: true,
ItemMove: true,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
}
var JenkinsProjectPermissionMap = map[string]gojenkins.ProjectPermissionIds{
ProjectOwner: gojenkins.ProjectPermissionIds{
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: true,
ItemCreate: true,
ItemDelete: true,
ItemDiscover: true,
ItemMove: true,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
},
ProjectMaintainer: gojenkins.ProjectPermissionIds{
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: false,
ItemCreate: true,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
},
ProjectDeveloper: gojenkins.ProjectPermissionIds{
CredentialCreate: false,
CredentialDelete: false,
CredentialManageDomains: false,
CredentialUpdate: false,
CredentialView: false,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: false,
ItemCreate: false,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: false,
},
ProjectReporter: gojenkins.ProjectPermissionIds{
CredentialCreate: false,
CredentialDelete: false,
CredentialManageDomains: false,
CredentialUpdate: false,
CredentialView: false,
ItemBuild: false,
ItemCancel: false,
ItemConfigure: false,
ItemCreate: false,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: false,
RunDelete: false,
RunReplay: false,
RunUpdate: false,
SCMTag: false,
},
}
var JenkinsPipelinePermissionMap = map[string]gojenkins.ProjectPermissionIds{
ProjectOwner: gojenkins.ProjectPermissionIds{
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: true,
ItemCreate: true,
ItemDelete: true,
ItemDiscover: true,
ItemMove: true,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
},
ProjectMaintainer: gojenkins.ProjectPermissionIds{
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: true,
ItemCreate: true,
ItemDelete: true,
ItemDiscover: true,
ItemMove: true,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
},
ProjectDeveloper: gojenkins.ProjectPermissionIds{
CredentialCreate: false,
CredentialDelete: false,
CredentialManageDomains: false,
CredentialUpdate: false,
CredentialView: false,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: false,
ItemCreate: false,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: false,
},
ProjectReporter: gojenkins.ProjectPermissionIds{
CredentialCreate: false,
CredentialDelete: false,
CredentialManageDomains: false,
CredentialUpdate: false,
CredentialView: false,
ItemBuild: false,
ItemCancel: false,
ItemConfigure: false,
ItemCreate: false,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: false,
RunDelete: false,
RunReplay: false,
RunUpdate: false,
SCMTag: false,
},
}
func GetProjectRoleName(projectId, role string) string {
return fmt.Sprintf("%s-%s-project", projectId, role)
}
func GetPipelineRoleName(projectId, role string) string {
return fmt.Sprintf("%s-%s-pipeline", projectId, role)
}
func GetProjectRolePattern(projectId string) string {
return fmt.Sprintf("^%s$", projectId)
}
func GetPipelineRolePattern(projectId string) string {
return fmt.Sprintf("^%s/.*", projectId)
}
type DevOpsProjectRoleResponse struct {
ProjectRole *gojenkins.ProjectRole
Err error
}
func CheckProjectUserInRole(username, projectId string, roles []string) error {
if username == devops.KS_ADMIN {
return nil
}
dbconn := devops_mysql.OpenDatabase()
membership := &devops.DevOpsProjectMembership{}
err := dbconn.Select(devops.DevOpsProjectMembershipColumns...).
From(devops.DevOpsProjectMembershipTableName).
Where(db.And(
db.Eq(devops.DevOpsProjectMembershipUsernameColumn, username),
db.Eq(devops.DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership)
if err != nil {
return err
}
if !reflectutils.In(membership.Role, roles) {
return fmt.Errorf("user [%s] in project [%s] role is not in %s", username, projectId, roles)
}
return nil
}
func ListDevopsProjects(workspace, username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) {
db := mysql.Client()
dbconn := devops_mysql.OpenDatabase()
var workspaceDOPBindings []models.WorkspaceDPBinding
query := dbconn.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, devops.DevOpsProject{})...).
From(devops.DevOpsProjectTableName)
var sqconditions []dbr.Builder
if err := db.Where("workspace = ?", workspace).Find(&workspaceDOPBindings).Error; err != nil {
return nil, err
}
projects, err := kubesphere.Client().ListDevopsProjects(username)
if err != nil {
return nil, err
sqconditions = append(sqconditions, db.Eq(devops.DevOpsProjectWorkSpaceColumn, workspace))
switch username {
case devops.KS_ADMIN:
default:
onCondition := fmt.Sprintf("%s = %s", devops.DevOpsProjectMembershipProjectIdColumn, devops.DevOpsProjectIdColumn)
query.Join(devops.DevOpsProjectMembershipTableName, onCondition)
sqconditions = append(sqconditions, db.Eq(devops.DevOpsProjectMembershipUsernameColumn, username))
sqconditions = append(sqconditions, db.Eq(
devops.DevOpsProjectMembershipTableName+"."+devops.StatusColumn, devops.StatusActive))
}
sqconditions = append(sqconditions, db.Eq(
devops.DevOpsProjectTableName+"."+devops.StatusColumn, devops.StatusActive))
if keyword := conditions.Match["keyword"]; keyword != "" {
for i := 0; i < len(projects); i++ {
if !strings.Contains(projects[i].Name, keyword) {
projects = append(projects[:i], projects[i+1:]...)
i--
}
}
sqconditions = append(sqconditions, db.Like(devops.DevOpsProjectNameColumn, keyword))
}
projects := make([]*devops.DevOpsProject, 0)
sort.Slice(projects, func(i, j int) bool {
if len(sqconditions) > 0 {
query.Where(db.And(sqconditions...))
}
switch orderBy {
case "name":
if reverse {
tmp := i
i = j
j = tmp
query.OrderDesc(devops.DevOpsProjectNameColumn)
} else {
query.OrderAsc(devops.DevOpsProjectNameColumn)
}
switch orderBy {
case "name":
return projects[i].Name > projects[j].Name
default:
return projects[i].CreateTime.Before(*projects[j].CreateTime)
default:
if reverse {
query.OrderAsc(devops.DevOpsProjectCreateTimeColumn)
} else {
query.OrderDesc(devops.DevOpsProjectCreateTimeColumn)
}
})
for i := 0; i < len(projects); i++ {
inWorkspace := false
for _, binding := range workspaceDOPBindings {
if binding.DevOpsProject == projects[i].ProjectId {
inWorkspace = true
}
}
if !inWorkspace {
projects = append(projects[:i], projects[i+1:]...)
i--
}
}
query.Limit(uint64(limit))
query.Offset(uint64(offset))
_, err := query.Load(&projects)
if err != nil {
glog.Errorf("%+v", err)
return nil, err
}
count, err := query.Count()
if err != nil {
glog.Errorf("%+v", err)
return nil, err
}
// limit offset
result := make([]interface{}, 0)
for i, v := range projects {
if len(result) < limit && i >= offset {
result = append(result, v)
for _, v := range projects {
result = append(result, v)
}
return &models.PageableResponse{Items: result, TotalCount: int(count)}, nil
}
func DeleteDevOpsProject(projectId, username string) (error, int) {
err := CheckProjectUserInRole(username, projectId, []string{ProjectOwner})
if err != nil {
glog.Errorf("%+v", err)
return err, http.StatusForbidden
}
gojenkins := admin_jenkins.Client()
devopsdb := devops_mysql.OpenDatabase()
_, err = gojenkins.DeleteJob(projectId)
if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound {
glog.Errorf("%+v", err)
return err, utils.GetJenkinsStatusCode(err)
}
roleNames := make([]string, 0)
for role := range JenkinsProjectPermissionMap {
roleNames = append(roleNames, GetProjectRoleName(projectId, role))
roleNames = append(roleNames, GetPipelineRoleName(projectId, role))
}
err = gojenkins.DeleteProjectRoles(roleNames...)
if err != nil {
glog.Errorf("%+v", err)
return err, utils.GetJenkinsStatusCode(err)
}
_, err = devopsdb.DeleteFrom(devops.DevOpsProjectMembershipTableName).
Where(db.Eq(devops.DevOpsProjectMembershipProjectIdColumn, projectId)).Exec()
if err != nil {
glog.Errorf("%+v", err)
return err, http.StatusInternalServerError
}
_, err = devopsdb.Update(devops.DevOpsProjectTableName).
Set(devops.StatusColumn, devops.StatusDeleted).
Where(db.Eq(devops.DevOpsProjectIdColumn, projectId)).Exec()
if err != nil {
glog.Errorf("%+v", err)
return err, http.StatusInternalServerError
}
project := &devops.DevOpsProject{}
err = devopsdb.Select(devops.DevOpsProjectColumns...).
From(devops.DevOpsProjectTableName).
Where(db.Eq(devops.DevOpsProjectIdColumn, projectId)).
LoadOne(project)
if err != nil {
glog.Errorf("%+v", err)
return err, http.StatusInternalServerError
}
return nil, http.StatusOK
}
func CreateDevopsProject(username string, workspace string, req *devops.DevOpsProject) (*devops.DevOpsProject, error, int) {
jenkinsClient := admin_jenkins.Client()
devopsdb := devops_mysql.OpenDatabase()
project := devops.NewDevOpsProject(req.Name, req.Description, username, req.Extra, workspace)
_, err := jenkinsClient.CreateFolder(project.ProjectId, project.Description)
if err != nil {
glog.Errorf("%+v", err)
return nil, err, utils.GetJenkinsStatusCode(err)
}
var addRoleCh = make(chan *DevOpsProjectRoleResponse, 8)
var addRoleWg sync.WaitGroup
for role, permission := range JenkinsProjectPermissionMap {
addRoleWg.Add(1)
go func(role string, permission gojenkins.ProjectPermissionIds) {
_, err := jenkinsClient.AddProjectRole(GetProjectRoleName(project.ProjectId, role),
GetProjectRolePattern(project.ProjectId), permission, true)
addRoleCh <- &DevOpsProjectRoleResponse{nil, err}
addRoleWg.Done()
}(role, permission)
}
for role, permission := range JenkinsPipelinePermissionMap {
addRoleWg.Add(1)
go func(role string, permission gojenkins.ProjectPermissionIds) {
_, err := jenkinsClient.AddProjectRole(GetPipelineRoleName(project.ProjectId, role),
GetPipelineRolePattern(project.ProjectId), permission, true)
addRoleCh <- &DevOpsProjectRoleResponse{nil, err}
addRoleWg.Done()
}(role, permission)
}
addRoleWg.Wait()
close(addRoleCh)
for addRoleResponse := range addRoleCh {
if addRoleResponse.Err != nil {
glog.Errorf("%+v", addRoleResponse.Err)
return nil, addRoleResponse.Err, utils.GetJenkinsStatusCode(addRoleResponse.Err)
}
}
return &models.PageableResponse{Items: result, TotalCount: len(projects)}, nil
globalRole, err := jenkinsClient.GetGlobalRole(devops.JenkinsAllUserRoleName)
if err != nil {
glog.Errorf("%+v", err)
return nil, err, utils.GetJenkinsStatusCode(err)
}
if globalRole == nil {
_, err := jenkinsClient.AddGlobalRole(devops.JenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{
GlobalRead: true,
}, true)
if err != nil {
glog.Error("failed to create jenkins global role")
return nil, err, utils.GetJenkinsStatusCode(err)
}
}
err = globalRole.AssignRole(username)
if err != nil {
glog.Errorf("%+v", err)
return nil, err, utils.GetJenkinsStatusCode(err)
}
projectRole, err := jenkinsClient.GetProjectRole(GetProjectRoleName(project.ProjectId, ProjectOwner))
if err != nil {
glog.Errorf("%+v", err)
return nil, err, utils.GetJenkinsStatusCode(err)
}
err = projectRole.AssignRole(username)
if err != nil {
glog.Errorf("%+v", err)
return nil, err, utils.GetJenkinsStatusCode(err)
}
pipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(project.ProjectId, ProjectOwner))
if err != nil {
glog.Errorf("%+v", err)
return nil, err, utils.GetJenkinsStatusCode(err)
}
err = pipelineRole.AssignRole(username)
if err != nil {
glog.Errorf("%+v", err)
return nil, err, utils.GetJenkinsStatusCode(err)
}
_, err = devopsdb.InsertInto(devops.DevOpsProjectTableName).
Columns(devops.DevOpsProjectColumns...).Record(project).Exec()
if err != nil {
glog.Errorf("%+v", err)
return nil, err, http.StatusInternalServerError
}
projectMembership := devops.NewDevOpsProjectMemberShip(username, project.ProjectId, ProjectOwner, username)
_, err = devopsdb.InsertInto(devops.DevOpsProjectMembershipTableName).
Columns(devops.DevOpsProjectMembershipColumns...).Record(projectMembership).Exec()
if err != nil {
glog.Errorf("%+v", err)
return nil, err, http.StatusInternalServerError
}
return project, nil, http.StatusOK
}

View File

@@ -21,17 +21,17 @@ import (
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/db"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/devops_mysql"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"strings"
core "k8s.io/api/core/v1"
@@ -43,28 +43,6 @@ import (
"k8s.io/apimachinery/pkg/labels"
)
func UnBindDevopsProject(workspace string, devops string) error {
db := mysql.Client()
return db.Delete(&models.WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error
}
func CreateDevopsProject(username string, workspace string, devops *models.DevopsProject) (*models.DevopsProject, error) {
created, err := kubesphere.Client().CreateDevopsProject(username, devops)
if err != nil {
return nil, err
}
err = BindingDevopsProject(workspace, created.ProjectId)
if err != nil {
return nil, err
}
return created, nil
}
func Namespaces(workspaceName string) ([]*core.Namespace, error) {
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
namespaces, err := namespaceLister.List(labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: workspaceName}))
@@ -86,11 +64,6 @@ func Namespaces(workspaceName string) ([]*core.Namespace, error) {
return out, nil
}
func BindingDevopsProject(workspace string, devops string) error {
db := mysql.Client()
return db.Create(&models.WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error
}
func DeleteNamespace(workspace string, namespaceName string) error {
namespace, err := k8s.Client().CoreV1().Namespaces().Get(namespaceName, meta_v1.GetOptions{})
if err != nil {
@@ -184,18 +157,17 @@ func DeleteWorkspaceRoleBinding(workspace, username string, role string) error {
func GetDevOpsProjects(workspaceName string) ([]string, error) {
db := mysql.Client()
dbconn := devops_mysql.OpenDatabase()
var workspaceDOPBindings []models.WorkspaceDPBinding
if err := db.Where("workspace = ?", workspaceName).Find(&workspaceDOPBindings).Error; err != nil {
return nil, err
}
query := dbconn.Select(devops.DevOpsProjectIdColumn).
From(devops.DevOpsProjectTableName).
Where(db.And(db.Eq(devops.DevOpsProjectWorkSpaceColumn, workspaceName),
db.Eq(devops.StatusColumn, devops.StatusActive)))
devOpsProjects := make([]string, 0)
for _, workspaceDOPBinding := range workspaceDOPBindings {
devOpsProjects = append(devOpsProjects, workspaceDOPBinding.DevOpsProject)
if _, err := query.Load(&devOpsProjects); err != nil {
return nil, err
}
return devOpsProjects, nil
}
@@ -249,12 +221,18 @@ func GetAllProjectNums() (int, error) {
}
func GetAllDevOpsProjectsNums() (int, error) {
db := mysql.Client()
var count int
if err := db.Model(&models.WorkspaceDPBinding{}).Count(&count).Error; err != nil {
dbconn := devops_mysql.OpenDatabase()
query := dbconn.Select(devops.DevOpsProjectIdColumn).
From(devops.DevOpsProjectTableName).
Where(db.Eq(devops.StatusColumn, devops.StatusActive))
devOpsProjects := make([]string, 0)
if _, err := query.Load(&devOpsProjects); err != nil {
return 0, err
}
return count, nil
return len(devOpsProjects), nil
}
func GetAllAccountNums() (int, error) {

View File

@@ -0,0 +1,63 @@
package admin_jenkins
import (
"flag"
"github.com/golang/glog"
"kubesphere.io/kubesphere/pkg/gojenkins"
"sync"
)
var (
jenkinsClientOnce sync.Once
jenkinsClient *gojenkins.Jenkins
jenkinsAdminAddress string
jenkinsAdminUsername string
jenkinsAdminPassword string
jenkinsMaxConn int
)
const (
JenkinsAllUserRoleName = "kubesphere-user"
)
func init() {
flag.StringVar(&jenkinsAdminAddress, "jenkins-address", "http://ks-jenkins.kubesphere-devops-system.svc/", "data source name")
flag.StringVar(&jenkinsAdminUsername, "jenkins-username", "admin", "username of jenkins")
flag.StringVar(&jenkinsAdminPassword, "jenkins-password", "passw0rd", "password of jenkins")
flag.IntVar(&jenkinsMaxConn, "jenkins-max-conn", 20, "max conn to jenkins")
}
func Client() *gojenkins.Jenkins {
jenkinsClientOnce.Do(func() {
jenkins := gojenkins.CreateJenkins(nil, jenkinsAdminAddress, jenkinsMaxConn, jenkinsAdminUsername, jenkinsAdminPassword)
jenkins, err := jenkins.Init()
if err != nil {
glog.Error("failed to connect jenkins")
return
}
jenkinsClient = jenkins
globalRole, err := jenkins.GetGlobalRole(JenkinsAllUserRoleName)
if err != nil {
glog.Error("failed to get jenkins role")
}
if globalRole == nil {
_, err := jenkins.AddGlobalRole(JenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{
GlobalRead: true,
}, true)
if err != nil {
glog.Error("failed to create jenkins global role")
return
}
}
_, err = jenkins.AddProjectRole(JenkinsAllUserRoleName, "\\n\\s*\\r", gojenkins.ProjectPermissionIds{
SCMTag: true,
}, true)
if err != nil {
glog.Error("failed to create jenkins project role")
return
}
})
return jenkinsClient
}

View File

@@ -0,0 +1,56 @@
/*
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 devops_mysql
import (
"flag"
"github.com/gocraft/dbr"
"github.com/golang/glog"
"kubesphere.io/kubesphere/pkg/db"
"sync"
"time"
)
var (
dbClientOnce sync.Once
dsn string
dbClient *db.Database
)
func init() {
flag.StringVar(&dsn, "devops-database-connection", "root@tcp(127.0.0.1:3306)/devops", "data source name")
}
var defaultEventReceiver = db.EventReceiver{}
func OpenDatabase() *db.Database {
dbClientOnce.Do(func() {
conn, err := dbr.Open("mysql", dsn+"?parseTime=1&multiStatements=1&charset=utf8mb4&collation=utf8mb4_unicode_ci", &defaultEventReceiver)
if err != nil {
glog.Fatal(err)
}
conn.SetMaxIdleConns(100)
conn.SetMaxOpenConns(100)
conn.SetConnMaxLifetime(10 * time.Second)
dbClient = &db.Database{
Session: conn.NewSession(nil),
}
err = dbClient.Ping()
if err != nil {
glog.Error(err)
}
})
return dbClient
}

View File

@@ -1,60 +0,0 @@
/*
Copyright 2019 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 mysql
import (
"flag"
"log"
"os"
"os/signal"
"sync"
"syscall"
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
)
var (
dbClientOnce sync.Once
dbClient *gorm.DB
dsn string
)
func init() {
flag.StringVar(&dsn, "database-connection", "root@tcp(localhost:3306)/kubesphere?charset=utf8&parseTime=True", "data source name")
}
func Client() *gorm.DB {
dbClientOnce.Do(func() {
var err error
dbClient, err = gorm.Open("mysql", dsn)
if err != nil {
log.Fatalln(err)
}
c := make(chan os.Signal, 0)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
dbClient.Close()
}()
})
return dbClient
}

View File

@@ -0,0 +1,49 @@
package sonarqube
import (
"flag"
"github.com/golang/glog"
"github.com/kubesphere/sonargo/sonar"
"strings"
"sync"
)
var (
sonarAddress string
sonarToken string
sonarOnce sync.Once
sonarClient *sonargo.Client
)
func init() {
flag.StringVar(&sonarAddress, "sonar-address", "", "sonar server host")
flag.StringVar(&sonarToken, "sonar-token", "", "sonar token")
}
func Client() *sonargo.Client {
sonarOnce.Do(func() {
if sonarAddress == "" {
sonarClient = nil
glog.Info("skip sonar init")
return
}
if !strings.HasSuffix(sonarAddress, "/") {
sonarAddress += "/"
}
client, err := sonargo.NewClientWithToken(sonarAddress+"api/", sonarToken)
if err != nil {
glog.Error("failed to connect to sonar")
return
}
_, _, err = client.Projects.Search(nil)
if err != nil {
glog.Errorf("failed to search sonar projects [%+v]", err)
return
}
glog.Info("init sonar client success")
sonarClient = client
})
return sonarClient
}

View File

@@ -0,0 +1,94 @@
/*
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 idutils
import (
"errors"
"net"
"os"
"github.com/golang/example/stringutil"
"github.com/sony/sonyflake"
hashids "github.com/speps/go-hashids"
"kubesphere.io/kubesphere/pkg/utils/stringutils"
)
var sf *sonyflake.Sonyflake
func init() {
var st sonyflake.Settings
if len(os.Getenv("DEVOPSPHERE_IP")) != 0 {
st.MachineID = machineID
}
sf = sonyflake.NewSonyflake(st)
if sf == nil {
panic("failed to initialize sonyflake")
}
}
func GetIntId() uint64 {
id, err := sf.NextID()
if err != nil {
panic(err)
}
return id
}
// format likes: B6BZVN3mOPvx
func GetUuid(prefix string) string {
id := GetIntId()
hd := hashids.NewData()
h, err := hashids.NewWithData(hd)
if err != nil {
panic(err)
}
i, err := h.Encode([]int{int(id)})
if err != nil {
panic(err)
}
return prefix + stringutils.Reverse(i)
}
const Alphabet36 = "abcdefghijklmnopqrstuvwxyz1234567890"
// format likes: 300m50zn91nwz5
func GetUuid36(prefix string) string {
id := GetIntId()
hd := hashids.NewData()
hd.Alphabet = Alphabet36
h, err := hashids.NewWithData(hd)
if err != nil {
panic(err)
}
i, err := h.Encode([]int{int(id)})
if err != nil {
panic(err)
}
return prefix + stringutil.Reverse(i)
}
func machineID() (uint16, error) {
ipStr := os.Getenv("DEVOPSPHERE_IP")
if len(ipStr) == 0 {
return 0, errors.New("'DEVOPSPHERE_IP' environment variable not set")
}
ip := net.ParseIP(ipStr)
if len(ip) < 4 {
return 0, errors.New("invalid IP")
}
return uint16(ip[2])<<8 + uint16(ip[3]), nil
}

View File

@@ -0,0 +1,24 @@
package idutils
import (
"fmt"
"sort"
"testing"
)
func TestGetUuid(t *testing.T) {
fmt.Println(GetUuid(""))
}
func TestGetUuid36(t *testing.T) {
fmt.Println(GetUuid36(""))
}
func TestGetManyUuid(t *testing.T) {
var strSlice []string
for i := 0; i < 10000; i++ {
testId := GetUuid("")
strSlice = append(strSlice, testId)
}
sort.Strings(strSlice)
}

View File

@@ -0,0 +1,35 @@
/*
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 reflectutils
import "reflect"
func In(value interface{}, container interface{}) bool {
containerValue := reflect.ValueOf(container)
switch reflect.TypeOf(container).Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < containerValue.Len(); i++ {
if containerValue.Index(i).Interface() == value {
return true
}
}
case reflect.Map:
if containerValue.MapIndex(reflect.ValueOf(value)).IsValid() {
return true
}
default:
return false
}
return false
}

View File

@@ -0,0 +1,77 @@
/*
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 stringutils
import (
"unicode/utf8"
"github.com/asaskevich/govalidator"
)
// Creates an slice of slice values not included in the other given slice.
func Diff(base, exclude []string) (result []string) {
excludeMap := make(map[string]bool)
for _, s := range exclude {
excludeMap[s] = true
}
for _, s := range base {
if !excludeMap[s] {
result = append(result, s)
}
}
return result
}
func Unique(ss []string) (result []string) {
smap := make(map[string]bool)
for _, s := range ss {
smap[s] = true
}
for s := range smap {
result = append(result, s)
}
return result
}
func CamelCaseToUnderscore(str string) string {
return govalidator.CamelCaseToUnderscore(str)
}
func UnderscoreToCamelCase(str string) string {
return govalidator.UnderscoreToCamelCase(str)
}
func FindString(array []string, str string) int {
for index, s := range array {
if str == s {
return index
}
}
return -1
}
func StringIn(str string, array []string) bool {
return FindString(array, str) > -1
}
func Reverse(s string) string {
size := len(s)
buf := make([]byte, size)
for start := 0; start < size; {
r, n := utf8.DecodeRuneInString(s[start:])
start += n
utf8.EncodeRune(buf[size-start:], r)
}
return string(buf)
}

21
vendor/github.com/asaskevich/govalidator/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Alex Saskevich
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.

58
vendor/github.com/asaskevich/govalidator/arrays.go generated vendored Normal file
View File

@@ -0,0 +1,58 @@
package govalidator
// Iterator is the function that accepts element of slice/array and its index
type Iterator func(interface{}, int)
// ResultIterator is the function that accepts element of slice/array and its index and returns any result
type ResultIterator func(interface{}, int) interface{}
// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean
type ConditionIterator func(interface{}, int) bool
// Each iterates over the slice and apply Iterator to every item
func Each(array []interface{}, iterator Iterator) {
for index, data := range array {
iterator(data, index)
}
}
// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result.
func Map(array []interface{}, iterator ResultIterator) []interface{} {
var result = make([]interface{}, len(array))
for index, data := range array {
result[index] = iterator(data, index)
}
return result
}
// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise.
func Find(array []interface{}, iterator ConditionIterator) interface{} {
for index, data := range array {
if iterator(data, index) {
return data
}
}
return nil
}
// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice.
func Filter(array []interface{}, iterator ConditionIterator) []interface{} {
var result = make([]interface{}, 0)
for index, data := range array {
if iterator(data, index) {
result = append(result, data)
}
}
return result
}
// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator.
func Count(array []interface{}, iterator ConditionIterator) int {
count := 0
for index, data := range array {
if iterator(data, index) {
count = count + 1
}
}
return count
}

64
vendor/github.com/asaskevich/govalidator/converter.go generated vendored Normal file
View File

@@ -0,0 +1,64 @@
package govalidator
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
)
// ToString convert the input to a string.
func ToString(obj interface{}) string {
res := fmt.Sprintf("%v", obj)
return string(res)
}
// ToJSON convert the input to a valid JSON string
func ToJSON(obj interface{}) (string, error) {
res, err := json.Marshal(obj)
if err != nil {
res = []byte("")
}
return string(res), err
}
// ToFloat convert the input string to a float, or 0.0 if the input is not a float.
func ToFloat(str string) (float64, error) {
res, err := strconv.ParseFloat(str, 64)
if err != nil {
res = 0.0
}
return res, err
}
// ToInt convert the input string or any int type to an integer type 64, or 0 if the input is not an integer.
func ToInt(value interface{}) (res int64, err error) {
val := reflect.ValueOf(value)
switch value.(type) {
case int, int8, int16, int32, int64:
res = val.Int()
case uint, uint8, uint16, uint32, uint64:
res = int64(val.Uint())
case string:
if IsInt(val.String()) {
res, err = strconv.ParseInt(val.String(), 0, 64)
if err != nil {
res = 0
}
} else {
err = fmt.Errorf("math: square root of negative number %g", value)
res = 0
}
default:
err = fmt.Errorf("math: square root of negative number %g", value)
res = 0
}
return
}
// ToBoolean convert the input string to a boolean.
func ToBoolean(str string) (bool, error) {
return strconv.ParseBool(str)
}

36
vendor/github.com/asaskevich/govalidator/error.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
package govalidator
import "strings"
// Errors is an array of multiple errors and conforms to the error interface.
type Errors []error
// Errors returns itself.
func (es Errors) Errors() []error {
return es
}
func (es Errors) Error() string {
var errs []string
for _, e := range es {
errs = append(errs, e.Error())
}
return strings.Join(errs, ";")
}
// Error encapsulates a name, an error and whether there's a custom error message or not.
type Error struct {
Name string
Err error
CustomErrorMessageExists bool
// Validator indicates the name of the validator that failed
Validator string
}
func (e Error) Error() string {
if e.CustomErrorMessageExists {
return e.Err.Error()
}
return e.Name + ": " + e.Err.Error()
}

97
vendor/github.com/asaskevich/govalidator/numerics.go generated vendored Normal file
View File

@@ -0,0 +1,97 @@
package govalidator
import (
"math"
"reflect"
)
// Abs returns absolute value of number
func Abs(value float64) float64 {
return math.Abs(value)
}
// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise
func Sign(value float64) float64 {
if value > 0 {
return 1
} else if value < 0 {
return -1
} else {
return 0
}
}
// IsNegative returns true if value < 0
func IsNegative(value float64) bool {
return value < 0
}
// IsPositive returns true if value > 0
func IsPositive(value float64) bool {
return value > 0
}
// IsNonNegative returns true if value >= 0
func IsNonNegative(value float64) bool {
return value >= 0
}
// IsNonPositive returns true if value <= 0
func IsNonPositive(value float64) bool {
return value <= 0
}
// InRange returns true if value lies between left and right border
func InRangeInt(value, left, right interface{}) bool {
value64, _ := ToInt(value)
left64, _ := ToInt(left)
right64, _ := ToInt(right)
if left64 > right64 {
left64, right64 = right64, left64
}
return value64 >= left64 && value64 <= right64
}
// InRange returns true if value lies between left and right border
func InRangeFloat32(value, left, right float32) bool {
if left > right {
left, right = right, left
}
return value >= left && value <= right
}
// InRange returns true if value lies between left and right border
func InRangeFloat64(value, left, right float64) bool {
if left > right {
left, right = right, left
}
return value >= left && value <= right
}
// InRange returns true if value lies between left and right border, generic type to handle int, float32 or float64, all types must the same type
func InRange(value interface{}, left interface{}, right interface{}) bool {
reflectValue := reflect.TypeOf(value).Kind()
reflectLeft := reflect.TypeOf(left).Kind()
reflectRight := reflect.TypeOf(right).Kind()
if reflectValue == reflect.Int && reflectLeft == reflect.Int && reflectRight == reflect.Int {
return InRangeInt(value.(int), left.(int), right.(int))
} else if reflectValue == reflect.Float32 && reflectLeft == reflect.Float32 && reflectRight == reflect.Float32 {
return InRangeFloat32(value.(float32), left.(float32), right.(float32))
} else if reflectValue == reflect.Float64 && reflectLeft == reflect.Float64 && reflectRight == reflect.Float64 {
return InRangeFloat64(value.(float64), left.(float64), right.(float64))
} else {
return false
}
}
// IsWhole returns true if value is whole number
func IsWhole(value float64) bool {
return math.Remainder(value, 1) == 0
}
// IsNatural returns true if value is natural number (positive and whole)
func IsNatural(value float64) bool {
return IsWhole(value) && IsPositive(value)
}

97
vendor/github.com/asaskevich/govalidator/patterns.go generated vendored Normal file
View File

@@ -0,0 +1,97 @@
package govalidator
import "regexp"
// Basic regular expressions for validating strings
const (
//Email string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$"
ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$"
ISBN13 string = "^(?:[0-9]{13})$"
UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
Alpha string = "^[a-zA-Z]+$"
Alphanumeric string = "^[a-zA-Z0-9]+$"
Numeric string = "^[0-9]+$"
Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$"
Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$"
Hexadecimal string = "^[0-9a-fA-F]+$"
Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
RGBcolor string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$"
ASCII string = "^[\x00-\x7F]+$"
Multibyte string = "[^\x00-\x7F]"
FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
PrintableASCII string = "^[\x20-\x7E]+$"
DataURI string = "^data:.+\\/(.+);base64$"
Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
DNSName string = `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`
IP string = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`
URLSchema string = `((ftp|tcp|udp|wss?|https?):\/\/)`
URLUsername string = `(\S+(:\S*)?@)`
URLPath string = `((\/|\?|#)[^\s]*)`
URLPort string = `(:(\d{1,5}))`
URLIP string = `([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))`
URLSubdomain string = `((www\.)|([a-zA-Z0-9]([-\.][-\._a-zA-Z0-9]+)*))`
URL string = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$`
SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$`
UnixPath string = `^(/[^/\x00]*)+/?$`
Semver string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$"
tagName string = "valid"
hasLowerCase string = ".*[[:lower:]]"
hasUpperCase string = ".*[[:upper:]]"
)
// Used by IsFilePath func
const (
// Unknown is unresolved OS type
Unknown = iota
// Win is Windows type
Win
// Unix is *nix OS types
Unix
)
var (
userRegexp = regexp.MustCompile("^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$")
hostRegexp = regexp.MustCompile("^[^\\s]+\\.[^\\s]+$")
userDotRegexp = regexp.MustCompile("(^[.]{1})|([.]{1}$)|([.]{2,})")
//rxEmail = regexp.MustCompile(Email)
rxCreditCard = regexp.MustCompile(CreditCard)
rxISBN10 = regexp.MustCompile(ISBN10)
rxISBN13 = regexp.MustCompile(ISBN13)
rxUUID3 = regexp.MustCompile(UUID3)
rxUUID4 = regexp.MustCompile(UUID4)
rxUUID5 = regexp.MustCompile(UUID5)
rxUUID = regexp.MustCompile(UUID)
rxAlpha = regexp.MustCompile(Alpha)
rxAlphanumeric = regexp.MustCompile(Alphanumeric)
rxNumeric = regexp.MustCompile(Numeric)
rxInt = regexp.MustCompile(Int)
rxFloat = regexp.MustCompile(Float)
rxHexadecimal = regexp.MustCompile(Hexadecimal)
rxHexcolor = regexp.MustCompile(Hexcolor)
rxRGBcolor = regexp.MustCompile(RGBcolor)
rxASCII = regexp.MustCompile(ASCII)
rxPrintableASCII = regexp.MustCompile(PrintableASCII)
rxMultibyte = regexp.MustCompile(Multibyte)
rxFullWidth = regexp.MustCompile(FullWidth)
rxHalfWidth = regexp.MustCompile(HalfWidth)
rxBase64 = regexp.MustCompile(Base64)
rxDataURI = regexp.MustCompile(DataURI)
rxLatitude = regexp.MustCompile(Latitude)
rxLongitude = regexp.MustCompile(Longitude)
rxDNSName = regexp.MustCompile(DNSName)
rxURL = regexp.MustCompile(URL)
rxSSN = regexp.MustCompile(SSN)
rxWinPath = regexp.MustCompile(WinPath)
rxUnixPath = regexp.MustCompile(UnixPath)
rxSemver = regexp.MustCompile(Semver)
rxHasLowerCase = regexp.MustCompile(hasLowerCase)
rxHasUpperCase = regexp.MustCompile(hasUpperCase)
)

616
vendor/github.com/asaskevich/govalidator/types.go generated vendored Normal file
View File

@@ -0,0 +1,616 @@
package govalidator
import (
"reflect"
"regexp"
"sync"
)
// Validator is a wrapper for a validator function that returns bool and accepts string.
type Validator func(str string) bool
// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type.
// The second parameter should be the context (in the case of validating a struct: the whole object being validated).
type CustomTypeValidator func(i interface{}, o interface{}) bool
// ParamValidator is a wrapper for validator functions that accepts additional parameters.
type ParamValidator func(str string, params ...string) bool
type tagOptionsMap map[string]string
// UnsupportedTypeError is a wrapper for reflect.Type
type UnsupportedTypeError struct {
Type reflect.Type
}
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
// It implements the methods to sort by string.
type stringValues []reflect.Value
// ParamTagMap is a map of functions accept variants parameters
var ParamTagMap = map[string]ParamValidator{
"length": ByteLength,
"range": Range,
"runelength": RuneLength,
"stringlength": StringLength,
"matches": StringMatches,
"in": isInRaw,
"rsapub": IsRsaPub,
}
// ParamTagRegexMap maps param tags to their respective regexes.
var ParamTagRegexMap = map[string]*regexp.Regexp{
"range": regexp.MustCompile("^range\\((\\d+)\\|(\\d+)\\)$"),
"length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"),
"runelength": regexp.MustCompile("^runelength\\((\\d+)\\|(\\d+)\\)$"),
"stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"),
"in": regexp.MustCompile(`^in\((.*)\)`),
"matches": regexp.MustCompile(`^matches\((.+)\)$`),
"rsapub": regexp.MustCompile("^rsapub\\((\\d+)\\)$"),
}
type customTypeTagMap struct {
validators map[string]CustomTypeValidator
sync.RWMutex
}
func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) {
tm.RLock()
defer tm.RUnlock()
v, ok := tm.validators[name]
return v, ok
}
func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) {
tm.Lock()
defer tm.Unlock()
tm.validators[name] = ctv
}
// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function.
// Use this to validate compound or custom types that need to be handled as a whole, e.g.
// `type UUID [16]byte` (this would be handled as an array of bytes).
var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)}
// TagMap is a map of functions, that can be used as tags for ValidateStruct function.
var TagMap = map[string]Validator{
"email": IsEmail,
"url": IsURL,
"dialstring": IsDialString,
"requrl": IsRequestURL,
"requri": IsRequestURI,
"alpha": IsAlpha,
"utfletter": IsUTFLetter,
"alphanum": IsAlphanumeric,
"utfletternum": IsUTFLetterNumeric,
"numeric": IsNumeric,
"utfnumeric": IsUTFNumeric,
"utfdigit": IsUTFDigit,
"hexadecimal": IsHexadecimal,
"hexcolor": IsHexcolor,
"rgbcolor": IsRGBcolor,
"lowercase": IsLowerCase,
"uppercase": IsUpperCase,
"int": IsInt,
"float": IsFloat,
"null": IsNull,
"uuid": IsUUID,
"uuidv3": IsUUIDv3,
"uuidv4": IsUUIDv4,
"uuidv5": IsUUIDv5,
"creditcard": IsCreditCard,
"isbn10": IsISBN10,
"isbn13": IsISBN13,
"json": IsJSON,
"multibyte": IsMultibyte,
"ascii": IsASCII,
"printableascii": IsPrintableASCII,
"fullwidth": IsFullWidth,
"halfwidth": IsHalfWidth,
"variablewidth": IsVariableWidth,
"base64": IsBase64,
"datauri": IsDataURI,
"ip": IsIP,
"port": IsPort,
"ipv4": IsIPv4,
"ipv6": IsIPv6,
"dns": IsDNSName,
"host": IsHost,
"mac": IsMAC,
"latitude": IsLatitude,
"longitude": IsLongitude,
"ssn": IsSSN,
"semver": IsSemver,
"rfc3339": IsRFC3339,
"rfc3339WithoutZone": IsRFC3339WithoutZone,
"ISO3166Alpha2": IsISO3166Alpha2,
"ISO3166Alpha3": IsISO3166Alpha3,
"ISO4217": IsISO4217,
}
// ISO3166Entry stores country codes
type ISO3166Entry struct {
EnglishShortName string
FrenchShortName string
Alpha2Code string
Alpha3Code string
Numeric string
}
//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes"
var ISO3166List = []ISO3166Entry{
{"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"},
{"Albania", "Albanie (l')", "AL", "ALB", "008"},
{"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"},
{"Algeria", "Algérie (l')", "DZ", "DZA", "012"},
{"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"},
{"Andorra", "Andorre (l')", "AD", "AND", "020"},
{"Angola", "Angola (l')", "AO", "AGO", "024"},
{"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"},
{"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"},
{"Argentina", "Argentine (l')", "AR", "ARG", "032"},
{"Australia", "Australie (l')", "AU", "AUS", "036"},
{"Austria", "Autriche (l')", "AT", "AUT", "040"},
{"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"},
{"Bahrain", "Bahreïn", "BH", "BHR", "048"},
{"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"},
{"Armenia", "Arménie (l')", "AM", "ARM", "051"},
{"Barbados", "Barbade (la)", "BB", "BRB", "052"},
{"Belgium", "Belgique (la)", "BE", "BEL", "056"},
{"Bermuda", "Bermudes (les)", "BM", "BMU", "060"},
{"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"},
{"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"},
{"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"},
{"Botswana", "Botswana (le)", "BW", "BWA", "072"},
{"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"},
{"Brazil", "Brésil (le)", "BR", "BRA", "076"},
{"Belize", "Belize (le)", "BZ", "BLZ", "084"},
{"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"},
{"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"},
{"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"},
{"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"},
{"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"},
{"Myanmar", "Myanmar (le)", "MM", "MMR", "104"},
{"Burundi", "Burundi (le)", "BI", "BDI", "108"},
{"Belarus", "Bélarus (le)", "BY", "BLR", "112"},
{"Cambodia", "Cambodge (le)", "KH", "KHM", "116"},
{"Cameroon", "Cameroun (le)", "CM", "CMR", "120"},
{"Canada", "Canada (le)", "CA", "CAN", "124"},
{"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"},
{"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"},
{"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"},
{"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"},
{"Chad", "Tchad (le)", "TD", "TCD", "148"},
{"Chile", "Chili (le)", "CL", "CHL", "152"},
{"China", "Chine (la)", "CN", "CHN", "156"},
{"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"},
{"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"},
{"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"},
{"Colombia", "Colombie (la)", "CO", "COL", "170"},
{"Comoros (the)", "Comores (les)", "KM", "COM", "174"},
{"Mayotte", "Mayotte", "YT", "MYT", "175"},
{"Congo (the)", "Congo (le)", "CG", "COG", "178"},
{"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"},
{"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"},
{"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"},
{"Croatia", "Croatie (la)", "HR", "HRV", "191"},
{"Cuba", "Cuba", "CU", "CUB", "192"},
{"Cyprus", "Chypre", "CY", "CYP", "196"},
{"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"},
{"Benin", "Bénin (le)", "BJ", "BEN", "204"},
{"Denmark", "Danemark (le)", "DK", "DNK", "208"},
{"Dominica", "Dominique (la)", "DM", "DMA", "212"},
{"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"},
{"Ecuador", "Équateur (l')", "EC", "ECU", "218"},
{"El Salvador", "El Salvador", "SV", "SLV", "222"},
{"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"},
{"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"},
{"Eritrea", "Érythrée (l')", "ER", "ERI", "232"},
{"Estonia", "Estonie (l')", "EE", "EST", "233"},
{"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"},
{"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"},
{"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"},
{"Fiji", "Fidji (les)", "FJ", "FJI", "242"},
{"Finland", "Finlande (la)", "FI", "FIN", "246"},
{"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"},
{"France", "France (la)", "FR", "FRA", "250"},
{"French Guiana", "Guyane française (la )", "GF", "GUF", "254"},
{"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"},
{"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"},
{"Djibouti", "Djibouti", "DJ", "DJI", "262"},
{"Gabon", "Gabon (le)", "GA", "GAB", "266"},
{"Georgia", "Géorgie (la)", "GE", "GEO", "268"},
{"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"},
{"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"},
{"Germany", "Allemagne (l')", "DE", "DEU", "276"},
{"Ghana", "Ghana (le)", "GH", "GHA", "288"},
{"Gibraltar", "Gibraltar", "GI", "GIB", "292"},
{"Kiribati", "Kiribati", "KI", "KIR", "296"},
{"Greece", "Grèce (la)", "GR", "GRC", "300"},
{"Greenland", "Groenland (le)", "GL", "GRL", "304"},
{"Grenada", "Grenade (la)", "GD", "GRD", "308"},
{"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"},
{"Guam", "Guam", "GU", "GUM", "316"},
{"Guatemala", "Guatemala (le)", "GT", "GTM", "320"},
{"Guinea", "Guinée (la)", "GN", "GIN", "324"},
{"Guyana", "Guyana (le)", "GY", "GUY", "328"},
{"Haiti", "Haïti", "HT", "HTI", "332"},
{"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"},
{"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"},
{"Honduras", "Honduras (le)", "HN", "HND", "340"},
{"Hong Kong", "Hong Kong", "HK", "HKG", "344"},
{"Hungary", "Hongrie (la)", "HU", "HUN", "348"},
{"Iceland", "Islande (l')", "IS", "ISL", "352"},
{"India", "Inde (l')", "IN", "IND", "356"},
{"Indonesia", "Indonésie (l')", "ID", "IDN", "360"},
{"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"},
{"Iraq", "Iraq (l')", "IQ", "IRQ", "368"},
{"Ireland", "Irlande (l')", "IE", "IRL", "372"},
{"Israel", "Israël", "IL", "ISR", "376"},
{"Italy", "Italie (l')", "IT", "ITA", "380"},
{"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"},
{"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"},
{"Japan", "Japon (le)", "JP", "JPN", "392"},
{"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"},
{"Jordan", "Jordanie (la)", "JO", "JOR", "400"},
{"Kenya", "Kenya (le)", "KE", "KEN", "404"},
{"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"},
{"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"},
{"Kuwait", "Koweït (le)", "KW", "KWT", "414"},
{"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"},
{"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"},
{"Lebanon", "Liban (le)", "LB", "LBN", "422"},
{"Lesotho", "Lesotho (le)", "LS", "LSO", "426"},
{"Latvia", "Lettonie (la)", "LV", "LVA", "428"},
{"Liberia", "Libéria (le)", "LR", "LBR", "430"},
{"Libya", "Libye (la)", "LY", "LBY", "434"},
{"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"},
{"Lithuania", "Lituanie (la)", "LT", "LTU", "440"},
{"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"},
{"Macao", "Macao", "MO", "MAC", "446"},
{"Madagascar", "Madagascar", "MG", "MDG", "450"},
{"Malawi", "Malawi (le)", "MW", "MWI", "454"},
{"Malaysia", "Malaisie (la)", "MY", "MYS", "458"},
{"Maldives", "Maldives (les)", "MV", "MDV", "462"},
{"Mali", "Mali (le)", "ML", "MLI", "466"},
{"Malta", "Malte", "MT", "MLT", "470"},
{"Martinique", "Martinique (la)", "MQ", "MTQ", "474"},
{"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"},
{"Mauritius", "Maurice", "MU", "MUS", "480"},
{"Mexico", "Mexique (le)", "MX", "MEX", "484"},
{"Monaco", "Monaco", "MC", "MCO", "492"},
{"Mongolia", "Mongolie (la)", "MN", "MNG", "496"},
{"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"},
{"Montenegro", "Monténégro (le)", "ME", "MNE", "499"},
{"Montserrat", "Montserrat", "MS", "MSR", "500"},
{"Morocco", "Maroc (le)", "MA", "MAR", "504"},
{"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"},
{"Oman", "Oman", "OM", "OMN", "512"},
{"Namibia", "Namibie (la)", "NA", "NAM", "516"},
{"Nauru", "Nauru", "NR", "NRU", "520"},
{"Nepal", "Népal (le)", "NP", "NPL", "524"},
{"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"},
{"Curaçao", "Curaçao", "CW", "CUW", "531"},
{"Aruba", "Aruba", "AW", "ABW", "533"},
{"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"},
{"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"},
{"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"},
{"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"},
{"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"},
{"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"},
{"Niger (the)", "Niger (le)", "NE", "NER", "562"},
{"Nigeria", "Nigéria (le)", "NG", "NGA", "566"},
{"Niue", "Niue", "NU", "NIU", "570"},
{"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"},
{"Norway", "Norvège (la)", "NO", "NOR", "578"},
{"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"},
{"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"},
{"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"},
{"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"},
{"Palau", "Palaos (les)", "PW", "PLW", "585"},
{"Pakistan", "Pakistan (le)", "PK", "PAK", "586"},
{"Panama", "Panama (le)", "PA", "PAN", "591"},
{"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"},
{"Paraguay", "Paraguay (le)", "PY", "PRY", "600"},
{"Peru", "Pérou (le)", "PE", "PER", "604"},
{"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"},
{"Pitcairn", "Pitcairn", "PN", "PCN", "612"},
{"Poland", "Pologne (la)", "PL", "POL", "616"},
{"Portugal", "Portugal (le)", "PT", "PRT", "620"},
{"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"},
{"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"},
{"Puerto Rico", "Porto Rico", "PR", "PRI", "630"},
{"Qatar", "Qatar (le)", "QA", "QAT", "634"},
{"Réunion", "Réunion (La)", "RE", "REU", "638"},
{"Romania", "Roumanie (la)", "RO", "ROU", "642"},
{"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"},
{"Rwanda", "Rwanda (le)", "RW", "RWA", "646"},
{"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"},
{"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"},
{"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"},
{"Anguilla", "Anguilla", "AI", "AIA", "660"},
{"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"},
{"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"},
{"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"},
{"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"},
{"San Marino", "Saint-Marin", "SM", "SMR", "674"},
{"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"},
{"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"},
{"Senegal", "Sénégal (le)", "SN", "SEN", "686"},
{"Serbia", "Serbie (la)", "RS", "SRB", "688"},
{"Seychelles", "Seychelles (les)", "SC", "SYC", "690"},
{"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"},
{"Singapore", "Singapour", "SG", "SGP", "702"},
{"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"},
{"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"},
{"Slovenia", "Slovénie (la)", "SI", "SVN", "705"},
{"Somalia", "Somalie (la)", "SO", "SOM", "706"},
{"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"},
{"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"},
{"Spain", "Espagne (l')", "ES", "ESP", "724"},
{"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"},
{"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"},
{"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"},
{"Suriname", "Suriname (le)", "SR", "SUR", "740"},
{"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"},
{"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"},
{"Sweden", "Suède (la)", "SE", "SWE", "752"},
{"Switzerland", "Suisse (la)", "CH", "CHE", "756"},
{"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"},
{"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"},
{"Thailand", "Thaïlande (la)", "TH", "THA", "764"},
{"Togo", "Togo (le)", "TG", "TGO", "768"},
{"Tokelau", "Tokelau (les)", "TK", "TKL", "772"},
{"Tonga", "Tonga (les)", "TO", "TON", "776"},
{"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"},
{"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"},
{"Tunisia", "Tunisie (la)", "TN", "TUN", "788"},
{"Turkey", "Turquie (la)", "TR", "TUR", "792"},
{"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"},
{"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"},
{"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"},
{"Uganda", "Ouganda (l')", "UG", "UGA", "800"},
{"Ukraine", "Ukraine (l')", "UA", "UKR", "804"},
{"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'exRépublique yougoslave de)", "MK", "MKD", "807"},
{"Egypt", "Égypte (l')", "EG", "EGY", "818"},
{"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"},
{"Guernsey", "Guernesey", "GG", "GGY", "831"},
{"Jersey", "Jersey", "JE", "JEY", "832"},
{"Isle of Man", "Île de Man", "IM", "IMN", "833"},
{"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"},
{"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"},
{"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"},
{"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"},
{"Uruguay", "Uruguay (l')", "UY", "URY", "858"},
{"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"},
{"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"},
{"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"},
{"Samoa", "Samoa (le)", "WS", "WSM", "882"},
{"Yemen", "Yémen (le)", "YE", "YEM", "887"},
{"Zambia", "Zambie (la)", "ZM", "ZMB", "894"},
}
// ISO4217List is the list of ISO currency codes
var ISO4217List = []string{
"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN",
"BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD",
"CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP", "CVE", "CZK",
"DJF", "DKK", "DOP", "DZD",
"EGP", "ERN", "ETB", "EUR",
"FJD", "FKP",
"GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD",
"HKD", "HNL", "HRK", "HTG", "HUF",
"IDR", "ILS", "INR", "IQD", "IRR", "ISK",
"JMD", "JOD", "JPY",
"KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT",
"LAK", "LBP", "LKR", "LRD", "LSL", "LYD",
"MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN",
"NAD", "NGN", "NIO", "NOK", "NPR", "NZD",
"OMR",
"PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG",
"QAR",
"RON", "RSD", "RUB", "RWF",
"SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "SVC", "SYP", "SZL",
"THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS",
"UAH", "UGX", "USD", "USN", "UYI", "UYU", "UZS",
"VEF", "VND", "VUV",
"WST",
"XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX",
"YER",
"ZAR", "ZMW", "ZWL",
}
// ISO693Entry stores ISO language codes
type ISO693Entry struct {
Alpha3bCode string
Alpha2Code string
English string
}
//ISO693List based on http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json
var ISO693List = []ISO693Entry{
{Alpha3bCode: "aar", Alpha2Code: "aa", English: "Afar"},
{Alpha3bCode: "abk", Alpha2Code: "ab", English: "Abkhazian"},
{Alpha3bCode: "afr", Alpha2Code: "af", English: "Afrikaans"},
{Alpha3bCode: "aka", Alpha2Code: "ak", English: "Akan"},
{Alpha3bCode: "alb", Alpha2Code: "sq", English: "Albanian"},
{Alpha3bCode: "amh", Alpha2Code: "am", English: "Amharic"},
{Alpha3bCode: "ara", Alpha2Code: "ar", English: "Arabic"},
{Alpha3bCode: "arg", Alpha2Code: "an", English: "Aragonese"},
{Alpha3bCode: "arm", Alpha2Code: "hy", English: "Armenian"},
{Alpha3bCode: "asm", Alpha2Code: "as", English: "Assamese"},
{Alpha3bCode: "ava", Alpha2Code: "av", English: "Avaric"},
{Alpha3bCode: "ave", Alpha2Code: "ae", English: "Avestan"},
{Alpha3bCode: "aym", Alpha2Code: "ay", English: "Aymara"},
{Alpha3bCode: "aze", Alpha2Code: "az", English: "Azerbaijani"},
{Alpha3bCode: "bak", Alpha2Code: "ba", English: "Bashkir"},
{Alpha3bCode: "bam", Alpha2Code: "bm", English: "Bambara"},
{Alpha3bCode: "baq", Alpha2Code: "eu", English: "Basque"},
{Alpha3bCode: "bel", Alpha2Code: "be", English: "Belarusian"},
{Alpha3bCode: "ben", Alpha2Code: "bn", English: "Bengali"},
{Alpha3bCode: "bih", Alpha2Code: "bh", English: "Bihari languages"},
{Alpha3bCode: "bis", Alpha2Code: "bi", English: "Bislama"},
{Alpha3bCode: "bos", Alpha2Code: "bs", English: "Bosnian"},
{Alpha3bCode: "bre", Alpha2Code: "br", English: "Breton"},
{Alpha3bCode: "bul", Alpha2Code: "bg", English: "Bulgarian"},
{Alpha3bCode: "bur", Alpha2Code: "my", English: "Burmese"},
{Alpha3bCode: "cat", Alpha2Code: "ca", English: "Catalan; Valencian"},
{Alpha3bCode: "cha", Alpha2Code: "ch", English: "Chamorro"},
{Alpha3bCode: "che", Alpha2Code: "ce", English: "Chechen"},
{Alpha3bCode: "chi", Alpha2Code: "zh", English: "Chinese"},
{Alpha3bCode: "chu", Alpha2Code: "cu", English: "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic"},
{Alpha3bCode: "chv", Alpha2Code: "cv", English: "Chuvash"},
{Alpha3bCode: "cor", Alpha2Code: "kw", English: "Cornish"},
{Alpha3bCode: "cos", Alpha2Code: "co", English: "Corsican"},
{Alpha3bCode: "cre", Alpha2Code: "cr", English: "Cree"},
{Alpha3bCode: "cze", Alpha2Code: "cs", English: "Czech"},
{Alpha3bCode: "dan", Alpha2Code: "da", English: "Danish"},
{Alpha3bCode: "div", Alpha2Code: "dv", English: "Divehi; Dhivehi; Maldivian"},
{Alpha3bCode: "dut", Alpha2Code: "nl", English: "Dutch; Flemish"},
{Alpha3bCode: "dzo", Alpha2Code: "dz", English: "Dzongkha"},
{Alpha3bCode: "eng", Alpha2Code: "en", English: "English"},
{Alpha3bCode: "epo", Alpha2Code: "eo", English: "Esperanto"},
{Alpha3bCode: "est", Alpha2Code: "et", English: "Estonian"},
{Alpha3bCode: "ewe", Alpha2Code: "ee", English: "Ewe"},
{Alpha3bCode: "fao", Alpha2Code: "fo", English: "Faroese"},
{Alpha3bCode: "fij", Alpha2Code: "fj", English: "Fijian"},
{Alpha3bCode: "fin", Alpha2Code: "fi", English: "Finnish"},
{Alpha3bCode: "fre", Alpha2Code: "fr", English: "French"},
{Alpha3bCode: "fry", Alpha2Code: "fy", English: "Western Frisian"},
{Alpha3bCode: "ful", Alpha2Code: "ff", English: "Fulah"},
{Alpha3bCode: "geo", Alpha2Code: "ka", English: "Georgian"},
{Alpha3bCode: "ger", Alpha2Code: "de", English: "German"},
{Alpha3bCode: "gla", Alpha2Code: "gd", English: "Gaelic; Scottish Gaelic"},
{Alpha3bCode: "gle", Alpha2Code: "ga", English: "Irish"},
{Alpha3bCode: "glg", Alpha2Code: "gl", English: "Galician"},
{Alpha3bCode: "glv", Alpha2Code: "gv", English: "Manx"},
{Alpha3bCode: "gre", Alpha2Code: "el", English: "Greek, Modern (1453-)"},
{Alpha3bCode: "grn", Alpha2Code: "gn", English: "Guarani"},
{Alpha3bCode: "guj", Alpha2Code: "gu", English: "Gujarati"},
{Alpha3bCode: "hat", Alpha2Code: "ht", English: "Haitian; Haitian Creole"},
{Alpha3bCode: "hau", Alpha2Code: "ha", English: "Hausa"},
{Alpha3bCode: "heb", Alpha2Code: "he", English: "Hebrew"},
{Alpha3bCode: "her", Alpha2Code: "hz", English: "Herero"},
{Alpha3bCode: "hin", Alpha2Code: "hi", English: "Hindi"},
{Alpha3bCode: "hmo", Alpha2Code: "ho", English: "Hiri Motu"},
{Alpha3bCode: "hrv", Alpha2Code: "hr", English: "Croatian"},
{Alpha3bCode: "hun", Alpha2Code: "hu", English: "Hungarian"},
{Alpha3bCode: "ibo", Alpha2Code: "ig", English: "Igbo"},
{Alpha3bCode: "ice", Alpha2Code: "is", English: "Icelandic"},
{Alpha3bCode: "ido", Alpha2Code: "io", English: "Ido"},
{Alpha3bCode: "iii", Alpha2Code: "ii", English: "Sichuan Yi; Nuosu"},
{Alpha3bCode: "iku", Alpha2Code: "iu", English: "Inuktitut"},
{Alpha3bCode: "ile", Alpha2Code: "ie", English: "Interlingue; Occidental"},
{Alpha3bCode: "ina", Alpha2Code: "ia", English: "Interlingua (International Auxiliary Language Association)"},
{Alpha3bCode: "ind", Alpha2Code: "id", English: "Indonesian"},
{Alpha3bCode: "ipk", Alpha2Code: "ik", English: "Inupiaq"},
{Alpha3bCode: "ita", Alpha2Code: "it", English: "Italian"},
{Alpha3bCode: "jav", Alpha2Code: "jv", English: "Javanese"},
{Alpha3bCode: "jpn", Alpha2Code: "ja", English: "Japanese"},
{Alpha3bCode: "kal", Alpha2Code: "kl", English: "Kalaallisut; Greenlandic"},
{Alpha3bCode: "kan", Alpha2Code: "kn", English: "Kannada"},
{Alpha3bCode: "kas", Alpha2Code: "ks", English: "Kashmiri"},
{Alpha3bCode: "kau", Alpha2Code: "kr", English: "Kanuri"},
{Alpha3bCode: "kaz", Alpha2Code: "kk", English: "Kazakh"},
{Alpha3bCode: "khm", Alpha2Code: "km", English: "Central Khmer"},
{Alpha3bCode: "kik", Alpha2Code: "ki", English: "Kikuyu; Gikuyu"},
{Alpha3bCode: "kin", Alpha2Code: "rw", English: "Kinyarwanda"},
{Alpha3bCode: "kir", Alpha2Code: "ky", English: "Kirghiz; Kyrgyz"},
{Alpha3bCode: "kom", Alpha2Code: "kv", English: "Komi"},
{Alpha3bCode: "kon", Alpha2Code: "kg", English: "Kongo"},
{Alpha3bCode: "kor", Alpha2Code: "ko", English: "Korean"},
{Alpha3bCode: "kua", Alpha2Code: "kj", English: "Kuanyama; Kwanyama"},
{Alpha3bCode: "kur", Alpha2Code: "ku", English: "Kurdish"},
{Alpha3bCode: "lao", Alpha2Code: "lo", English: "Lao"},
{Alpha3bCode: "lat", Alpha2Code: "la", English: "Latin"},
{Alpha3bCode: "lav", Alpha2Code: "lv", English: "Latvian"},
{Alpha3bCode: "lim", Alpha2Code: "li", English: "Limburgan; Limburger; Limburgish"},
{Alpha3bCode: "lin", Alpha2Code: "ln", English: "Lingala"},
{Alpha3bCode: "lit", Alpha2Code: "lt", English: "Lithuanian"},
{Alpha3bCode: "ltz", Alpha2Code: "lb", English: "Luxembourgish; Letzeburgesch"},
{Alpha3bCode: "lub", Alpha2Code: "lu", English: "Luba-Katanga"},
{Alpha3bCode: "lug", Alpha2Code: "lg", English: "Ganda"},
{Alpha3bCode: "mac", Alpha2Code: "mk", English: "Macedonian"},
{Alpha3bCode: "mah", Alpha2Code: "mh", English: "Marshallese"},
{Alpha3bCode: "mal", Alpha2Code: "ml", English: "Malayalam"},
{Alpha3bCode: "mao", Alpha2Code: "mi", English: "Maori"},
{Alpha3bCode: "mar", Alpha2Code: "mr", English: "Marathi"},
{Alpha3bCode: "may", Alpha2Code: "ms", English: "Malay"},
{Alpha3bCode: "mlg", Alpha2Code: "mg", English: "Malagasy"},
{Alpha3bCode: "mlt", Alpha2Code: "mt", English: "Maltese"},
{Alpha3bCode: "mon", Alpha2Code: "mn", English: "Mongolian"},
{Alpha3bCode: "nau", Alpha2Code: "na", English: "Nauru"},
{Alpha3bCode: "nav", Alpha2Code: "nv", English: "Navajo; Navaho"},
{Alpha3bCode: "nbl", Alpha2Code: "nr", English: "Ndebele, South; South Ndebele"},
{Alpha3bCode: "nde", Alpha2Code: "nd", English: "Ndebele, North; North Ndebele"},
{Alpha3bCode: "ndo", Alpha2Code: "ng", English: "Ndonga"},
{Alpha3bCode: "nep", Alpha2Code: "ne", English: "Nepali"},
{Alpha3bCode: "nno", Alpha2Code: "nn", English: "Norwegian Nynorsk; Nynorsk, Norwegian"},
{Alpha3bCode: "nob", Alpha2Code: "nb", English: "Bokmål, Norwegian; Norwegian Bokmål"},
{Alpha3bCode: "nor", Alpha2Code: "no", English: "Norwegian"},
{Alpha3bCode: "nya", Alpha2Code: "ny", English: "Chichewa; Chewa; Nyanja"},
{Alpha3bCode: "oci", Alpha2Code: "oc", English: "Occitan (post 1500); Provençal"},
{Alpha3bCode: "oji", Alpha2Code: "oj", English: "Ojibwa"},
{Alpha3bCode: "ori", Alpha2Code: "or", English: "Oriya"},
{Alpha3bCode: "orm", Alpha2Code: "om", English: "Oromo"},
{Alpha3bCode: "oss", Alpha2Code: "os", English: "Ossetian; Ossetic"},
{Alpha3bCode: "pan", Alpha2Code: "pa", English: "Panjabi; Punjabi"},
{Alpha3bCode: "per", Alpha2Code: "fa", English: "Persian"},
{Alpha3bCode: "pli", Alpha2Code: "pi", English: "Pali"},
{Alpha3bCode: "pol", Alpha2Code: "pl", English: "Polish"},
{Alpha3bCode: "por", Alpha2Code: "pt", English: "Portuguese"},
{Alpha3bCode: "pus", Alpha2Code: "ps", English: "Pushto; Pashto"},
{Alpha3bCode: "que", Alpha2Code: "qu", English: "Quechua"},
{Alpha3bCode: "roh", Alpha2Code: "rm", English: "Romansh"},
{Alpha3bCode: "rum", Alpha2Code: "ro", English: "Romanian; Moldavian; Moldovan"},
{Alpha3bCode: "run", Alpha2Code: "rn", English: "Rundi"},
{Alpha3bCode: "rus", Alpha2Code: "ru", English: "Russian"},
{Alpha3bCode: "sag", Alpha2Code: "sg", English: "Sango"},
{Alpha3bCode: "san", Alpha2Code: "sa", English: "Sanskrit"},
{Alpha3bCode: "sin", Alpha2Code: "si", English: "Sinhala; Sinhalese"},
{Alpha3bCode: "slo", Alpha2Code: "sk", English: "Slovak"},
{Alpha3bCode: "slv", Alpha2Code: "sl", English: "Slovenian"},
{Alpha3bCode: "sme", Alpha2Code: "se", English: "Northern Sami"},
{Alpha3bCode: "smo", Alpha2Code: "sm", English: "Samoan"},
{Alpha3bCode: "sna", Alpha2Code: "sn", English: "Shona"},
{Alpha3bCode: "snd", Alpha2Code: "sd", English: "Sindhi"},
{Alpha3bCode: "som", Alpha2Code: "so", English: "Somali"},
{Alpha3bCode: "sot", Alpha2Code: "st", English: "Sotho, Southern"},
{Alpha3bCode: "spa", Alpha2Code: "es", English: "Spanish; Castilian"},
{Alpha3bCode: "srd", Alpha2Code: "sc", English: "Sardinian"},
{Alpha3bCode: "srp", Alpha2Code: "sr", English: "Serbian"},
{Alpha3bCode: "ssw", Alpha2Code: "ss", English: "Swati"},
{Alpha3bCode: "sun", Alpha2Code: "su", English: "Sundanese"},
{Alpha3bCode: "swa", Alpha2Code: "sw", English: "Swahili"},
{Alpha3bCode: "swe", Alpha2Code: "sv", English: "Swedish"},
{Alpha3bCode: "tah", Alpha2Code: "ty", English: "Tahitian"},
{Alpha3bCode: "tam", Alpha2Code: "ta", English: "Tamil"},
{Alpha3bCode: "tat", Alpha2Code: "tt", English: "Tatar"},
{Alpha3bCode: "tel", Alpha2Code: "te", English: "Telugu"},
{Alpha3bCode: "tgk", Alpha2Code: "tg", English: "Tajik"},
{Alpha3bCode: "tgl", Alpha2Code: "tl", English: "Tagalog"},
{Alpha3bCode: "tha", Alpha2Code: "th", English: "Thai"},
{Alpha3bCode: "tib", Alpha2Code: "bo", English: "Tibetan"},
{Alpha3bCode: "tir", Alpha2Code: "ti", English: "Tigrinya"},
{Alpha3bCode: "ton", Alpha2Code: "to", English: "Tonga (Tonga Islands)"},
{Alpha3bCode: "tsn", Alpha2Code: "tn", English: "Tswana"},
{Alpha3bCode: "tso", Alpha2Code: "ts", English: "Tsonga"},
{Alpha3bCode: "tuk", Alpha2Code: "tk", English: "Turkmen"},
{Alpha3bCode: "tur", Alpha2Code: "tr", English: "Turkish"},
{Alpha3bCode: "twi", Alpha2Code: "tw", English: "Twi"},
{Alpha3bCode: "uig", Alpha2Code: "ug", English: "Uighur; Uyghur"},
{Alpha3bCode: "ukr", Alpha2Code: "uk", English: "Ukrainian"},
{Alpha3bCode: "urd", Alpha2Code: "ur", English: "Urdu"},
{Alpha3bCode: "uzb", Alpha2Code: "uz", English: "Uzbek"},
{Alpha3bCode: "ven", Alpha2Code: "ve", English: "Venda"},
{Alpha3bCode: "vie", Alpha2Code: "vi", English: "Vietnamese"},
{Alpha3bCode: "vol", Alpha2Code: "vo", English: "Volapük"},
{Alpha3bCode: "wel", Alpha2Code: "cy", English: "Welsh"},
{Alpha3bCode: "wln", Alpha2Code: "wa", English: "Walloon"},
{Alpha3bCode: "wol", Alpha2Code: "wo", English: "Wolof"},
{Alpha3bCode: "xho", Alpha2Code: "xh", English: "Xhosa"},
{Alpha3bCode: "yid", Alpha2Code: "yi", English: "Yiddish"},
{Alpha3bCode: "yor", Alpha2Code: "yo", English: "Yoruba"},
{Alpha3bCode: "zha", Alpha2Code: "za", English: "Zhuang; Chuang"},
{Alpha3bCode: "zul", Alpha2Code: "zu", English: "Zulu"},
}

264
vendor/github.com/asaskevich/govalidator/utils.go generated vendored Normal file
View File

@@ -0,0 +1,264 @@
package govalidator
import (
"errors"
"fmt"
"html"
"math"
"path"
"regexp"
"strings"
"unicode"
"unicode/utf8"
)
// Contains check if the string contains the substring.
func Contains(str, substring string) bool {
return strings.Contains(str, substring)
}
// Matches check if string matches the pattern (pattern is regular expression)
// In case of error return false
func Matches(str, pattern string) bool {
match, _ := regexp.MatchString(pattern, str)
return match
}
// LeftTrim trim characters from the left-side of the input.
// If second argument is empty, it's will be remove leading spaces.
func LeftTrim(str, chars string) string {
if chars == "" {
return strings.TrimLeftFunc(str, unicode.IsSpace)
}
r, _ := regexp.Compile("^[" + chars + "]+")
return r.ReplaceAllString(str, "")
}
// RightTrim trim characters from the right-side of the input.
// If second argument is empty, it's will be remove spaces.
func RightTrim(str, chars string) string {
if chars == "" {
return strings.TrimRightFunc(str, unicode.IsSpace)
}
r, _ := regexp.Compile("[" + chars + "]+$")
return r.ReplaceAllString(str, "")
}
// Trim trim characters from both sides of the input.
// If second argument is empty, it's will be remove spaces.
func Trim(str, chars string) string {
return LeftTrim(RightTrim(str, chars), chars)
}
// WhiteList remove characters that do not appear in the whitelist.
func WhiteList(str, chars string) string {
pattern := "[^" + chars + "]+"
r, _ := regexp.Compile(pattern)
return r.ReplaceAllString(str, "")
}
// BlackList remove characters that appear in the blacklist.
func BlackList(str, chars string) string {
pattern := "[" + chars + "]+"
r, _ := regexp.Compile(pattern)
return r.ReplaceAllString(str, "")
}
// StripLow remove characters with a numerical value < 32 and 127, mostly control characters.
// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD).
func StripLow(str string, keepNewLines bool) string {
chars := ""
if keepNewLines {
chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F"
} else {
chars = "\x00-\x1F\x7F"
}
return BlackList(str, chars)
}
// ReplacePattern replace regular expression pattern in string
func ReplacePattern(str, pattern, replace string) string {
r, _ := regexp.Compile(pattern)
return r.ReplaceAllString(str, replace)
}
// Escape replace <, >, & and " with HTML entities.
var Escape = html.EscapeString
func addSegment(inrune, segment []rune) []rune {
if len(segment) == 0 {
return inrune
}
if len(inrune) != 0 {
inrune = append(inrune, '_')
}
inrune = append(inrune, segment...)
return inrune
}
// UnderscoreToCamelCase converts from underscore separated form to camel case form.
// Ex.: my_func => MyFunc
func UnderscoreToCamelCase(s string) string {
return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1)
}
// CamelCaseToUnderscore converts from camel case form to underscore separated form.
// Ex.: MyFunc => my_func
func CamelCaseToUnderscore(str string) string {
var output []rune
var segment []rune
for _, r := range str {
// not treat number as separate segment
if !unicode.IsLower(r) && string(r) != "_" && !unicode.IsNumber(r) {
output = addSegment(output, segment)
segment = nil
}
segment = append(segment, unicode.ToLower(r))
}
output = addSegment(output, segment)
return string(output)
}
// Reverse return reversed string
func Reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
// GetLines split string by "\n" and return array of lines
func GetLines(s string) []string {
return strings.Split(s, "\n")
}
// GetLine return specified line of multiline string
func GetLine(s string, index int) (string, error) {
lines := GetLines(s)
if index < 0 || index >= len(lines) {
return "", errors.New("line index out of bounds")
}
return lines[index], nil
}
// RemoveTags remove all tags from HTML string
func RemoveTags(s string) string {
return ReplacePattern(s, "<[^>]*>", "")
}
// SafeFileName return safe string that can be used in file names
func SafeFileName(str string) string {
name := strings.ToLower(str)
name = path.Clean(path.Base(name))
name = strings.Trim(name, " ")
separators, err := regexp.Compile(`[ &_=+:]`)
if err == nil {
name = separators.ReplaceAllString(name, "-")
}
legal, err := regexp.Compile(`[^[:alnum:]-.]`)
if err == nil {
name = legal.ReplaceAllString(name, "")
}
for strings.Contains(name, "--") {
name = strings.Replace(name, "--", "-", -1)
}
return name
}
// NormalizeEmail canonicalize an email address.
// The local part of the email address is lowercased for all domains; the hostname is always lowercased and
// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail).
// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and
// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are
// normalized to @gmail.com.
func NormalizeEmail(str string) (string, error) {
if !IsEmail(str) {
return "", fmt.Errorf("%s is not an email", str)
}
parts := strings.Split(str, "@")
parts[0] = strings.ToLower(parts[0])
parts[1] = strings.ToLower(parts[1])
if parts[1] == "gmail.com" || parts[1] == "googlemail.com" {
parts[1] = "gmail.com"
parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0]
}
return strings.Join(parts, "@"), nil
}
// Truncate a string to the closest length without breaking words.
func Truncate(str string, length int, ending string) string {
var aftstr, befstr string
if len(str) > length {
words := strings.Fields(str)
before, present := 0, 0
for i := range words {
befstr = aftstr
before = present
aftstr = aftstr + words[i] + " "
present = len(aftstr)
if present > length && i != 0 {
if (length - before) < (present - length) {
return Trim(befstr, " /\\.,\"'#!?&@+-") + ending
}
return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending
}
}
}
return str
}
// PadLeft pad left side of string if size of string is less then indicated pad length
func PadLeft(str string, padStr string, padLen int) string {
return buildPadStr(str, padStr, padLen, true, false)
}
// PadRight pad right side of string if size of string is less then indicated pad length
func PadRight(str string, padStr string, padLen int) string {
return buildPadStr(str, padStr, padLen, false, true)
}
// PadBoth pad sides of string if size of string is less then indicated pad length
func PadBoth(str string, padStr string, padLen int) string {
return buildPadStr(str, padStr, padLen, true, true)
}
// PadString either left, right or both sides, not the padding string can be unicode and more then one
// character
func buildPadStr(str string, padStr string, padLen int, padLeft bool, padRight bool) string {
// When padded length is less then the current string size
if padLen < utf8.RuneCountInString(str) {
return str
}
padLen -= utf8.RuneCountInString(str)
targetLen := padLen
targetLenLeft := targetLen
targetLenRight := targetLen
if padLeft && padRight {
targetLenLeft = padLen / 2
targetLenRight = padLen - targetLenLeft
}
strToRepeatLen := utf8.RuneCountInString(padStr)
repeatTimes := int(math.Ceil(float64(targetLen) / float64(strToRepeatLen)))
repeatedString := strings.Repeat(padStr, repeatTimes)
leftSide := ""
if padLeft {
leftSide = repeatedString[0:targetLenLeft]
}
rightSide := ""
if padRight {
rightSide = repeatedString[0:targetLenRight]
}
return leftSide + str + rightSide
}

1213
vendor/github.com/asaskevich/govalidator/validator.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

21
vendor/github.com/fatih/structs/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Fatih Arslan
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.

141
vendor/github.com/fatih/structs/field.go generated vendored Normal file
View File

@@ -0,0 +1,141 @@
package structs
import (
"errors"
"fmt"
"reflect"
)
var (
errNotExported = errors.New("field is not exported")
errNotSettable = errors.New("field is not settable")
)
// Field represents a single struct field that encapsulates high level
// functions around the field.
type Field struct {
value reflect.Value
field reflect.StructField
defaultTag string
}
// Tag returns the value associated with key in the tag string. If there is no
// such key in the tag, Tag returns the empty string.
func (f *Field) Tag(key string) string {
return f.field.Tag.Get(key)
}
// Value returns the underlying value of the field. It panics if the field
// is not exported.
func (f *Field) Value() interface{} {
return f.value.Interface()
}
// IsEmbedded returns true if the given field is an anonymous field (embedded)
func (f *Field) IsEmbedded() bool {
return f.field.Anonymous
}
// IsExported returns true if the given field is exported.
func (f *Field) IsExported() bool {
return f.field.PkgPath == ""
}
// IsZero returns true if the given field is not initialized (has a zero value).
// It panics if the field is not exported.
func (f *Field) IsZero() bool {
zero := reflect.Zero(f.value.Type()).Interface()
current := f.Value()
return reflect.DeepEqual(current, zero)
}
// Name returns the name of the given field
func (f *Field) Name() string {
return f.field.Name
}
// Kind returns the fields kind, such as "string", "map", "bool", etc ..
func (f *Field) Kind() reflect.Kind {
return f.value.Kind()
}
// Set sets the field to given value v. It returns an error if the field is not
// settable (not addressable or not exported) or if the given value's type
// doesn't match the fields type.
func (f *Field) Set(val interface{}) error {
// we can't set unexported fields, so be sure this field is exported
if !f.IsExported() {
return errNotExported
}
// do we get here? not sure...
if !f.value.CanSet() {
return errNotSettable
}
given := reflect.ValueOf(val)
if f.value.Kind() != given.Kind() {
return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind())
}
f.value.Set(given)
return nil
}
// Zero sets the field to its zero value. It returns an error if the field is not
// settable (not addressable or not exported).
func (f *Field) Zero() error {
zero := reflect.Zero(f.value.Type()).Interface()
return f.Set(zero)
}
// Fields returns a slice of Fields. This is particular handy to get the fields
// of a nested struct . A struct tag with the content of "-" ignores the
// checking of that particular field. Example:
//
// // Field is ignored by this package.
// Field *http.Request `structs:"-"`
//
// It panics if field is not exported or if field's kind is not struct
func (f *Field) Fields() []*Field {
return getFields(f.value, f.defaultTag)
}
// Field returns the field from a nested struct. It panics if the nested struct
// is not exported or if the field was not found.
func (f *Field) Field(name string) *Field {
field, ok := f.FieldOk(name)
if !ok {
panic("field not found")
}
return field
}
// FieldOk returns the field from a nested struct. The boolean returns whether
// the field was found (true) or not (false).
func (f *Field) FieldOk(name string) (*Field, bool) {
value := &f.value
// value must be settable so we need to make sure it holds the address of the
// variable and not a copy, so we can pass the pointer to strctVal instead of a
// copy (which is not assigned to any variable, hence not settable).
// see "https://blog.golang.org/laws-of-reflection#TOC_8."
if f.value.Kind() != reflect.Ptr {
a := f.value.Addr()
value = &a
}
v := strctVal(value.Interface())
t := v.Type()
field, ok := t.FieldByName(name)
if !ok {
return nil, false
}
return &Field{
field: field,
value: v.FieldByName(name),
}, true
}

584
vendor/github.com/fatih/structs/structs.go generated vendored Normal file
View File

@@ -0,0 +1,584 @@
// Package structs contains various utilities functions to work with structs.
package structs
import (
"fmt"
"reflect"
)
var (
// DefaultTagName is the default tag name for struct fields which provides
// a more granular to tweak certain structs. Lookup the necessary functions
// for more info.
DefaultTagName = "structs" // struct's field default tag name
)
// Struct encapsulates a struct type to provide several high level functions
// around the struct.
type Struct struct {
raw interface{}
value reflect.Value
TagName string
}
// New returns a new *Struct with the struct s. It panics if the s's kind is
// not struct.
func New(s interface{}) *Struct {
return &Struct{
raw: s,
value: strctVal(s),
TagName: DefaultTagName,
}
}
// Map converts the given struct to a map[string]interface{}, where the keys
// of the map are the field names and the values of the map the associated
// values of the fields. The default key string is the struct field name but
// can be changed in the struct field's tag value. The "structs" key in the
// struct's field tag value is the key name. Example:
//
// // Field appears in map as key "myName".
// Name string `structs:"myName"`
//
// A tag value with the content of "-" ignores that particular field. Example:
//
// // Field is ignored by this package.
// Field bool `structs:"-"`
//
// A tag value with the content of "string" uses the stringer to get the value. Example:
//
// // The value will be output of Animal's String() func.
// // Map will panic if Animal does not implement String().
// Field *Animal `structs:"field,string"`
//
// A tag value with the option of "flatten" used in a struct field is to flatten its fields
// in the output map. Example:
//
// // The FieldStruct's fields will be flattened into the output map.
// FieldStruct time.Time `structs:",flatten"`
//
// A tag value with the option of "omitnested" stops iterating further if the type
// is a struct. Example:
//
// // Field is not processed further by this package.
// Field time.Time `structs:"myName,omitnested"`
// Field *http.Request `structs:",omitnested"`
//
// A tag value with the option of "omitempty" ignores that particular field if
// the field value is empty. Example:
//
// // Field appears in map as key "myName", but the field is
// // skipped if empty.
// Field string `structs:"myName,omitempty"`
//
// // Field appears in map as key "Field" (the default), but
// // the field is skipped if empty.
// Field string `structs:",omitempty"`
//
// Note that only exported fields of a struct can be accessed, non exported
// fields will be neglected.
func (s *Struct) Map() map[string]interface{} {
out := make(map[string]interface{})
s.FillMap(out)
return out
}
// FillMap is the same as Map. Instead of returning the output, it fills the
// given map.
func (s *Struct) FillMap(out map[string]interface{}) {
if out == nil {
return
}
fields := s.structFields()
for _, field := range fields {
name := field.Name
val := s.value.FieldByName(name)
isSubStruct := false
var finalVal interface{}
tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
if tagName != "" {
name = tagName
}
// if the value is a zero value and the field is marked as omitempty do
// not include
if tagOpts.Has("omitempty") {
zero := reflect.Zero(val.Type()).Interface()
current := val.Interface()
if reflect.DeepEqual(current, zero) {
continue
}
}
if !tagOpts.Has("omitnested") {
finalVal = s.nested(val)
v := reflect.ValueOf(val.Interface())
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
switch v.Kind() {
case reflect.Map, reflect.Struct:
isSubStruct = true
}
} else {
finalVal = val.Interface()
}
if tagOpts.Has("string") {
s, ok := val.Interface().(fmt.Stringer)
if ok {
out[name] = s.String()
}
continue
}
if isSubStruct && (tagOpts.Has("flatten")) {
for k := range finalVal.(map[string]interface{}) {
out[k] = finalVal.(map[string]interface{})[k]
}
} else {
out[name] = finalVal
}
}
}
// Values converts the given s struct's field values to a []interface{}. A
// struct tag with the content of "-" ignores the that particular field.
// Example:
//
// // Field is ignored by this package.
// Field int `structs:"-"`
//
// A value with the option of "omitnested" stops iterating further if the type
// is a struct. Example:
//
// // Fields is not processed further by this package.
// Field time.Time `structs:",omitnested"`
// Field *http.Request `structs:",omitnested"`
//
// A tag value with the option of "omitempty" ignores that particular field and
// is not added to the values if the field value is empty. Example:
//
// // Field is skipped if empty
// Field string `structs:",omitempty"`
//
// Note that only exported fields of a struct can be accessed, non exported
// fields will be neglected.
func (s *Struct) Values() []interface{} {
fields := s.structFields()
var t []interface{}
for _, field := range fields {
val := s.value.FieldByName(field.Name)
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
// if the value is a zero value and the field is marked as omitempty do
// not include
if tagOpts.Has("omitempty") {
zero := reflect.Zero(val.Type()).Interface()
current := val.Interface()
if reflect.DeepEqual(current, zero) {
continue
}
}
if tagOpts.Has("string") {
s, ok := val.Interface().(fmt.Stringer)
if ok {
t = append(t, s.String())
}
continue
}
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
// look out for embedded structs, and convert them to a
// []interface{} to be added to the final values slice
t = append(t, Values(val.Interface())...)
} else {
t = append(t, val.Interface())
}
}
return t
}
// Fields returns a slice of Fields. A struct tag with the content of "-"
// ignores the checking of that particular field. Example:
//
// // Field is ignored by this package.
// Field bool `structs:"-"`
//
// It panics if s's kind is not struct.
func (s *Struct) Fields() []*Field {
return getFields(s.value, s.TagName)
}
// Names returns a slice of field names. A struct tag with the content of "-"
// ignores the checking of that particular field. Example:
//
// // Field is ignored by this package.
// Field bool `structs:"-"`
//
// It panics if s's kind is not struct.
func (s *Struct) Names() []string {
fields := getFields(s.value, s.TagName)
names := make([]string, len(fields))
for i, field := range fields {
names[i] = field.Name()
}
return names
}
func getFields(v reflect.Value, tagName string) []*Field {
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
t := v.Type()
var fields []*Field
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if tag := field.Tag.Get(tagName); tag == "-" {
continue
}
f := &Field{
field: field,
value: v.FieldByName(field.Name),
}
fields = append(fields, f)
}
return fields
}
// Field returns a new Field struct that provides several high level functions
// around a single struct field entity. It panics if the field is not found.
func (s *Struct) Field(name string) *Field {
f, ok := s.FieldOk(name)
if !ok {
panic("field not found")
}
return f
}
// FieldOk returns a new Field struct that provides several high level functions
// around a single struct field entity. The boolean returns true if the field
// was found.
func (s *Struct) FieldOk(name string) (*Field, bool) {
t := s.value.Type()
field, ok := t.FieldByName(name)
if !ok {
return nil, false
}
return &Field{
field: field,
value: s.value.FieldByName(name),
defaultTag: s.TagName,
}, true
}
// IsZero returns true if all fields in a struct is a zero value (not
// initialized) A struct tag with the content of "-" ignores the checking of
// that particular field. Example:
//
// // Field is ignored by this package.
// Field bool `structs:"-"`
//
// A value with the option of "omitnested" stops iterating further if the type
// is a struct. Example:
//
// // Field is not processed further by this package.
// Field time.Time `structs:"myName,omitnested"`
// Field *http.Request `structs:",omitnested"`
//
// Note that only exported fields of a struct can be accessed, non exported
// fields will be neglected. It panics if s's kind is not struct.
func (s *Struct) IsZero() bool {
fields := s.structFields()
for _, field := range fields {
val := s.value.FieldByName(field.Name)
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
ok := IsZero(val.Interface())
if !ok {
return false
}
continue
}
// zero value of the given field, such as "" for string, 0 for int
zero := reflect.Zero(val.Type()).Interface()
// current value of the given field
current := val.Interface()
if !reflect.DeepEqual(current, zero) {
return false
}
}
return true
}
// HasZero returns true if a field in a struct is not initialized (zero value).
// A struct tag with the content of "-" ignores the checking of that particular
// field. Example:
//
// // Field is ignored by this package.
// Field bool `structs:"-"`
//
// A value with the option of "omitnested" stops iterating further if the type
// is a struct. Example:
//
// // Field is not processed further by this package.
// Field time.Time `structs:"myName,omitnested"`
// Field *http.Request `structs:",omitnested"`
//
// Note that only exported fields of a struct can be accessed, non exported
// fields will be neglected. It panics if s's kind is not struct.
func (s *Struct) HasZero() bool {
fields := s.structFields()
for _, field := range fields {
val := s.value.FieldByName(field.Name)
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
ok := HasZero(val.Interface())
if ok {
return true
}
continue
}
// zero value of the given field, such as "" for string, 0 for int
zero := reflect.Zero(val.Type()).Interface()
// current value of the given field
current := val.Interface()
if reflect.DeepEqual(current, zero) {
return true
}
}
return false
}
// Name returns the structs's type name within its package. For more info refer
// to Name() function.
func (s *Struct) Name() string {
return s.value.Type().Name()
}
// structFields returns the exported struct fields for a given s struct. This
// is a convenient helper method to avoid duplicate code in some of the
// functions.
func (s *Struct) structFields() []reflect.StructField {
t := s.value.Type()
var f []reflect.StructField
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
// we can't access the value of unexported fields
if field.PkgPath != "" {
continue
}
// don't check if it's omitted
if tag := field.Tag.Get(s.TagName); tag == "-" {
continue
}
f = append(f, field)
}
return f
}
func strctVal(s interface{}) reflect.Value {
v := reflect.ValueOf(s)
// if pointer get the underlying element≤
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
panic("not struct")
}
return v
}
// Map converts the given struct to a map[string]interface{}. For more info
// refer to Struct types Map() method. It panics if s's kind is not struct.
func Map(s interface{}) map[string]interface{} {
return New(s).Map()
}
// FillMap is the same as Map. Instead of returning the output, it fills the
// given map.
func FillMap(s interface{}, out map[string]interface{}) {
New(s).FillMap(out)
}
// Values converts the given struct to a []interface{}. For more info refer to
// Struct types Values() method. It panics if s's kind is not struct.
func Values(s interface{}) []interface{} {
return New(s).Values()
}
// Fields returns a slice of *Field. For more info refer to Struct types
// Fields() method. It panics if s's kind is not struct.
func Fields(s interface{}) []*Field {
return New(s).Fields()
}
// Names returns a slice of field names. For more info refer to Struct types
// Names() method. It panics if s's kind is not struct.
func Names(s interface{}) []string {
return New(s).Names()
}
// IsZero returns true if all fields is equal to a zero value. For more info
// refer to Struct types IsZero() method. It panics if s's kind is not struct.
func IsZero(s interface{}) bool {
return New(s).IsZero()
}
// HasZero returns true if any field is equal to a zero value. For more info
// refer to Struct types HasZero() method. It panics if s's kind is not struct.
func HasZero(s interface{}) bool {
return New(s).HasZero()
}
// IsStruct returns true if the given variable is a struct or a pointer to
// struct.
func IsStruct(s interface{}) bool {
v := reflect.ValueOf(s)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
// uninitialized zero value of a struct
if v.Kind() == reflect.Invalid {
return false
}
return v.Kind() == reflect.Struct
}
// Name returns the structs's type name within its package. It returns an
// empty string for unnamed types. It panics if s's kind is not struct.
func Name(s interface{}) string {
return New(s).Name()
}
// nested retrieves recursively all types for the given value and returns the
// nested value.
func (s *Struct) nested(val reflect.Value) interface{} {
var finalVal interface{}
v := reflect.ValueOf(val.Interface())
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
switch v.Kind() {
case reflect.Struct:
n := New(val.Interface())
n.TagName = s.TagName
m := n.Map()
// do not add the converted value if there are no exported fields, ie:
// time.Time
if len(m) == 0 {
finalVal = val.Interface()
} else {
finalVal = m
}
case reflect.Map:
// get the element type of the map
mapElem := val.Type()
switch val.Type().Kind() {
case reflect.Ptr, reflect.Array, reflect.Map,
reflect.Slice, reflect.Chan:
mapElem = val.Type().Elem()
if mapElem.Kind() == reflect.Ptr {
mapElem = mapElem.Elem()
}
}
// only iterate over struct types, ie: map[string]StructType,
// map[string][]StructType,
if mapElem.Kind() == reflect.Struct ||
(mapElem.Kind() == reflect.Slice &&
mapElem.Elem().Kind() == reflect.Struct) {
m := make(map[string]interface{}, val.Len())
for _, k := range val.MapKeys() {
m[k.String()] = s.nested(val.MapIndex(k))
}
finalVal = m
break
}
// TODO(arslan): should this be optional?
finalVal = val.Interface()
case reflect.Slice, reflect.Array:
if val.Type().Kind() == reflect.Interface {
finalVal = val.Interface()
break
}
// TODO(arslan): should this be optional?
// do not iterate of non struct types, just pass the value. Ie: []int,
// []string, co... We only iterate further if it's a struct.
// i.e []foo or []*foo
if val.Type().Elem().Kind() != reflect.Struct &&
!(val.Type().Elem().Kind() == reflect.Ptr &&
val.Type().Elem().Elem().Kind() == reflect.Struct) {
finalVal = val.Interface()
break
}
slices := make([]interface{}, val.Len())
for x := 0; x < val.Len(); x++ {
slices[x] = s.nested(val.Index(x))
}
finalVal = slices
default:
finalVal = val.Interface()
}
return finalVal
}

32
vendor/github.com/fatih/structs/tags.go generated vendored Normal file
View File

@@ -0,0 +1,32 @@
package structs
import "strings"
// tagOptions contains a slice of tag options
type tagOptions []string
// Has returns true if the given option is available in tagOptions
func (t tagOptions) Has(opt string) bool {
for _, tagOpt := range t {
if tagOpt == opt {
return true
}
}
return false
}
// parseTag splits a struct field's tag into its name and a list of options
// which comes after a name. A tag is in the form of: "name,option1,option2".
// The name can be neglectected.
func parseTag(tag string) (string, tagOptions) {
// tag is one of followings:
// ""
// "name"
// "name,opt"
// "name,opt,opt2"
// ",opt"
res := strings.Split(tag, ",")
return res[0], res[1:]
}

20
vendor/github.com/gocraft/dbr/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014-2017 Jonathan Novak, Tai-Lin Chu
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.

29
vendor/github.com/gocraft/dbr/buffer.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
package dbr
import "bytes"
type Buffer interface {
WriteString(s string) (n int, err error)
String() string
WriteValue(v ...interface{}) (err error)
Value() []interface{}
}
type buffer struct {
bytes.Buffer
v []interface{}
}
func NewBuffer() Buffer {
return &buffer{}
}
func (b *buffer) WriteValue(v ...interface{}) error {
b.v = append(b.v, v...)
return nil
}
func (b *buffer) Value() []interface{} {
return b.v
}

13
vendor/github.com/gocraft/dbr/builder.go generated vendored Normal file
View File

@@ -0,0 +1,13 @@
package dbr
// Builder builds sql in one dialect like MySQL/PostgreSQL
// e.g. XxxBuilder
type Builder interface {
Build(Dialect, Buffer) error
}
type BuildFunc func(Dialect, Buffer) error
func (b BuildFunc) Build(d Dialect, buf Buffer) error {
return b(d, buf)
}

119
vendor/github.com/gocraft/dbr/condition.go generated vendored Normal file
View File

@@ -0,0 +1,119 @@
package dbr
import "reflect"
func buildCond(d Dialect, buf Buffer, pred string, cond ...Builder) error {
for i, c := range cond {
if i > 0 {
buf.WriteString(" ")
buf.WriteString(pred)
buf.WriteString(" ")
}
buf.WriteString("(")
err := c.Build(d, buf)
if err != nil {
return err
}
buf.WriteString(")")
}
return nil
}
// And creates AND from a list of conditions
func And(cond ...Builder) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
return buildCond(d, buf, "AND", cond...)
})
}
// Or creates OR from a list of conditions
func Or(cond ...Builder) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
return buildCond(d, buf, "OR", cond...)
})
}
func buildCmp(d Dialect, buf Buffer, pred string, column string, value interface{}) error {
buf.WriteString(d.QuoteIdent(column))
buf.WriteString(" ")
buf.WriteString(pred)
buf.WriteString(" ")
buf.WriteString(placeholder)
buf.WriteValue(value)
return nil
}
// Eq is `=`.
// When value is nil, it will be translated to `IS NULL`.
// When value is a slice, it will be translated to `IN`.
// Otherwise it will be translated to `=`.
func Eq(column string, value interface{}) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
if value == nil {
buf.WriteString(d.QuoteIdent(column))
buf.WriteString(" IS NULL")
return nil
}
v := reflect.ValueOf(value)
if v.Kind() == reflect.Slice {
if v.Len() == 0 {
buf.WriteString(d.EncodeBool(false))
return nil
}
return buildCmp(d, buf, "IN", column, value)
}
return buildCmp(d, buf, "=", column, value)
})
}
// Neq is `!=`.
// When value is nil, it will be translated to `IS NOT NULL`.
// When value is a slice, it will be translated to `NOT IN`.
// Otherwise it will be translated to `!=`.
func Neq(column string, value interface{}) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
if value == nil {
buf.WriteString(d.QuoteIdent(column))
buf.WriteString(" IS NOT NULL")
return nil
}
v := reflect.ValueOf(value)
if v.Kind() == reflect.Slice {
if v.Len() == 0 {
buf.WriteString(d.EncodeBool(true))
return nil
}
return buildCmp(d, buf, "NOT IN", column, value)
}
return buildCmp(d, buf, "!=", column, value)
})
}
// Gt is `>`.
func Gt(column string, value interface{}) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
return buildCmp(d, buf, ">", column, value)
})
}
// Gte is '>='.
func Gte(column string, value interface{}) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
return buildCmp(d, buf, ">=", column, value)
})
}
// Lt is '<'.
func Lt(column string, value interface{}) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
return buildCmp(d, buf, "<", column, value)
})
}
// Lte is `<=`.
func Lte(column string, value interface{}) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
return buildCmp(d, buf, "<=", column, value)
})
}

174
vendor/github.com/gocraft/dbr/dbr.go generated vendored Normal file
View File

@@ -0,0 +1,174 @@
package dbr
import (
"context"
"database/sql"
"fmt"
"time"
"github.com/gocraft/dbr/dialect"
)
// Open instantiates a Connection for a given database/sql connection
// and event receiver
func Open(driver, dsn string, log EventReceiver) (*Connection, error) {
if log == nil {
log = nullReceiver
}
conn, err := sql.Open(driver, dsn)
if err != nil {
return nil, err
}
var d Dialect
switch driver {
case "mysql":
d = dialect.MySQL
case "postgres":
d = dialect.PostgreSQL
case "sqlite3":
d = dialect.SQLite3
default:
return nil, ErrNotSupported
}
return &Connection{DB: conn, EventReceiver: log, Dialect: d}, nil
}
const (
placeholder = "?"
)
// Connection is a connection to the database with an EventReceiver
// to send events, errors, and timings to
type Connection struct {
*sql.DB
Dialect Dialect
EventReceiver
}
// Session represents a business unit of execution for some connection
type Session struct {
*Connection
EventReceiver
Timeout time.Duration
}
func (s *Session) GetTimeout() time.Duration {
return s.Timeout
}
// NewSession instantiates a Session for the Connection
func (conn *Connection) NewSession(log EventReceiver) *Session {
if log == nil {
log = conn.EventReceiver // Use parent instrumentation
}
return &Session{Connection: conn, EventReceiver: log}
}
// Ensure that tx and session are session runner
var (
_ SessionRunner = (*Tx)(nil)
_ SessionRunner = (*Session)(nil)
)
// SessionRunner can do anything that a Session can except start a transaction.
type SessionRunner interface {
Select(column ...string) *SelectBuilder
SelectBySql(query string, value ...interface{}) *SelectBuilder
InsertInto(table string) *InsertBuilder
InsertBySql(query string, value ...interface{}) *InsertBuilder
Update(table string) *UpdateBuilder
UpdateBySql(query string, value ...interface{}) *UpdateBuilder
DeleteFrom(table string) *DeleteBuilder
DeleteBySql(query string, value ...interface{}) *DeleteBuilder
}
type runner interface {
GetTimeout() time.Duration
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
}
func exec(ctx context.Context, runner runner, log EventReceiver, builder Builder, d Dialect) (sql.Result, error) {
timeout := runner.GetTimeout()
if timeout > 0 {
var cancel func()
ctx, cancel = context.WithTimeout(ctx, timeout)
defer cancel()
}
i := interpolator{
Buffer: NewBuffer(),
Dialect: d,
IgnoreBinary: true,
}
err := i.interpolate(placeholder, []interface{}{builder})
query, value := i.String(), i.Value()
if err != nil {
return nil, log.EventErrKv("dbr.exec.interpolate", err, kvs{
"sql": query,
"args": fmt.Sprint(value),
})
}
startTime := time.Now()
defer func() {
log.TimingKv("dbr.exec", time.Since(startTime).Nanoseconds(), kvs{
"sql": query,
})
}()
result, err := runner.ExecContext(ctx, query, value...)
if err != nil {
return result, log.EventErrKv("dbr.exec.exec", err, kvs{
"sql": query,
})
}
return result, nil
}
func query(ctx context.Context, runner runner, log EventReceiver, builder Builder, d Dialect, dest interface{}) (int, error) {
timeout := runner.GetTimeout()
if timeout > 0 {
var cancel func()
ctx, cancel = context.WithTimeout(ctx, timeout)
defer cancel()
}
i := interpolator{
Buffer: NewBuffer(),
Dialect: d,
IgnoreBinary: true,
}
err := i.interpolate(placeholder, []interface{}{builder})
query, value := i.String(), i.Value()
if err != nil {
return 0, log.EventErrKv("dbr.select.interpolate", err, kvs{
"sql": query,
"args": fmt.Sprint(value),
})
}
startTime := time.Now()
defer func() {
log.TimingKv("dbr.select", time.Since(startTime).Nanoseconds(), kvs{
"sql": query,
})
}()
rows, err := runner.QueryContext(ctx, query, value...)
if err != nil {
return 0, log.EventErrKv("dbr.select.load.query", err, kvs{
"sql": query,
})
}
count, err := Load(rows, dest)
if err != nil {
return 0, log.EventErrKv("dbr.select.load.scan", err, kvs{
"sql": query,
})
}
return count, nil
}

61
vendor/github.com/gocraft/dbr/delete.go generated vendored Normal file
View File

@@ -0,0 +1,61 @@
package dbr
// DeleteStmt builds `DELETE ...`
type DeleteStmt struct {
raw
Table string
WhereCond []Builder
}
// Build builds `DELETE ...` in dialect
func (b *DeleteStmt) Build(d Dialect, buf Buffer) error {
if b.raw.Query != "" {
return b.raw.Build(d, buf)
}
if b.Table == "" {
return ErrTableNotSpecified
}
buf.WriteString("DELETE FROM ")
buf.WriteString(d.QuoteIdent(b.Table))
if len(b.WhereCond) > 0 {
buf.WriteString(" WHERE ")
err := And(b.WhereCond...).Build(d, buf)
if err != nil {
return err
}
}
return nil
}
// DeleteFrom creates a DeleteStmt
func DeleteFrom(table string) *DeleteStmt {
return &DeleteStmt{
Table: table,
}
}
// DeleteBySql creates a DeleteStmt from raw query
func DeleteBySql(query string, value ...interface{}) *DeleteStmt {
return &DeleteStmt{
raw: raw{
Query: query,
Value: value,
},
}
}
// Where adds a where condition
func (b *DeleteStmt) Where(query interface{}, value ...interface{}) *DeleteStmt {
switch query := query.(type) {
case string:
b.WhereCond = append(b.WhereCond, Expr(query, value...))
case Builder:
b.WhereCond = append(b.WhereCond, query)
}
return b
}

87
vendor/github.com/gocraft/dbr/delete_builder.go generated vendored Normal file
View File

@@ -0,0 +1,87 @@
package dbr
import (
"context"
"database/sql"
"fmt"
)
type DeleteBuilder struct {
runner
EventReceiver
Dialect Dialect
*DeleteStmt
LimitCount int64
}
func (sess *Session) DeleteFrom(table string) *DeleteBuilder {
return &DeleteBuilder{
runner: sess,
EventReceiver: sess,
Dialect: sess.Dialect,
DeleteStmt: DeleteFrom(table),
LimitCount: -1,
}
}
func (tx *Tx) DeleteFrom(table string) *DeleteBuilder {
return &DeleteBuilder{
runner: tx,
EventReceiver: tx,
Dialect: tx.Dialect,
DeleteStmt: DeleteFrom(table),
LimitCount: -1,
}
}
func (sess *Session) DeleteBySql(query string, value ...interface{}) *DeleteBuilder {
return &DeleteBuilder{
runner: sess,
EventReceiver: sess,
Dialect: sess.Dialect,
DeleteStmt: DeleteBySql(query, value...),
LimitCount: -1,
}
}
func (tx *Tx) DeleteBySql(query string, value ...interface{}) *DeleteBuilder {
return &DeleteBuilder{
runner: tx,
EventReceiver: tx,
Dialect: tx.Dialect,
DeleteStmt: DeleteBySql(query, value...),
LimitCount: -1,
}
}
func (b *DeleteBuilder) Exec() (sql.Result, error) {
return b.ExecContext(context.Background())
}
func (b *DeleteBuilder) ExecContext(ctx context.Context) (sql.Result, error) {
return exec(ctx, b.runner, b.EventReceiver, b, b.Dialect)
}
func (b *DeleteBuilder) Where(query interface{}, value ...interface{}) *DeleteBuilder {
b.DeleteStmt.Where(query, value...)
return b
}
func (b *DeleteBuilder) Limit(n uint64) *DeleteBuilder {
b.LimitCount = int64(n)
return b
}
func (b *DeleteBuilder) Build(d Dialect, buf Buffer) error {
err := b.DeleteStmt.Build(b.Dialect, buf)
if err != nil {
return err
}
if b.LimitCount >= 0 {
buf.WriteString(" LIMIT ")
buf.WriteString(fmt.Sprint(b.LimitCount))
}
return nil
}

15
vendor/github.com/gocraft/dbr/dialect.go generated vendored Normal file
View File

@@ -0,0 +1,15 @@
package dbr
import "time"
// Dialect abstracts database differences
type Dialect interface {
QuoteIdent(id string) string
EncodeString(s string) string
EncodeBool(b bool) string
EncodeTime(t time.Time) string
EncodeBytes(b []byte) string
Placeholder(n int) string
}

24
vendor/github.com/gocraft/dbr/dialect/dialect.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
package dialect
import "strings"
var (
// MySQL dialect
MySQL = mysql{}
// PostgreSQL dialect
PostgreSQL = postgreSQL{}
// SQLite3 dialect
SQLite3 = sqlite3{}
)
const (
timeFormat = "2006-01-02 15:04:05.000000"
)
func quoteIdent(s, quote string) string {
part := strings.SplitN(s, ".", 2)
if len(part) == 2 {
return quoteIdent(part[0], quote) + "." + quoteIdent(part[1], quote)
}
return quote + s + quote
}

66
vendor/github.com/gocraft/dbr/dialect/mysql.go generated vendored Normal file
View File

@@ -0,0 +1,66 @@
package dialect
import (
"bytes"
"fmt"
"time"
)
type mysql struct{}
func (d mysql) QuoteIdent(s string) string {
return quoteIdent(s, "`")
}
func (d mysql) EncodeString(s string) string {
buf := new(bytes.Buffer)
buf.WriteRune('\'')
// https://dev.mysql.com/doc/refman/5.7/en/string-literals.html
for i := 0; i < len(s); i++ {
switch s[i] {
case 0:
buf.WriteString(`\0`)
case '\'':
buf.WriteString(`\'`)
case '"':
buf.WriteString(`\"`)
case '\b':
buf.WriteString(`\b`)
case '\n':
buf.WriteString(`\n`)
case '\r':
buf.WriteString(`\r`)
case '\t':
buf.WriteString(`\t`)
case 26:
buf.WriteString(`\Z`)
case '\\':
buf.WriteString(`\\`)
default:
buf.WriteByte(s[i])
}
}
buf.WriteRune('\'')
return buf.String()
}
func (d mysql) EncodeBool(b bool) string {
if b {
return "1"
}
return "0"
}
func (d mysql) EncodeTime(t time.Time) string {
return `'` + t.UTC().Format(timeFormat) + `'`
}
func (d mysql) EncodeBytes(b []byte) string {
return fmt.Sprintf(`0x%x`, b)
}
func (d mysql) Placeholder(_ int) string {
return "?"
}

37
vendor/github.com/gocraft/dbr/dialect/postgresql.go generated vendored Normal file
View File

@@ -0,0 +1,37 @@
package dialect
import (
"fmt"
"strings"
"time"
)
type postgreSQL struct{}
func (d postgreSQL) QuoteIdent(s string) string {
return quoteIdent(s, `"`)
}
func (d postgreSQL) EncodeString(s string) string {
// http://www.postgresql.org/docs/9.2/static/sql-syntax-lexical.html
return `'` + strings.Replace(s, `'`, `''`, -1) + `'`
}
func (d postgreSQL) EncodeBool(b bool) string {
if b {
return "TRUE"
}
return "FALSE"
}
func (d postgreSQL) EncodeTime(t time.Time) string {
return MySQL.EncodeTime(t)
}
func (d postgreSQL) EncodeBytes(b []byte) string {
return fmt.Sprintf(`E'\\x%x'`, b)
}
func (d postgreSQL) Placeholder(n int) string {
return fmt.Sprintf("$%d", n+1)
}

40
vendor/github.com/gocraft/dbr/dialect/sqlite3.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
package dialect
import (
"fmt"
"strings"
"time"
)
type sqlite3 struct{}
func (d sqlite3) QuoteIdent(s string) string {
return quoteIdent(s, `"`)
}
func (d sqlite3) EncodeString(s string) string {
// https://www.sqlite.org/faq.html
return `'` + strings.Replace(s, `'`, `''`, -1) + `'`
}
func (d sqlite3) EncodeBool(b bool) string {
// https://www.sqlite.org/lang_expr.html
if b {
return "1"
}
return "0"
}
func (d sqlite3) EncodeTime(t time.Time) string {
// https://www.sqlite.org/lang_datefunc.html
return MySQL.EncodeTime(t)
}
func (d sqlite3) EncodeBytes(b []byte) string {
// https://www.sqlite.org/lang_expr.html
return fmt.Sprintf(`X'%x'`, b)
}
func (d sqlite3) Placeholder(_ int) string {
return "?"
}

16
vendor/github.com/gocraft/dbr/errors.go generated vendored Normal file
View File

@@ -0,0 +1,16 @@
package dbr
import "errors"
// package errors
var (
ErrNotFound = errors.New("dbr: not found")
ErrNotSupported = errors.New("dbr: not supported")
ErrTableNotSpecified = errors.New("dbr: table not specified")
ErrColumnNotSpecified = errors.New("dbr: column not specified")
ErrInvalidPointer = errors.New("dbr: attempt to load into an invalid pointer")
ErrPlaceholderCount = errors.New("dbr: wrong placeholder count")
ErrInvalidSliceLength = errors.New("dbr: length of slice is 0. length must be >= 1")
ErrCantConvertToTime = errors.New("dbr: can't convert to time.Time")
ErrInvalidTimestring = errors.New("dbr: invalid time string")
)

40
vendor/github.com/gocraft/dbr/event.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
package dbr
// EventReceiver gets events from dbr methods for logging purposes
type EventReceiver interface {
Event(eventName string)
EventKv(eventName string, kvs map[string]string)
EventErr(eventName string, err error) error
EventErrKv(eventName string, err error, kvs map[string]string) error
Timing(eventName string, nanoseconds int64)
TimingKv(eventName string, nanoseconds int64, kvs map[string]string)
}
type kvs map[string]string
var nullReceiver = &NullEventReceiver{}
// NullEventReceiver is a sentinel EventReceiver; use it if the caller doesn't supply one
type NullEventReceiver struct{}
// Event receives a simple notification when various events occur
func (n *NullEventReceiver) Event(eventName string) {}
// EventKv receives a notification when various events occur along with
// optional key/value data
func (n *NullEventReceiver) EventKv(eventName string, kvs map[string]string) {}
// EventErr receives a notification of an error if one occurs
func (n *NullEventReceiver) EventErr(eventName string, err error) error { return err }
// EventErrKv receives a notification of an error if one occurs along with
// optional key/value data
func (n *NullEventReceiver) EventErrKv(eventName string, err error, kvs map[string]string) error {
return err
}
// Timing receives the time an event took to happen
func (n *NullEventReceiver) Timing(eventName string, nanoseconds int64) {}
// TimingKv receives the time an event took to happen along with optional key/value data
func (n *NullEventReceiver) TimingKv(eventName string, nanoseconds int64, kvs map[string]string) {}

18
vendor/github.com/gocraft/dbr/expr.go generated vendored Normal file
View File

@@ -0,0 +1,18 @@
package dbr
// XxxBuilders all support raw query
type raw struct {
Query string
Value []interface{}
}
// Expr should be used when sql syntax is not supported
func Expr(query string, value ...interface{}) Builder {
return &raw{Query: query, Value: value}
}
func (raw *raw) Build(_ Dialect, buf Buffer) error {
buf.WriteString(raw.Query)
buf.WriteValue(raw.Value...)
return nil
}

24
vendor/github.com/gocraft/dbr/ident.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
package dbr
// identifier is a type of string
type I string
func (i I) Build(d Dialect, buf Buffer) error {
buf.WriteString(d.QuoteIdent(string(i)))
return nil
}
// As creates an alias for expr. e.g. SELECT `a1` AS `a2`
func (i I) As(alias string) Builder {
return as(i, alias)
}
func as(expr interface{}, alias string) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
buf.WriteString(placeholder)
buf.WriteValue(expr)
buf.WriteString(" AS ")
buf.WriteString(d.QuoteIdent(alias))
return nil
})
}

123
vendor/github.com/gocraft/dbr/insert.go generated vendored Normal file
View File

@@ -0,0 +1,123 @@
package dbr
import (
"bytes"
"reflect"
)
// InsertStmt builds `INSERT INTO ...`
type InsertStmt struct {
raw
Table string
Column []string
Value [][]interface{}
ReturnColumn []string
}
// Build builds `INSERT INTO ...` in dialect
func (b *InsertStmt) Build(d Dialect, buf Buffer) error {
if b.raw.Query != "" {
return b.raw.Build(d, buf)
}
if b.Table == "" {
return ErrTableNotSpecified
}
if len(b.Column) == 0 {
return ErrColumnNotSpecified
}
buf.WriteString("INSERT INTO ")
buf.WriteString(d.QuoteIdent(b.Table))
placeholderBuf := new(bytes.Buffer)
placeholderBuf.WriteString("(")
buf.WriteString(" (")
for i, col := range b.Column {
if i > 0 {
buf.WriteString(",")
placeholderBuf.WriteString(",")
}
buf.WriteString(d.QuoteIdent(col))
placeholderBuf.WriteString(placeholder)
}
buf.WriteString(") VALUES ")
placeholderBuf.WriteString(")")
placeholderStr := placeholderBuf.String()
for i, tuple := range b.Value {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(placeholderStr)
buf.WriteValue(tuple...)
}
if len(b.ReturnColumn) > 0 {
buf.WriteString(" RETURNING ")
for i, col := range b.ReturnColumn {
if i > 0 {
buf.WriteString(",")
}
buf.WriteString(d.QuoteIdent(col))
}
}
return nil
}
// InsertInto creates an InsertStmt
func InsertInto(table string) *InsertStmt {
return &InsertStmt{
Table: table,
}
}
// InsertBySql creates an InsertStmt from raw query
func InsertBySql(query string, value ...interface{}) *InsertStmt {
return &InsertStmt{
raw: raw{
Query: query,
Value: value,
},
}
}
// Columns adds columns
func (b *InsertStmt) Columns(column ...string) *InsertStmt {
b.Column = column
return b
}
// Values adds a tuple for columns
func (b *InsertStmt) Values(value ...interface{}) *InsertStmt {
b.Value = append(b.Value, value)
return b
}
// Record adds a tuple for columns from a struct
func (b *InsertStmt) Record(structValue interface{}) *InsertStmt {
v := reflect.Indirect(reflect.ValueOf(structValue))
if v.Kind() == reflect.Struct {
var value []interface{}
m := structMap(v)
for _, key := range b.Column {
if val, ok := m[key]; ok {
value = append(value, val.Interface())
} else {
value = append(value, nil)
}
}
b.Values(value...)
}
return b
}
func (b *InsertStmt) Returning(column ...string) *InsertStmt {
b.ReturnColumn = column
return b
}

126
vendor/github.com/gocraft/dbr/insert_builder.go generated vendored Normal file
View File

@@ -0,0 +1,126 @@
package dbr
import (
"context"
"database/sql"
"reflect"
)
type InsertBuilder struct {
runner
EventReceiver
Dialect Dialect
RecordID reflect.Value
*InsertStmt
}
func (sess *Session) InsertInto(table string) *InsertBuilder {
return &InsertBuilder{
runner: sess,
EventReceiver: sess,
Dialect: sess.Dialect,
InsertStmt: InsertInto(table),
}
}
func (tx *Tx) InsertInto(table string) *InsertBuilder {
return &InsertBuilder{
runner: tx,
EventReceiver: tx,
Dialect: tx.Dialect,
InsertStmt: InsertInto(table),
}
}
func (sess *Session) InsertBySql(query string, value ...interface{}) *InsertBuilder {
return &InsertBuilder{
runner: sess,
EventReceiver: sess,
Dialect: sess.Dialect,
InsertStmt: InsertBySql(query, value...),
}
}
func (tx *Tx) InsertBySql(query string, value ...interface{}) *InsertBuilder {
return &InsertBuilder{
runner: tx,
EventReceiver: tx,
Dialect: tx.Dialect,
InsertStmt: InsertBySql(query, value...),
}
}
func (b *InsertBuilder) Pair(column string, value interface{}) *InsertBuilder {
b.Column = append(b.Column, column)
switch len(b.Value) {
case 0:
b.InsertStmt.Values(value)
case 1:
b.Value[0] = append(b.Value[0], value)
default:
panic("pair only allows one record to insert")
}
return b
}
func (b *InsertBuilder) Exec() (sql.Result, error) {
return b.ExecContext(context.Background())
}
func (b *InsertBuilder) ExecContext(ctx context.Context) (sql.Result, error) {
result, err := exec(ctx, b.runner, b.EventReceiver, b, b.Dialect)
if err != nil {
return nil, err
}
if b.RecordID.IsValid() {
if id, err := result.LastInsertId(); err == nil {
b.RecordID.SetInt(id)
}
}
return result, nil
}
func (b *InsertBuilder) LoadContext(ctx context.Context, value interface{}) error {
_, err := query(ctx, b.runner, b.EventReceiver, b, b.Dialect, value)
return err
}
func (b *InsertBuilder) Load(value interface{}) error {
return b.LoadContext(context.Background(), value)
}
func (b *InsertBuilder) Columns(column ...string) *InsertBuilder {
b.InsertStmt.Columns(column...)
return b
}
func (b *InsertBuilder) Returning(column ...string) *InsertBuilder {
b.InsertStmt.Returning(column...)
return b
}
func (b *InsertBuilder) Record(structValue interface{}) *InsertBuilder {
v := reflect.Indirect(reflect.ValueOf(structValue))
if v.Kind() == reflect.Struct && v.CanSet() {
// ID is recommended by golint here
for _, name := range []string{"Id", "ID"} {
field := v.FieldByName(name)
if field.IsValid() && field.Kind() == reflect.Int64 {
b.RecordID = field
break
}
}
}
b.InsertStmt.Record(structValue)
return b
}
func (b *InsertBuilder) Values(value ...interface{}) *InsertBuilder {
b.InsertStmt.Values(value...)
return b
}

157
vendor/github.com/gocraft/dbr/interpolate.go generated vendored Normal file
View File

@@ -0,0 +1,157 @@
package dbr
import (
"database/sql/driver"
"reflect"
"strconv"
"strings"
"time"
)
type interpolator struct {
Buffer
Dialect
IgnoreBinary bool
N int
}
// InterpolateForDialect replaces placeholder in query with corresponding value in dialect
func InterpolateForDialect(query string, value []interface{}, d Dialect) (string, error) {
i := interpolator{
Buffer: NewBuffer(),
Dialect: d,
}
err := i.interpolate(query, value)
if err != nil {
return "", err
}
return i.String(), nil
}
func (i *interpolator) interpolate(query string, value []interface{}) error {
if strings.Count(query, placeholder) != len(value) {
return ErrPlaceholderCount
}
valueIndex := 0
for {
index := strings.Index(query, placeholder)
if index == -1 {
break
}
i.WriteString(query[:index])
if _, ok := value[valueIndex].([]byte); ok && i.IgnoreBinary {
i.WriteString(i.Placeholder(i.N))
i.N++
i.WriteValue(value[valueIndex])
} else {
err := i.encodePlaceholder(value[valueIndex])
if err != nil {
return err
}
}
query = query[index+len(placeholder):]
valueIndex++
}
// placeholder not found; write remaining query
i.WriteString(query)
return nil
}
func (i *interpolator) encodePlaceholder(value interface{}) error {
if builder, ok := value.(Builder); ok {
pbuf := NewBuffer()
err := builder.Build(i.Dialect, pbuf)
if err != nil {
return err
}
paren := true
switch value.(type) {
case *SelectStmt:
case *union:
default:
paren = false
}
if paren {
i.WriteString("(")
}
err = i.interpolate(pbuf.String(), pbuf.Value())
if err != nil {
return err
}
if paren {
i.WriteString(")")
}
return nil
}
if valuer, ok := value.(driver.Valuer); ok {
// get driver.Valuer's data
var err error
value, err = valuer.Value()
if err != nil {
return err
}
}
if value == nil {
i.WriteString("NULL")
return nil
}
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.String:
i.WriteString(i.EncodeString(v.String()))
return nil
case reflect.Bool:
i.WriteString(i.EncodeBool(v.Bool()))
return nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i.WriteString(strconv.FormatInt(v.Int(), 10))
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
i.WriteString(strconv.FormatUint(v.Uint(), 10))
return nil
case reflect.Float32, reflect.Float64:
i.WriteString(strconv.FormatFloat(v.Float(), 'f', -1, 64))
return nil
case reflect.Struct:
if v.Type() == reflect.TypeOf(time.Time{}) {
i.WriteString(i.EncodeTime(v.Interface().(time.Time)))
return nil
}
case reflect.Slice:
if v.Type().Elem().Kind() == reflect.Uint8 {
// []byte
i.WriteString(i.EncodeBytes(v.Bytes()))
return nil
}
if v.Len() == 0 {
// FIXME: support zero-length slice
return ErrInvalidSliceLength
}
i.WriteString("(")
for n := 0; n < v.Len(); n++ {
if n > 0 {
i.WriteString(",")
}
err := i.encodePlaceholder(v.Index(n).Interface())
if err != nil {
return err
}
}
i.WriteString(")")
return nil
case reflect.Ptr:
if v.IsNil() {
i.WriteString("NULL")
return nil
}
return i.encodePlaceholder(v.Elem().Interface())
}
return ErrNotSupported
}

41
vendor/github.com/gocraft/dbr/join.go generated vendored Normal file
View File

@@ -0,0 +1,41 @@
package dbr
type joinType uint8
const (
inner joinType = iota
left
right
full
)
func join(t joinType, table interface{}, on interface{}) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
buf.WriteString(" ")
switch t {
case left:
buf.WriteString("LEFT ")
case right:
buf.WriteString("RIGHT ")
case full:
buf.WriteString("FULL ")
}
buf.WriteString("JOIN ")
switch table := table.(type) {
case string:
buf.WriteString(d.QuoteIdent(table))
default:
buf.WriteString(placeholder)
buf.WriteValue(table)
}
buf.WriteString(" ON ")
switch on := on.(type) {
case string:
buf.WriteString(on)
case Builder:
buf.WriteString(placeholder)
buf.WriteValue(on)
}
return nil
})
}

119
vendor/github.com/gocraft/dbr/load.go generated vendored Normal file
View File

@@ -0,0 +1,119 @@
package dbr
import (
"database/sql"
"reflect"
)
// Load loads any value from sql.Rows
func Load(rows *sql.Rows, value interface{}) (int, error) {
defer rows.Close()
column, err := rows.Columns()
if err != nil {
return 0, err
}
v := reflect.ValueOf(value)
if v.Kind() != reflect.Ptr || v.IsNil() {
return 0, ErrInvalidPointer
}
v = v.Elem()
isScanner := v.Addr().Type().Implements(typeScanner)
isSlice := v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 && !isScanner
isMap := v.Kind() == reflect.Map && !isScanner
isMapOfSlices := isMap && v.Type().Elem().Kind() == reflect.Slice && v.Type().Elem().Elem().Kind() != reflect.Uint8
if isMap {
v.Set(reflect.MakeMap(v.Type()))
}
count := 0
for rows.Next() {
var elem, keyElem reflect.Value
var ptr []interface{}
var err error
if isMapOfSlices {
elem = reflect.New(v.Type().Elem().Elem()).Elem()
} else if isSlice || isMap {
elem = reflect.New(v.Type().Elem()).Elem()
} else {
elem = v
}
if isMap {
ptr, err = findPtr(column[1:], elem)
if err != nil {
return 0, err
}
keyElem = reflect.New(v.Type().Key()).Elem()
keyPtr, err := findPtr(column[0:1], keyElem)
if err != nil {
return 0, err
}
ptr = append(keyPtr, ptr...)
} else {
ptr, err = findPtr(column, elem)
if err != nil {
return 0, err
}
}
err = rows.Scan(ptr...)
if err != nil {
return 0, err
}
count++
if isSlice {
v.Set(reflect.Append(v, elem))
} else if isMapOfSlices {
s := v.MapIndex(keyElem)
if !s.IsValid() {
s = reflect.Zero(v.Type().Elem())
}
v.SetMapIndex(keyElem, reflect.Append(s, elem))
} else if isMap {
v.SetMapIndex(keyElem, elem)
} else {
break
}
}
return count, nil
}
type dummyScanner struct{}
func (dummyScanner) Scan(interface{}) error {
return nil
}
var (
dummyDest sql.Scanner = dummyScanner{}
typeScanner = reflect.TypeOf((*sql.Scanner)(nil)).Elem()
)
func findPtr(column []string, value reflect.Value) ([]interface{}, error) {
if value.Addr().Type().Implements(typeScanner) {
return []interface{}{value.Addr().Interface()}, nil
}
switch value.Kind() {
case reflect.Struct:
var ptr []interface{}
m := structMap(value)
for _, key := range column {
if val, ok := m[key]; ok {
ptr = append(ptr, val.Addr().Interface())
} else {
ptr = append(ptr, dummyDest)
}
}
return ptr, nil
case reflect.Ptr:
if value.IsNil() {
value.Set(reflect.New(value.Type().Elem()))
}
return findPtr(column, value.Elem())
}
return []interface{}{value.Addr().Interface()}, nil
}

19
vendor/github.com/gocraft/dbr/now.go generated vendored Normal file
View File

@@ -0,0 +1,19 @@
package dbr
import (
"database/sql/driver"
"time"
)
// Now is a value that serializes to the current time
var Now = nowSentinel{}
const timeFormat = "2006-01-02 15:04:05.000000"
type nowSentinel struct{}
// Value implements a valuer for compatibility
func (n nowSentinel) Value() (driver.Value, error) {
now := time.Now().UTC().Format(timeFormat)
return now, nil
}

24
vendor/github.com/gocraft/dbr/order.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
package dbr
type direction bool
// orderby directions
// most databases by default use asc
const (
asc direction = false
desc = true
)
func order(column string, dir direction) Builder {
return BuildFunc(func(d Dialect, buf Buffer) error {
// FIXME: no quote ident
buf.WriteString(column)
switch dir {
case asc:
buf.WriteString(" ASC")
case desc:
buf.WriteString(" DESC")
}
return nil
})
}

238
vendor/github.com/gocraft/dbr/select.go generated vendored Normal file
View File

@@ -0,0 +1,238 @@
package dbr
import "fmt"
// SelectStmt builds `SELECT ...`
type SelectStmt struct {
raw
IsDistinct bool
Column []interface{}
Table interface{}
JoinTable []Builder
WhereCond []Builder
Group []Builder
HavingCond []Builder
Order []Builder
LimitCount int64
OffsetCount int64
}
// Build builds `SELECT ...` in dialect
func (b *SelectStmt) Build(d Dialect, buf Buffer) error {
if b.raw.Query != "" {
return b.raw.Build(d, buf)
}
if len(b.Column) == 0 {
return ErrColumnNotSpecified
}
buf.WriteString("SELECT ")
if b.IsDistinct {
buf.WriteString("DISTINCT ")
}
for i, col := range b.Column {
if i > 0 {
buf.WriteString(", ")
}
switch col := col.(type) {
case string:
// FIXME: no quote ident
buf.WriteString(col)
default:
buf.WriteString(placeholder)
buf.WriteValue(col)
}
}
if b.Table != nil {
buf.WriteString(" FROM ")
switch table := b.Table.(type) {
case string:
// FIXME: no quote ident
buf.WriteString(table)
default:
buf.WriteString(placeholder)
buf.WriteValue(table)
}
if len(b.JoinTable) > 0 {
for _, join := range b.JoinTable {
err := join.Build(d, buf)
if err != nil {
return err
}
}
}
}
if len(b.WhereCond) > 0 {
buf.WriteString(" WHERE ")
err := And(b.WhereCond...).Build(d, buf)
if err != nil {
return err
}
}
if len(b.Group) > 0 {
buf.WriteString(" GROUP BY ")
for i, group := range b.Group {
if i > 0 {
buf.WriteString(", ")
}
err := group.Build(d, buf)
if err != nil {
return err
}
}
}
if len(b.HavingCond) > 0 {
buf.WriteString(" HAVING ")
err := And(b.HavingCond...).Build(d, buf)
if err != nil {
return err
}
}
if len(b.Order) > 0 {
buf.WriteString(" ORDER BY ")
for i, order := range b.Order {
if i > 0 {
buf.WriteString(", ")
}
err := order.Build(d, buf)
if err != nil {
return err
}
}
}
if b.LimitCount >= 0 {
buf.WriteString(" LIMIT ")
buf.WriteString(fmt.Sprint(b.LimitCount))
}
if b.OffsetCount >= 0 {
buf.WriteString(" OFFSET ")
buf.WriteString(fmt.Sprint(b.OffsetCount))
}
return nil
}
// Select creates a SelectStmt
func Select(column ...interface{}) *SelectStmt {
return &SelectStmt{
Column: column,
LimitCount: -1,
OffsetCount: -1,
}
}
// From specifies table
func (b *SelectStmt) From(table interface{}) *SelectStmt {
b.Table = table
return b
}
// SelectBySql creates a SelectStmt from raw query
func SelectBySql(query string, value ...interface{}) *SelectStmt {
return &SelectStmt{
raw: raw{
Query: query,
Value: value,
},
LimitCount: -1,
OffsetCount: -1,
}
}
// Distinct adds `DISTINCT`
func (b *SelectStmt) Distinct() *SelectStmt {
b.IsDistinct = true
return b
}
// Where adds a where condition
func (b *SelectStmt) Where(query interface{}, value ...interface{}) *SelectStmt {
switch query := query.(type) {
case string:
b.WhereCond = append(b.WhereCond, Expr(query, value...))
case Builder:
b.WhereCond = append(b.WhereCond, query)
}
return b
}
// Having adds a having condition
func (b *SelectStmt) Having(query interface{}, value ...interface{}) *SelectStmt {
switch query := query.(type) {
case string:
b.HavingCond = append(b.HavingCond, Expr(query, value...))
case Builder:
b.HavingCond = append(b.HavingCond, query)
}
return b
}
// GroupBy specifies columns for grouping
func (b *SelectStmt) GroupBy(col ...string) *SelectStmt {
for _, group := range col {
b.Group = append(b.Group, Expr(group))
}
return b
}
// OrderBy specifies columns for ordering
func (b *SelectStmt) OrderAsc(col string) *SelectStmt {
b.Order = append(b.Order, order(col, asc))
return b
}
func (b *SelectStmt) OrderDesc(col string) *SelectStmt {
b.Order = append(b.Order, order(col, desc))
return b
}
// Limit adds limit
func (b *SelectStmt) Limit(n uint64) *SelectStmt {
b.LimitCount = int64(n)
return b
}
// Offset adds offset
func (b *SelectStmt) Offset(n uint64) *SelectStmt {
b.OffsetCount = int64(n)
return b
}
// Join joins table on condition
func (b *SelectStmt) Join(table, on interface{}) *SelectStmt {
b.JoinTable = append(b.JoinTable, join(inner, table, on))
return b
}
func (b *SelectStmt) LeftJoin(table, on interface{}) *SelectStmt {
b.JoinTable = append(b.JoinTable, join(left, table, on))
return b
}
func (b *SelectStmt) RightJoin(table, on interface{}) *SelectStmt {
b.JoinTable = append(b.JoinTable, join(right, table, on))
return b
}
func (b *SelectStmt) FullJoin(table, on interface{}) *SelectStmt {
b.JoinTable = append(b.JoinTable, join(full, table, on))
return b
}
// As creates alias for select statement
func (b *SelectStmt) As(alias string) Builder {
return as(b, alias)
}

173
vendor/github.com/gocraft/dbr/select_builder.go generated vendored Normal file
View File

@@ -0,0 +1,173 @@
package dbr
import "context"
type SelectBuilder struct {
runner
EventReceiver
Dialect Dialect
*SelectStmt
}
func prepareSelect(a []string) []interface{} {
b := make([]interface{}, len(a))
for i := range a {
b[i] = a[i]
}
return b
}
func (sess *Session) Select(column ...string) *SelectBuilder {
return &SelectBuilder{
runner: sess,
EventReceiver: sess,
Dialect: sess.Dialect,
SelectStmt: Select(prepareSelect(column)...),
}
}
func (tx *Tx) Select(column ...string) *SelectBuilder {
return &SelectBuilder{
runner: tx,
EventReceiver: tx,
Dialect: tx.Dialect,
SelectStmt: Select(prepareSelect(column)...),
}
}
func (sess *Session) SelectBySql(query string, value ...interface{}) *SelectBuilder {
return &SelectBuilder{
runner: sess,
EventReceiver: sess,
Dialect: sess.Dialect,
SelectStmt: SelectBySql(query, value...),
}
}
func (tx *Tx) SelectBySql(query string, value ...interface{}) *SelectBuilder {
return &SelectBuilder{
runner: tx,
EventReceiver: tx,
Dialect: tx.Dialect,
SelectStmt: SelectBySql(query, value...),
}
}
// DEPRECATED: use LoadOne instead
func (b *SelectBuilder) LoadStruct(value interface{}) error {
return b.LoadOne(value)
}
// DEPRECATED: use Load instead
func (b *SelectBuilder) LoadStructs(value interface{}) (int, error) {
return b.Load(value)
}
// DEPRECATED: use LoadOne instead
func (b *SelectBuilder) LoadValue(value interface{}) error {
return b.LoadOne(value)
}
// DEPRECATED: use Load instead
func (b *SelectBuilder) LoadValues(value interface{}) (int, error) {
return b.Load(value)
}
func (b *SelectBuilder) LoadOneContext(ctx context.Context, value interface{}) error {
count, err := query(ctx, b.runner, b.EventReceiver, b, b.Dialect, value)
if err != nil {
return err
}
if count == 0 {
return ErrNotFound
}
return nil
}
func (b *SelectBuilder) LoadOne(value interface{}) error {
return b.LoadOneContext(context.Background(), value)
}
func (b *SelectBuilder) LoadContext(ctx context.Context, value interface{}) (int, error) {
return query(ctx, b.runner, b.EventReceiver, b, b.Dialect, value)
}
func (b *SelectBuilder) Load(value interface{}) (int, error) {
return b.LoadContext(context.Background(), value)
}
func (b *SelectBuilder) Join(table, on interface{}) *SelectBuilder {
b.SelectStmt.Join(table, on)
return b
}
func (b *SelectBuilder) LeftJoin(table, on interface{}) *SelectBuilder {
b.SelectStmt.LeftJoin(table, on)
return b
}
func (b *SelectBuilder) RightJoin(table, on interface{}) *SelectBuilder {
b.SelectStmt.RightJoin(table, on)
return b
}
func (b *SelectBuilder) FullJoin(table, on interface{}) *SelectBuilder {
b.SelectStmt.FullJoin(table, on)
return b
}
func (b *SelectBuilder) Distinct() *SelectBuilder {
b.SelectStmt.Distinct()
return b
}
func (b *SelectBuilder) From(table interface{}) *SelectBuilder {
b.SelectStmt.From(table)
return b
}
func (b *SelectBuilder) GroupBy(col ...string) *SelectBuilder {
b.SelectStmt.GroupBy(col...)
return b
}
func (b *SelectBuilder) Having(query interface{}, value ...interface{}) *SelectBuilder {
b.SelectStmt.Having(query, value...)
return b
}
func (b *SelectBuilder) Limit(n uint64) *SelectBuilder {
b.SelectStmt.Limit(n)
return b
}
func (b *SelectBuilder) Offset(n uint64) *SelectBuilder {
b.SelectStmt.Offset(n)
return b
}
func (b *SelectBuilder) OrderDir(col string, isAsc bool) *SelectBuilder {
if isAsc {
b.SelectStmt.OrderAsc(col)
} else {
b.SelectStmt.OrderDesc(col)
}
return b
}
func (b *SelectBuilder) Paginate(page, perPage uint64) *SelectBuilder {
b.Limit(perPage)
b.Offset((page - 1) * perPage)
return b
}
func (b *SelectBuilder) OrderBy(col string) *SelectBuilder {
b.SelectStmt.Order = append(b.SelectStmt.Order, Expr(col))
return b
}
func (b *SelectBuilder) Where(query interface{}, value ...interface{}) *SelectBuilder {
b.SelectStmt.Where(query, value...)
return b
}

66
vendor/github.com/gocraft/dbr/select_return.go generated vendored Normal file
View File

@@ -0,0 +1,66 @@
package dbr
//
// These are a set of helpers that just call LoadValue and return the value.
// They return (_, ErrNotFound) if nothing was found.
//
// The inclusion of these helpers in the package is not an obvious choice:
// Benefits:
// - slight increase in code clarity/conciseness b/c you can use ":=" to define the variable
//
// count, err := d.Select("COUNT(*)").From("users").Where("x = ?", x).ReturnInt64()
//
// vs
//
// var count int64
// err := d.Select("COUNT(*)").From("users").Where("x = ?", x).LoadValue(&count)
//
// Downsides:
// - very small increase in code cost, although it's not complex code
// - increase in conceptual model / API footprint when presenting the package to new users
// - no functionality that you can't achieve calling .LoadValue directly.
// - There's a lot of possible types. Do we want to include ALL of them? u?int{8,16,32,64}?, strings, null varieties, etc.
// - Let's just do the common, non-null varieties.
// ReturnInt64 executes the SelectStmt and returns the value as an int64
func (b *SelectBuilder) ReturnInt64() (int64, error) {
var v int64
err := b.LoadValue(&v)
return v, err
}
// ReturnInt64s executes the SelectStmt and returns the value as a slice of int64s
func (b *SelectBuilder) ReturnInt64s() ([]int64, error) {
var v []int64
_, err := b.LoadValues(&v)
return v, err
}
// ReturnUint64 executes the SelectStmt and returns the value as an uint64
func (b *SelectBuilder) ReturnUint64() (uint64, error) {
var v uint64
err := b.LoadValue(&v)
return v, err
}
// ReturnUint64s executes the SelectStmt and returns the value as a slice of uint64s
func (b *SelectBuilder) ReturnUint64s() ([]uint64, error) {
var v []uint64
_, err := b.LoadValues(&v)
return v, err
}
// ReturnString executes the SelectStmt and returns the value as a string
func (b *SelectBuilder) ReturnString() (string, error) {
var v string
err := b.LoadValue(&v)
return v, err
}
// ReturnStrings executes the SelectStmt and returns the value as a slice of strings
func (b *SelectBuilder) ReturnStrings() ([]string, error) {
var v []string
_, err := b.LoadValues(&v)
return v, err
}

99
vendor/github.com/gocraft/dbr/transaction.go generated vendored Normal file
View File

@@ -0,0 +1,99 @@
package dbr
import (
"context"
"database/sql"
"time"
)
// Tx is a transaction for the given Session
type Tx struct {
EventReceiver
Dialect Dialect
*sql.Tx
Timeout time.Duration
// normally we don't call the context cancelFunc.
// however, if we start a tx without explictly tx,
// we will need to call this after the transaction.
Cancel func()
}
func (tx *Tx) GetTimeout() time.Duration {
return tx.Timeout
}
func (sess *Session) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
tx, err := sess.Connection.BeginTx(ctx, opts)
if err != nil {
return nil, sess.EventErr("dbr.begin.error", err)
}
sess.Event("dbr.begin")
stx := &Tx{
EventReceiver: sess,
Dialect: sess.Dialect,
Tx: tx,
Cancel: func() {},
}
deadline, ok := ctx.Deadline()
if ok {
stx.Timeout = deadline.Sub(time.Now())
}
return stx, nil
}
// Begin creates a transaction for the given session
func (sess *Session) Begin() (*Tx, error) {
ctx := context.Background()
var cancel func()
timeout := sess.GetTimeout()
if timeout > 0 {
ctx, cancel = context.WithTimeout(ctx, timeout)
}
stx, err := sess.BeginTx(ctx, nil)
if err != nil {
return nil, err
}
if cancel != nil {
stx.Cancel = cancel
}
return stx, nil
}
// Commit finishes the transaction
func (tx *Tx) Commit() error {
defer tx.Cancel()
err := tx.Tx.Commit()
if err != nil {
return tx.EventErr("dbr.commit.error", err)
}
tx.Event("dbr.commit")
return nil
}
// Rollback cancels the transaction
func (tx *Tx) Rollback() error {
defer tx.Cancel()
err := tx.Tx.Rollback()
if err != nil {
return tx.EventErr("dbr.rollback", err)
}
tx.Event("dbr.rollback")
return nil
}
// RollbackUnlessCommitted rollsback the transaction unless it has already been committed or rolled back.
// Useful to defer tx.RollbackUnlessCommitted() -- so you don't have to handle N failure cases
// Keep in mind the only way to detect an error on the rollback is via the event log.
func (tx *Tx) RollbackUnlessCommitted() {
defer tx.Cancel()
err := tx.Tx.Rollback()
if err == sql.ErrTxDone {
// ok
} else if err != nil {
tx.EventErr("dbr.rollback_unless_committed", err)
} else {
tx.Event("dbr.rollback")
}
}

242
vendor/github.com/gocraft/dbr/types.go generated vendored Normal file
View File

@@ -0,0 +1,242 @@
package dbr
import (
"bytes"
"database/sql"
"database/sql/driver"
"encoding/json"
"time"
)
//
// Your app can use these Null types instead of the defaults. The sole benefit you get is a MarshalJSON method that is not retarded.
//
// NullString is a type that can be null or a string
type NullString struct {
sql.NullString
}
// NullFloat64 is a type that can be null or a float64
type NullFloat64 struct {
sql.NullFloat64
}
// NullInt64 is a type that can be null or an int
type NullInt64 struct {
sql.NullInt64
}
// NullTime is a type that can be null or a time
type NullTime struct {
Time time.Time
Valid bool // Valid is true if Time is not NULL
}
// Value implements the driver Valuer interface.
func (n NullTime) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
return n.Time, nil
}
// NullBool is a type that can be null or a bool
type NullBool struct {
sql.NullBool
}
var nullString = []byte("null")
// MarshalJSON correctly serializes a NullString to JSON
func (n NullString) MarshalJSON() ([]byte, error) {
if n.Valid {
return json.Marshal(n.String)
}
return nullString, nil
}
// MarshalJSON correctly serializes a NullInt64 to JSON
func (n NullInt64) MarshalJSON() ([]byte, error) {
if n.Valid {
return json.Marshal(n.Int64)
}
return nullString, nil
}
// MarshalJSON correctly serializes a NullFloat64 to JSON
func (n NullFloat64) MarshalJSON() ([]byte, error) {
if n.Valid {
return json.Marshal(n.Float64)
}
return nullString, nil
}
// MarshalJSON correctly serializes a NullTime to JSON
func (n NullTime) MarshalJSON() ([]byte, error) {
if n.Valid {
return json.Marshal(n.Time)
}
return nullString, nil
}
// MarshalJSON correctly serializes a NullBool to JSON
func (n NullBool) MarshalJSON() ([]byte, error) {
if n.Valid {
return json.Marshal(n.Bool)
}
return nullString, nil
}
// UnmarshalJSON correctly deserializes a NullString from JSON
func (n *NullString) UnmarshalJSON(b []byte) error {
var s interface{}
if err := json.Unmarshal(b, &s); err != nil {
return err
}
return n.Scan(s)
}
// UnmarshalJSON correctly deserializes a NullInt64 from JSON
func (n *NullInt64) UnmarshalJSON(b []byte) error {
var s json.Number
if err := json.Unmarshal(b, &s); err != nil {
return err
}
if s == "" {
return n.Scan(nil)
}
return n.Scan(s)
}
// UnmarshalJSON correctly deserializes a NullFloat64 from JSON
func (n *NullFloat64) UnmarshalJSON(b []byte) error {
var s interface{}
if err := json.Unmarshal(b, &s); err != nil {
return err
}
return n.Scan(s)
}
// UnmarshalJSON correctly deserializes a NullTime from JSON
func (n *NullTime) UnmarshalJSON(b []byte) error {
// scan for null
if bytes.Equal(b, nullString) {
return n.Scan(nil)
}
// scan for JSON timestamp
var t time.Time
if err := json.Unmarshal(b, &t); err != nil {
return err
}
return n.Scan(t)
}
// UnmarshalJSON correctly deserializes a NullBool from JSON
func (n *NullBool) UnmarshalJSON(b []byte) error {
var s interface{}
if err := json.Unmarshal(b, &s); err != nil {
return err
}
return n.Scan(s)
}
func NewNullInt64(v interface{}) (n NullInt64) {
n.Scan(v)
return
}
func NewNullFloat64(v interface{}) (n NullFloat64) {
n.Scan(v)
return
}
func NewNullString(v interface{}) (n NullString) {
n.Scan(v)
return
}
func NewNullTime(v interface{}) (n NullTime) {
n.Scan(v)
return
}
func NewNullBool(v interface{}) (n NullBool) {
n.Scan(v)
return
}
// The `(*NullTime) Scan(interface{})` and `parseDateTime(string, *time.Location)`
// functions are slightly modified versions of code from the github.com/go-sql-driver/mysql
// package. They work with Postgres and MySQL databases. Potential future
// drivers should ensure these will work for them, or come up with an alternative.
//
// Conforming with its licensing terms the copyright notice and link to the licence
// are available below.
//
// Source: https://github.com/go-sql-driver/mysql/blob/527bcd55aab2e53314f1a150922560174b493034/utils.go#L452-L508
// Copyright notice from original developers:
//
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/
// Scan implements the Scanner interface.
// The value type must be time.Time or string / []byte (formatted time-string),
// otherwise Scan fails.
func (n *NullTime) Scan(value interface{}) error {
var err error
if value == nil {
n.Time, n.Valid = time.Time{}, false
return nil
}
switch v := value.(type) {
case time.Time:
n.Time, n.Valid = v, true
return nil
case []byte:
n.Time, err = parseDateTime(string(v), time.UTC)
n.Valid = (err == nil)
return err
case string:
n.Time, err = parseDateTime(v, time.UTC)
n.Valid = (err == nil)
return err
}
n.Valid = false
return nil
}
func parseDateTime(str string, loc *time.Location) (time.Time, error) {
var t time.Time
var err error
base := "0000-00-00 00:00:00.0000000"
switch len(str) {
case 10, 19, 21, 22, 23, 24, 25, 26:
if str == base[:len(str)] {
return t, err
}
t, err = time.Parse(timeFormat[:len(str)], str)
default:
err = ErrInvalidTimestring
return t, err
}
// Adjust location
if err == nil && loc != time.UTC {
y, mo, d := t.Date()
h, mi, s := t.Clock()
t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil
}
return t, err
}

43
vendor/github.com/gocraft/dbr/union.go generated vendored Normal file
View File

@@ -0,0 +1,43 @@
package dbr
type union struct {
builder []Builder
all bool
}
func Union(builder ...Builder) interface {
Builder
As(string) Builder
} {
return &union{
builder: builder,
}
}
func UnionAll(builder ...Builder) interface {
Builder
As(string) Builder
} {
return &union{
builder: builder,
all: true,
}
}
func (u *union) Build(d Dialect, buf Buffer) error {
for i, b := range u.builder {
if i > 0 {
buf.WriteString(" UNION ")
if u.all {
buf.WriteString("ALL ")
}
}
buf.WriteString(placeholder)
buf.WriteValue(b)
}
return nil
}
func (u *union) As(alias string) Builder {
return as(u, alias)
}

96
vendor/github.com/gocraft/dbr/update.go generated vendored Normal file
View File

@@ -0,0 +1,96 @@
package dbr
// UpdateStmt builds `UPDATE ...`
type UpdateStmt struct {
raw
Table string
Value map[string]interface{}
WhereCond []Builder
}
// Build builds `UPDATE ...` in dialect
func (b *UpdateStmt) Build(d Dialect, buf Buffer) error {
if b.raw.Query != "" {
return b.raw.Build(d, buf)
}
if b.Table == "" {
return ErrTableNotSpecified
}
if len(b.Value) == 0 {
return ErrColumnNotSpecified
}
buf.WriteString("UPDATE ")
buf.WriteString(d.QuoteIdent(b.Table))
buf.WriteString(" SET ")
i := 0
for col, v := range b.Value {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(d.QuoteIdent(col))
buf.WriteString(" = ")
buf.WriteString(placeholder)
buf.WriteValue(v)
i++
}
if len(b.WhereCond) > 0 {
buf.WriteString(" WHERE ")
err := And(b.WhereCond...).Build(d, buf)
if err != nil {
return err
}
}
return nil
}
// Update creates an UpdateStmt
func Update(table string) *UpdateStmt {
return &UpdateStmt{
Table: table,
Value: make(map[string]interface{}),
}
}
// UpdateBySql creates an UpdateStmt with raw query
func UpdateBySql(query string, value ...interface{}) *UpdateStmt {
return &UpdateStmt{
raw: raw{
Query: query,
Value: value,
},
Value: make(map[string]interface{}),
}
}
// Where adds a where condition
func (b *UpdateStmt) Where(query interface{}, value ...interface{}) *UpdateStmt {
switch query := query.(type) {
case string:
b.WhereCond = append(b.WhereCond, Expr(query, value...))
case Builder:
b.WhereCond = append(b.WhereCond, query)
}
return b
}
// Set specifies a key-value pair
func (b *UpdateStmt) Set(column string, value interface{}) *UpdateStmt {
b.Value[column] = value
return b
}
// SetMap specifies a list of key-value pair
func (b *UpdateStmt) SetMap(m map[string]interface{}) *UpdateStmt {
for col, val := range m {
b.Set(col, val)
}
return b
}

97
vendor/github.com/gocraft/dbr/update_builder.go generated vendored Normal file
View File

@@ -0,0 +1,97 @@
package dbr
import (
"context"
"database/sql"
"fmt"
)
type UpdateBuilder struct {
runner
EventReceiver
Dialect Dialect
*UpdateStmt
LimitCount int64
}
func (sess *Session) Update(table string) *UpdateBuilder {
return &UpdateBuilder{
runner: sess,
EventReceiver: sess,
Dialect: sess.Dialect,
UpdateStmt: Update(table),
LimitCount: -1,
}
}
func (tx *Tx) Update(table string) *UpdateBuilder {
return &UpdateBuilder{
runner: tx,
EventReceiver: tx,
Dialect: tx.Dialect,
UpdateStmt: Update(table),
LimitCount: -1,
}
}
func (sess *Session) UpdateBySql(query string, value ...interface{}) *UpdateBuilder {
return &UpdateBuilder{
runner: sess,
EventReceiver: sess,
Dialect: sess.Dialect,
UpdateStmt: UpdateBySql(query, value...),
LimitCount: -1,
}
}
func (tx *Tx) UpdateBySql(query string, value ...interface{}) *UpdateBuilder {
return &UpdateBuilder{
runner: tx,
EventReceiver: tx,
Dialect: tx.Dialect,
UpdateStmt: UpdateBySql(query, value...),
LimitCount: -1,
}
}
func (b *UpdateBuilder) Exec() (sql.Result, error) {
return b.ExecContext(context.Background())
}
func (b *UpdateBuilder) ExecContext(ctx context.Context) (sql.Result, error) {
return exec(ctx, b.runner, b.EventReceiver, b, b.Dialect)
}
func (b *UpdateBuilder) Set(column string, value interface{}) *UpdateBuilder {
b.UpdateStmt.Set(column, value)
return b
}
func (b *UpdateBuilder) SetMap(m map[string]interface{}) *UpdateBuilder {
b.UpdateStmt.SetMap(m)
return b
}
func (b *UpdateBuilder) Where(query interface{}, value ...interface{}) *UpdateBuilder {
b.UpdateStmt.Where(query, value...)
return b
}
func (b *UpdateBuilder) Limit(n uint64) *UpdateBuilder {
b.LimitCount = int64(n)
return b
}
func (b *UpdateBuilder) Build(d Dialect, buf Buffer) error {
err := b.UpdateStmt.Build(b.Dialect, buf)
if err != nil {
return err
}
if b.LimitCount >= 0 {
buf.WriteString(" LIMIT ")
buf.WriteString(fmt.Sprint(b.LimitCount))
}
return nil
}

71
vendor/github.com/gocraft/dbr/util.go generated vendored Normal file
View File

@@ -0,0 +1,71 @@
package dbr
import (
"bytes"
"database/sql/driver"
"reflect"
"unicode"
)
func camelCaseToSnakeCase(name string) string {
buf := new(bytes.Buffer)
runes := []rune(name)
for i := 0; i < len(runes); i++ {
buf.WriteRune(unicode.ToLower(runes[i]))
if i != len(runes)-1 && unicode.IsUpper(runes[i+1]) &&
(unicode.IsLower(runes[i]) || unicode.IsDigit(runes[i]) ||
(i != len(runes)-2 && unicode.IsLower(runes[i+2]))) {
buf.WriteRune('_')
}
}
return buf.String()
}
func structMap(value reflect.Value) map[string]reflect.Value {
m := make(map[string]reflect.Value)
structValue(m, value)
return m
}
var (
typeValuer = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
)
func structValue(m map[string]reflect.Value, value reflect.Value) {
if value.Type().Implements(typeValuer) {
return
}
switch value.Kind() {
case reflect.Ptr:
if value.IsNil() {
return
}
structValue(m, value.Elem())
case reflect.Struct:
t := value.Type()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if field.PkgPath != "" && !field.Anonymous {
// unexported
continue
}
tag := field.Tag.Get("db")
if tag == "-" {
// ignore
continue
}
if tag == "" {
// no tag, but we can record the field name
tag = camelCaseToSnakeCase(field.Name)
}
fieldValue := value.Field(i)
if _, ok := m[tag]; !ok {
m[tag] = fieldValue
}
structValue(m, fieldValue)
}
}
}

202
vendor/github.com/golang/example/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.

27
vendor/github.com/golang/example/stringutil/reverse.go generated vendored Normal file
View File

@@ -0,0 +1,27 @@
/*
Copyright 2014 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package stringutil contains utility functions for working with strings.
package stringutil
// Reverse returns its argument string reversed rune-wise left to right.
func Reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}

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