203
Gopkg.lock
generated
203
Gopkg.lock
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
11
pkg/db/Dockerfile
Normal 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
109
pkg/db/condition.go
Normal 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
88
pkg/db/condition_test.go
Normal 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
283
pkg/db/db.go
Normal 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
2
pkg/db/ddl/devops.sql
Normal 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
31
pkg/db/error.go
Normal 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
55
pkg/db/event.go
Normal 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)
|
||||
}
|
||||
23
pkg/db/schema/devops/V0_1__init.sql
Normal file
23
pkg/db/schema/devops/V0_1__init.sql
Normal 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`)
|
||||
);
|
||||
|
||||
8
pkg/db/schema/devops/V0_2__credential.sql
Normal file
8
pkg/db/schema/devops/V0_2__credential.sql
Normal 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`)
|
||||
);
|
||||
15
pkg/db/schema/devops/V0_3__workspace.sql
Normal file
15
pkg/db/schema/devops/V0_3__workspace.sql
Normal 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
17
pkg/db/scripts/ddl_init.sh
Executable 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
24
pkg/gojenkins/README.md
Normal 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
|
||||
4
pkg/gojenkins/_tests/build_history.txt
Normal file
4
pkg/gojenkins/_tests/build_history.txt
Normal 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 > Console Output" tooltip="Success > 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 > Console Output" tooltip="Success > 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 > Console Output" tooltip="Success > 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>
|
||||
27
pkg/gojenkins/_tests/job.xml
Normal file
27
pkg/gojenkins/_tests/job.xml
Normal 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
118
pkg/gojenkins/artifact.go
Normal 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
480
pkg/gojenkins/build.go
Normal 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
|
||||
}
|
||||
109
pkg/gojenkins/build_history.go
Normal file
109
pkg/gojenkins/build_history.go
Normal 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 > Console Output" tooltip="Failed > 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
|
||||
}
|
||||
20
pkg/gojenkins/constants.go
Normal file
20
pkg/gojenkins/constants.go
Normal 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
225
pkg/gojenkins/credential.go
Normal 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
44
pkg/gojenkins/executor.go
Normal 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"`
|
||||
}
|
||||
95
pkg/gojenkins/fingerprint.go
Normal file
95
pkg/gojenkins/fingerprint.go
Normal 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
77
pkg/gojenkins/folder.go
Normal 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
1067
pkg/gojenkins/jenkins.go
Normal file
File diff suppressed because it is too large
Load Diff
529
pkg/gojenkins/job.go
Normal file
529
pkg/gojenkins/job.go
Normal 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
62
pkg/gojenkins/label.go
Normal 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
230
pkg/gojenkins/node.go
Normal 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
|
||||
}
|
||||
162
pkg/gojenkins/pipeline_model_converter.go
Normal file
162
pkg/gojenkins/pipeline_model_converter.go
Normal 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
75
pkg/gojenkins/plugin.go
Normal 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
158
pkg/gojenkins/queue.go
Normal 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
468
pkg/gojenkins/request.go
Normal 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(¶ms)
|
||||
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(¶ms)
|
||||
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
204
pkg/gojenkins/role.go
Normal 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
61
pkg/gojenkins/utils.go
Normal 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))
|
||||
}
|
||||
21
pkg/gojenkins/utils/utils.go
Normal file
21
pkg/gojenkins/utils/utils.go
Normal 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
94
pkg/gojenkins/views.go
Normal 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
|
||||
}
|
||||
61
pkg/models/devops/common.go
Normal file
61
pkg/models/devops/common.go
Normal 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"
|
||||
)
|
||||
28
pkg/models/devops/membership.go
Normal file
28
pkg/models/devops/membership.go
Normal 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,
|
||||
}
|
||||
}
|
||||
45
pkg/models/devops/project.go
Normal file
45
pkg/models/devops/project.go
Normal 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,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
63
pkg/simple/client/admin_jenkins/jenkins.go
Normal file
63
pkg/simple/client/admin_jenkins/jenkins.go
Normal 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
|
||||
|
||||
}
|
||||
56
pkg/simple/client/devops_mysql/mysql.go
Normal file
56
pkg/simple/client/devops_mysql/mysql.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
49
pkg/simple/client/sonarqube/sonar.go
Normal file
49
pkg/simple/client/sonarqube/sonar.go
Normal 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
|
||||
}
|
||||
94
pkg/utils/idutils/id_utils.go
Normal file
94
pkg/utils/idutils/id_utils.go
Normal 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
|
||||
}
|
||||
24
pkg/utils/idutils/id_utils_test.go
Normal file
24
pkg/utils/idutils/id_utils_test.go
Normal 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)
|
||||
}
|
||||
35
pkg/utils/reflectutils/reflect.go
Normal file
35
pkg/utils/reflectutils/reflect.go
Normal 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
|
||||
}
|
||||
77
pkg/utils/stringutils/string.go
Normal file
77
pkg/utils/stringutils/string.go
Normal 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
21
vendor/github.com/asaskevich/govalidator/LICENSE
generated
vendored
Normal 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
58
vendor/github.com/asaskevich/govalidator/arrays.go
generated
vendored
Normal 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
64
vendor/github.com/asaskevich/govalidator/converter.go
generated
vendored
Normal 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
36
vendor/github.com/asaskevich/govalidator/error.go
generated
vendored
Normal 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
97
vendor/github.com/asaskevich/govalidator/numerics.go
generated
vendored
Normal 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
97
vendor/github.com/asaskevich/govalidator/patterns.go
generated
vendored
Normal 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
616
vendor/github.com/asaskevich/govalidator/types.go
generated
vendored
Normal 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'ex‑Ré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
264
vendor/github.com/asaskevich/govalidator/utils.go
generated
vendored
Normal 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
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
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal 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
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal 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
584
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal 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
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal 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
20
vendor/github.com/gocraft/dbr/LICENSE
generated
vendored
Normal 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
29
vendor/github.com/gocraft/dbr/buffer.go
generated
vendored
Normal 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
13
vendor/github.com/gocraft/dbr/builder.go
generated
vendored
Normal 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
119
vendor/github.com/gocraft/dbr/condition.go
generated
vendored
Normal 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
174
vendor/github.com/gocraft/dbr/dbr.go
generated
vendored
Normal 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
61
vendor/github.com/gocraft/dbr/delete.go
generated
vendored
Normal 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
87
vendor/github.com/gocraft/dbr/delete_builder.go
generated
vendored
Normal 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
15
vendor/github.com/gocraft/dbr/dialect.go
generated
vendored
Normal 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
24
vendor/github.com/gocraft/dbr/dialect/dialect.go
generated
vendored
Normal 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
66
vendor/github.com/gocraft/dbr/dialect/mysql.go
generated
vendored
Normal 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
37
vendor/github.com/gocraft/dbr/dialect/postgresql.go
generated
vendored
Normal 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
40
vendor/github.com/gocraft/dbr/dialect/sqlite3.go
generated
vendored
Normal 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
16
vendor/github.com/gocraft/dbr/errors.go
generated
vendored
Normal 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
40
vendor/github.com/gocraft/dbr/event.go
generated
vendored
Normal 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
18
vendor/github.com/gocraft/dbr/expr.go
generated
vendored
Normal 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
24
vendor/github.com/gocraft/dbr/ident.go
generated
vendored
Normal 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
123
vendor/github.com/gocraft/dbr/insert.go
generated
vendored
Normal 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
126
vendor/github.com/gocraft/dbr/insert_builder.go
generated
vendored
Normal 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
157
vendor/github.com/gocraft/dbr/interpolate.go
generated
vendored
Normal 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
41
vendor/github.com/gocraft/dbr/join.go
generated
vendored
Normal 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
119
vendor/github.com/gocraft/dbr/load.go
generated
vendored
Normal 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
19
vendor/github.com/gocraft/dbr/now.go
generated
vendored
Normal 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
24
vendor/github.com/gocraft/dbr/order.go
generated
vendored
Normal 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
238
vendor/github.com/gocraft/dbr/select.go
generated
vendored
Normal 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
173
vendor/github.com/gocraft/dbr/select_builder.go
generated
vendored
Normal 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
66
vendor/github.com/gocraft/dbr/select_return.go
generated
vendored
Normal 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
99
vendor/github.com/gocraft/dbr/transaction.go
generated
vendored
Normal 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
242
vendor/github.com/gocraft/dbr/types.go
generated
vendored
Normal 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
43
vendor/github.com/gocraft/dbr/union.go
generated
vendored
Normal 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
96
vendor/github.com/gocraft/dbr/update.go
generated
vendored
Normal 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
97
vendor/github.com/gocraft/dbr/update_builder.go
generated
vendored
Normal 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
71
vendor/github.com/gocraft/dbr/util.go
generated
vendored
Normal 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
202
vendor/github.com/golang/example/LICENSE
generated
vendored
Normal 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
27
vendor/github.com/golang/example/stringutil/reverse.go
generated
vendored
Normal 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
Reference in New Issue
Block a user