From 61838e11c2d4812b7d12c93e04f862780bd8000a Mon Sep 17 00:00:00 2001 From: Jeff Grafton Date: Wed, 5 Feb 2020 12:01:28 -0800 Subject: [PATCH 1/2] boskos: move main packages into cmd/ --- boskos/BUILD.bazel | 73 ++++--------------- boskos/cleaner/BUILD.bazel | 5 +- boskos/cmd/boskos/BUILD.bazel | 67 +++++++++++++++++ boskos/{ => cmd/boskos}/boskos.go | 0 boskos/{ => cmd/boskos}/boskos_test.go | 0 boskos/{ => cmd/boskos}/server_client_test.go | 0 boskos/{ => cmd/boskos}/storage_test.go | 0 .../{cleaner/cmd => cmd/cleaner}/BUILD.bazel | 2 +- .../cleaner}/example-deployment.yaml | 0 boskos/{cleaner/cmd => cmd/cleaner}/main.go | 0 boskos/{mason => cmd}/fake-mason/BUILD.bazel | 2 +- boskos/{mason => cmd}/fake-mason/main.go | 0 boskos/{ => cmd}/janitor/BUILD.bazel | 2 +- boskos/{ => cmd}/janitor/gcp_janitor.py | 0 boskos/{ => cmd}/janitor/janitor.go | 0 boskos/{ => cmd}/janitor/janitor_test.go | 0 boskos/{ => cmd}/metrics/BUILD.bazel | 2 +- boskos/{ => cmd}/metrics/metrics.go | 0 boskos/{ => cmd}/reaper/BUILD.bazel | 2 +- boskos/{ => cmd}/reaper/reaper.go | 0 boskos/mason/BUILD.bazel | 5 +- .../jobs/kubernetes/test-infra/janitors.yaml | 2 +- scenarios/kubernetes_janitor.py | 2 +- 23 files changed, 89 insertions(+), 75 deletions(-) create mode 100644 boskos/cmd/boskos/BUILD.bazel rename boskos/{ => cmd/boskos}/boskos.go (100%) rename boskos/{ => cmd/boskos}/boskos_test.go (100%) rename boskos/{ => cmd/boskos}/server_client_test.go (100%) rename boskos/{ => cmd/boskos}/storage_test.go (100%) rename boskos/{cleaner/cmd => cmd/cleaner}/BUILD.bazel (94%) rename boskos/{cleaner/cmd => cmd/cleaner}/example-deployment.yaml (100%) rename boskos/{cleaner/cmd => cmd/cleaner}/main.go (100%) rename boskos/{mason => cmd}/fake-mason/BUILD.bazel (94%) rename boskos/{mason => cmd}/fake-mason/main.go (100%) rename boskos/{ => cmd}/janitor/BUILD.bazel (96%) rename boskos/{ => cmd}/janitor/gcp_janitor.py (100%) rename boskos/{ => cmd}/janitor/janitor.go (100%) rename boskos/{ => cmd}/janitor/janitor_test.go (100%) rename boskos/{ => cmd}/metrics/BUILD.bazel (95%) rename boskos/{ => cmd}/metrics/metrics.go (100%) rename boskos/{ => cmd}/reaper/BUILD.bazel (93%) rename boskos/{ => cmd}/reaper/reaper.go (100%) diff --git a/boskos/BUILD.bazel b/boskos/BUILD.bazel index 0f023cdef0d6..38f4afd15241 100644 --- a/boskos/BUILD.bazel +++ b/boskos/BUILD.bazel @@ -1,73 +1,23 @@ package(default_visibility = ["//visibility:public"]) load("//def:image.bzl", "tags") -load("//prow:def.bzl", "prow_image", "prow_push") -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") - -go_binary( - name = "boskos", - embed = [":go_default_library"], - pure = "on", -) - -prow_image( - name = "image", - visibility = ["//visibility:public"], -) +load("//prow:def.bzl", "prow_push") prow_push( name = "push", images = tags(targets = { "{STABLE_PROW_REPO}/boskos/aws-janitor": "//boskos/aws-janitor:image", "{STABLE_PROW_REPO}/boskos/aws-janitor-boskos": "//boskos/aws-janitor/cmd/aws-janitor-boskos:image", - "{STABLE_PROW_REPO}/boskos/boskos": "//boskos:image", - "{STABLE_PROW_REPO}/boskos/fake-mason": "//boskos/mason/fake-mason:image", - "{STABLE_PROW_REPO}/boskos/cleaner": "//boskos/cleaner/cmd:image", - "{STABLE_PROW_REPO}/boskos/janitor": "//boskos/janitor:image", - "{STABLE_PROW_REPO}/boskos/metrics": "//boskos/metrics:image", - "{STABLE_PROW_REPO}/boskos/reaper": "//boskos/reaper:image", + "{STABLE_PROW_REPO}/boskos/boskos": "//boskos/cmd/boskos:image", + "{STABLE_PROW_REPO}/boskos/fake-mason": "//boskos/cmd/fake-mason:image", + "{STABLE_PROW_REPO}/boskos/cleaner": "//boskos/cmd/cleaner:image", + "{STABLE_PROW_REPO}/boskos/janitor": "//boskos/cmd/janitor:image", + "{STABLE_PROW_REPO}/boskos/metrics": "//boskos/cmd/metrics:image", + "{STABLE_PROW_REPO}/boskos/reaper": "//boskos/cmd/reaper:image", "{STABLE_PROW_REPO}/boskos/cli": "//boskos/cmd/cli:image", }), ) -go_test( - name = "go_default_test", - srcs = [ - "boskos_test.go", - "server_client_test.go", - "storage_test.go", - ], - embed = [":go_default_library"], - deps = [ - "//boskos/client:go_default_library", - "//boskos/common:go_default_library", - "//boskos/crds:go_default_library", - "//boskos/ranch:go_default_library", - "//boskos/storage:go_default_library", - ], -) - -go_library( - name = "go_default_library", - srcs = ["boskos.go"], - importpath = "k8s.io/test-infra/boskos", - deps = [ - "//boskos/common:go_default_library", - "//boskos/crds:go_default_library", - "//boskos/ranch:go_default_library", - "//prow/config:go_default_library", - "//prow/interrupts:go_default_library", - "//prow/logrusutil:go_default_library", - "//prow/metrics:go_default_library", - "//prow/pjutil:go_default_library", - "//prow/simplifypath:go_default_library", - "@com_github_fsnotify_fsnotify//:go_default_library", - "@com_github_prometheus_client_golang//prometheus:go_default_library", - "@com_github_sirupsen_logrus//:go_default_library", - "@com_github_spf13_viper//:go_default_library", - ], -) - filegroup( name = "package-srcs", srcs = glob(["**"]), @@ -82,14 +32,17 @@ filegroup( "//boskos/aws-janitor:all-srcs", "//boskos/cleaner:all-srcs", "//boskos/client:all-srcs", + "//boskos/cmd/boskos:all-srcs", + "//boskos/cmd/cleaner:all-srcs", "//boskos/cmd/cli:all-srcs", + "//boskos/cmd/fake-mason:all-srcs", + "//boskos/cmd/janitor:all-srcs", + "//boskos/cmd/metrics:all-srcs", + "//boskos/cmd/reaper:all-srcs", "//boskos/common:all-srcs", "//boskos/crds:all-srcs", - "//boskos/janitor:all-srcs", "//boskos/mason:all-srcs", - "//boskos/metrics:all-srcs", "//boskos/ranch:all-srcs", - "//boskos/reaper:all-srcs", "//boskos/storage:all-srcs", ], tags = ["automanaged"], diff --git a/boskos/cleaner/BUILD.bazel b/boskos/cleaner/BUILD.bazel index 1b576fb88b41..4875b4a6c7b3 100644 --- a/boskos/cleaner/BUILD.bazel +++ b/boskos/cleaner/BUILD.bazel @@ -21,10 +21,7 @@ filegroup( filegroup( name = "all-srcs", - srcs = [ - ":package-srcs", - "//boskos/cleaner/cmd:all-srcs", - ], + srcs = [":package-srcs"], tags = ["automanaged"], visibility = ["//visibility:public"], ) diff --git a/boskos/cmd/boskos/BUILD.bazel b/boskos/cmd/boskos/BUILD.bazel new file mode 100644 index 000000000000..de432680d4bf --- /dev/null +++ b/boskos/cmd/boskos/BUILD.bazel @@ -0,0 +1,67 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") +load("//prow:def.bzl", "prow_image") + +go_binary( + name = "boskos", + embed = [":go_default_library"], + pure = "on", + visibility = ["//visibility:public"], +) + +prow_image( + name = "image", + visibility = ["//visibility:public"], +) + +go_library( + name = "go_default_library", + srcs = ["boskos.go"], + importpath = "k8s.io/test-infra/boskos/cmd/boskos", + visibility = ["//visibility:private"], + deps = [ + "//boskos/common:go_default_library", + "//boskos/crds:go_default_library", + "//boskos/ranch:go_default_library", + "//prow/config:go_default_library", + "//prow/interrupts:go_default_library", + "//prow/logrusutil:go_default_library", + "//prow/metrics:go_default_library", + "//prow/pjutil:go_default_library", + "//prow/simplifypath:go_default_library", + "@com_github_fsnotify_fsnotify//:go_default_library", + "@com_github_prometheus_client_golang//prometheus:go_default_library", + "@com_github_sirupsen_logrus//:go_default_library", + "@com_github_spf13_viper//:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = [ + "boskos_test.go", + "server_client_test.go", + "storage_test.go", + ], + embed = [":go_default_library"], + deps = [ + "//boskos/client:go_default_library", + "//boskos/common:go_default_library", + "//boskos/crds:go_default_library", + "//boskos/ranch:go_default_library", + "//boskos/storage:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/boskos/boskos.go b/boskos/cmd/boskos/boskos.go similarity index 100% rename from boskos/boskos.go rename to boskos/cmd/boskos/boskos.go diff --git a/boskos/boskos_test.go b/boskos/cmd/boskos/boskos_test.go similarity index 100% rename from boskos/boskos_test.go rename to boskos/cmd/boskos/boskos_test.go diff --git a/boskos/server_client_test.go b/boskos/cmd/boskos/server_client_test.go similarity index 100% rename from boskos/server_client_test.go rename to boskos/cmd/boskos/server_client_test.go diff --git a/boskos/storage_test.go b/boskos/cmd/boskos/storage_test.go similarity index 100% rename from boskos/storage_test.go rename to boskos/cmd/boskos/storage_test.go diff --git a/boskos/cleaner/cmd/BUILD.bazel b/boskos/cmd/cleaner/BUILD.bazel similarity index 94% rename from boskos/cleaner/cmd/BUILD.bazel rename to boskos/cmd/cleaner/BUILD.bazel index f7376b725289..c4f684f6b5b2 100644 --- a/boskos/cleaner/cmd/BUILD.bazel +++ b/boskos/cmd/cleaner/BUILD.bazel @@ -4,7 +4,7 @@ load("//prow:def.bzl", "prow_image") go_library( name = "go_default_library", srcs = ["main.go"], - importpath = "k8s.io/test-infra/boskos/cleaner/cmd", + importpath = "k8s.io/test-infra/boskos/cmd/cleaner", visibility = ["//visibility:private"], deps = [ "//boskos/cleaner:go_default_library", diff --git a/boskos/cleaner/cmd/example-deployment.yaml b/boskos/cmd/cleaner/example-deployment.yaml similarity index 100% rename from boskos/cleaner/cmd/example-deployment.yaml rename to boskos/cmd/cleaner/example-deployment.yaml diff --git a/boskos/cleaner/cmd/main.go b/boskos/cmd/cleaner/main.go similarity index 100% rename from boskos/cleaner/cmd/main.go rename to boskos/cmd/cleaner/main.go diff --git a/boskos/mason/fake-mason/BUILD.bazel b/boskos/cmd/fake-mason/BUILD.bazel similarity index 94% rename from boskos/mason/fake-mason/BUILD.bazel rename to boskos/cmd/fake-mason/BUILD.bazel index 825b42e23f92..eb1b0f989613 100644 --- a/boskos/mason/fake-mason/BUILD.bazel +++ b/boskos/cmd/fake-mason/BUILD.bazel @@ -4,7 +4,7 @@ load("//prow:def.bzl", "prow_image") go_library( name = "go_default_library", srcs = ["main.go"], - importpath = "k8s.io/test-infra/boskos/mason/fake-mason", + importpath = "k8s.io/test-infra/boskos/cmd/fake-mason", visibility = ["//visibility:private"], deps = [ "//boskos/client:go_default_library", diff --git a/boskos/mason/fake-mason/main.go b/boskos/cmd/fake-mason/main.go similarity index 100% rename from boskos/mason/fake-mason/main.go rename to boskos/cmd/fake-mason/main.go diff --git a/boskos/janitor/BUILD.bazel b/boskos/cmd/janitor/BUILD.bazel similarity index 96% rename from boskos/janitor/BUILD.bazel rename to boskos/cmd/janitor/BUILD.bazel index ad725a3920b3..f9a5baf6605c 100644 --- a/boskos/janitor/BUILD.bazel +++ b/boskos/cmd/janitor/BUILD.bazel @@ -19,7 +19,7 @@ go_binary( go_library( name = "go_default_library", srcs = ["janitor.go"], - importpath = "k8s.io/test-infra/boskos/janitor", + importpath = "k8s.io/test-infra/boskos/cmd/janitor", deps = [ "//boskos/client:go_default_library", "//boskos/common:go_default_library", diff --git a/boskos/janitor/gcp_janitor.py b/boskos/cmd/janitor/gcp_janitor.py similarity index 100% rename from boskos/janitor/gcp_janitor.py rename to boskos/cmd/janitor/gcp_janitor.py diff --git a/boskos/janitor/janitor.go b/boskos/cmd/janitor/janitor.go similarity index 100% rename from boskos/janitor/janitor.go rename to boskos/cmd/janitor/janitor.go diff --git a/boskos/janitor/janitor_test.go b/boskos/cmd/janitor/janitor_test.go similarity index 100% rename from boskos/janitor/janitor_test.go rename to boskos/cmd/janitor/janitor_test.go diff --git a/boskos/metrics/BUILD.bazel b/boskos/cmd/metrics/BUILD.bazel similarity index 95% rename from boskos/metrics/BUILD.bazel rename to boskos/cmd/metrics/BUILD.bazel index 90f53a23c09d..3351d760be32 100644 --- a/boskos/metrics/BUILD.bazel +++ b/boskos/cmd/metrics/BUILD.bazel @@ -19,7 +19,7 @@ go_binary( go_library( name = "go_default_library", srcs = ["metrics.go"], - importpath = "k8s.io/test-infra/boskos/metrics", + importpath = "k8s.io/test-infra/boskos/cmd/metrics", deps = [ "//boskos/client:go_default_library", "//boskos/common:go_default_library", diff --git a/boskos/metrics/metrics.go b/boskos/cmd/metrics/metrics.go similarity index 100% rename from boskos/metrics/metrics.go rename to boskos/cmd/metrics/metrics.go diff --git a/boskos/reaper/BUILD.bazel b/boskos/cmd/reaper/BUILD.bazel similarity index 93% rename from boskos/reaper/BUILD.bazel rename to boskos/cmd/reaper/BUILD.bazel index 216c4d4cdf09..ef700fe9da8d 100644 --- a/boskos/reaper/BUILD.bazel +++ b/boskos/cmd/reaper/BUILD.bazel @@ -12,7 +12,7 @@ go_binary( go_library( name = "go_default_library", srcs = ["reaper.go"], - importpath = "k8s.io/test-infra/boskos/reaper", + importpath = "k8s.io/test-infra/boskos/cmd/reaper", deps = [ "//boskos/client:go_default_library", "//boskos/common:go_default_library", diff --git a/boskos/reaper/reaper.go b/boskos/cmd/reaper/reaper.go similarity index 100% rename from boskos/reaper/reaper.go rename to boskos/cmd/reaper/reaper.go diff --git a/boskos/mason/BUILD.bazel b/boskos/mason/BUILD.bazel index 7684309ede22..e00dc17ce485 100644 --- a/boskos/mason/BUILD.bazel +++ b/boskos/mason/BUILD.bazel @@ -44,10 +44,7 @@ filegroup( filegroup( name = "all-srcs", - srcs = [ - ":package-srcs", - "//boskos/mason/fake-mason:all-srcs", - ], + srcs = [":package-srcs"], tags = ["automanaged"], visibility = ["//visibility:public"], ) diff --git a/config/jobs/kubernetes/test-infra/janitors.yaml b/config/jobs/kubernetes/test-infra/janitors.yaml index 995de9febf03..47670cebde3a 100644 --- a/config/jobs/kubernetes/test-infra/janitors.yaml +++ b/config/jobs/kubernetes/test-infra/janitors.yaml @@ -39,7 +39,7 @@ periodics: - experiment/ci-janitor/main.go - --config-path=config/prow/config.yaml - --job-config-path=config/jobs - - --janitor-path=boskos/janitor/gcp_janitor.py + - --janitor-path=boskos/cmd/janitor/gcp_janitor.py image: gcr.io/k8s-testimages/kubekins-e2e:v20200205-602500d-master resources: requests: diff --git a/scenarios/kubernetes_janitor.py b/scenarios/kubernetes_janitor.py index c3ed9b9467a1..a76f76551d58 100755 --- a/scenarios/kubernetes_janitor.py +++ b/scenarios/kubernetes_janitor.py @@ -63,7 +63,7 @@ def clean_project(project, hours=24, dryrun=False, ratelimit=None, filt=None): return CHECKED.add(project) - cmd = ['python', test_infra('boskos/janitor/gcp_janitor.py'), '--project=%s' % project] + cmd = ['python', test_infra('boskos/cmd/janitor/gcp_janitor.py'), '--project=%s' % project] cmd.append('--hour=%d' % hours) if dryrun: cmd.append('--dryrun') From 79d1fd81d5565217f41c2269eae407a895c1dc8e Mon Sep 17 00:00:00 2001 From: Jeff Grafton Date: Wed, 5 Feb 2020 12:31:16 -0800 Subject: [PATCH 2/2] boskos: move handlers and tests into their own package --- boskos/BUILD.bazel | 1 + boskos/cmd/boskos/BUILD.bazel | 22 +- boskos/cmd/boskos/boskos.go | 364 +--------------- boskos/handlers/BUILD.bazel | 43 ++ boskos/handlers/handlers.go | 388 ++++++++++++++++++ .../handlers_test.go} | 9 +- .../boskos => handlers}/server_client_test.go | 9 +- boskos/storage/BUILD.bazel | 12 +- .../{cmd/boskos => storage}/storage_test.go | 4 +- 9 files changed, 460 insertions(+), 392 deletions(-) create mode 100644 boskos/handlers/BUILD.bazel create mode 100644 boskos/handlers/handlers.go rename boskos/{cmd/boskos/boskos_test.go => handlers/handlers_test.go} (98%) rename boskos/{cmd/boskos => handlers}/server_client_test.go (97%) rename boskos/{cmd/boskos => storage}/storage_test.go (96%) diff --git a/boskos/BUILD.bazel b/boskos/BUILD.bazel index 38f4afd15241..1f18ff5effdc 100644 --- a/boskos/BUILD.bazel +++ b/boskos/BUILD.bazel @@ -41,6 +41,7 @@ filegroup( "//boskos/cmd/reaper:all-srcs", "//boskos/common:all-srcs", "//boskos/crds:all-srcs", + "//boskos/handlers:all-srcs", "//boskos/mason:all-srcs", "//boskos/ranch:all-srcs", "//boskos/storage:all-srcs", diff --git a/boskos/cmd/boskos/BUILD.bazel b/boskos/cmd/boskos/BUILD.bazel index de432680d4bf..55be2252b31a 100644 --- a/boskos/cmd/boskos/BUILD.bazel +++ b/boskos/cmd/boskos/BUILD.bazel @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") load("//prow:def.bzl", "prow_image") go_binary( @@ -19,15 +19,14 @@ go_library( importpath = "k8s.io/test-infra/boskos/cmd/boskos", visibility = ["//visibility:private"], deps = [ - "//boskos/common:go_default_library", "//boskos/crds:go_default_library", + "//boskos/handlers:go_default_library", "//boskos/ranch:go_default_library", "//prow/config:go_default_library", "//prow/interrupts:go_default_library", "//prow/logrusutil:go_default_library", "//prow/metrics:go_default_library", "//prow/pjutil:go_default_library", - "//prow/simplifypath:go_default_library", "@com_github_fsnotify_fsnotify//:go_default_library", "@com_github_prometheus_client_golang//prometheus:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", @@ -35,23 +34,6 @@ go_library( ], ) -go_test( - name = "go_default_test", - srcs = [ - "boskos_test.go", - "server_client_test.go", - "storage_test.go", - ], - embed = [":go_default_library"], - deps = [ - "//boskos/client:go_default_library", - "//boskos/common:go_default_library", - "//boskos/crds:go_default_library", - "//boskos/ranch:go_default_library", - "//boskos/storage:go_default_library", - ], -) - filegroup( name = "package-srcs", srcs = glob(["**"]), diff --git a/boskos/cmd/boskos/boskos.go b/boskos/cmd/boskos/boskos.go index 975f2a865123..6c8c45f0eee6 100644 --- a/boskos/cmd/boskos/boskos.go +++ b/boskos/cmd/boskos/boskos.go @@ -17,14 +17,10 @@ limitations under the License. package main import ( - "bytes" - "encoding/json" "flag" "fmt" - "io" "net/http" "runtime" - "strings" "time" "github.com/fsnotify/fsnotify" @@ -32,15 +28,14 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/viper" - "k8s.io/test-infra/boskos/common" "k8s.io/test-infra/boskos/crds" + "k8s.io/test-infra/boskos/handlers" "k8s.io/test-infra/boskos/ranch" "k8s.io/test-infra/prow/config" "k8s.io/test-infra/prow/interrupts" "k8s.io/test-infra/prow/logrusutil" "k8s.io/test-infra/prow/metrics" "k8s.io/test-infra/prow/pjutil" - "k8s.io/test-infra/prow/simplifypath" ) const ( @@ -62,7 +57,7 @@ var ( var ( httpRequestDuration = metrics.HttpRequestDuration("boskos", 0.005, 1200) httpResponseSize = metrics.HttpResponseSize("boskos", 128, 65536) - traceHandler = metrics.TraceHandler(simplifier, httpRequestDuration, httpResponseSize) + traceHandler = metrics.TraceHandler(handlers.NewBoskosSimplifier(), httpRequestDuration, httpResponseSize) ) func init() { @@ -70,20 +65,6 @@ func init() { prometheus.MustRegister(httpResponseSize) } -var simplifier = simplifypath.NewSimplifier(l("", // shadow element mimicing the root - l("acquire"), - l("acquirebystate"), - l("release"), - l("reset"), - l("update"), - l("metric"), -)) - -// l keeps the tree legible -func l(fragment string, children ...simplifypath.Node) simplifypath.Node { - return simplifypath.L(fragment, children...) -} - func main() { logrusutil.ComponentInit("boskos") kubeClientOptions.AddFlags(flag.CommandLine) @@ -129,7 +110,7 @@ func main() { } boskos := &http.Server{ - Handler: traceHandler(NewBoskosHandler(r)), + Handler: traceHandler(handlers.NewBoskosHandler(r)), Addr: ":8080", } @@ -155,342 +136,3 @@ func main() { // signal to the world that we're ready health.ServeReady() } - -//NewBoskosHandler constructs the boskos handler. -func NewBoskosHandler(r *ranch.Ranch) *http.ServeMux { - mux := http.NewServeMux() - mux.Handle("/", handleDefault(r)) - mux.Handle("/acquire", handleAcquire(r)) - mux.Handle("/acquirebystate", handleAcquireByState(r)) - mux.Handle("/release", handleRelease(r)) - mux.Handle("/reset", handleReset(r)) - mux.Handle("/update", handleUpdate(r)) - mux.Handle("/metric", handleMetric(r)) - return mux -} - -// ErrorToStatus translates error into http code -func ErrorToStatus(err error) int { - switch err.(type) { - default: - return http.StatusInternalServerError - case *ranch.OwnerNotMatch: - return http.StatusUnauthorized - case *ranch.ResourceNotFound: - return http.StatusNotFound - case *ranch.ResourceTypeNotFound: - return http.StatusNotFound - case *ranch.StateNotMatch: - return http.StatusConflict - } -} - -// handleDefault: Handler for /, always pass with 200 -func handleDefault(r *ranch.Ranch) http.HandlerFunc { - return func(res http.ResponseWriter, req *http.Request) { - logrus.WithField("handler", "handleDefault").Infof("From %v", req.RemoteAddr) - } -} - -// handleAcquire: Handler for /acquire -// Method: POST -// URLParams: -// Required: type=[string] : type of requested resource -// Required: state=[string] : current state of the requested resource -// Required: dest=[string] : destination state of the requested resource -// Required: owner=[string] : requester of the resource -func handleAcquire(r *ranch.Ranch) http.HandlerFunc { - return func(res http.ResponseWriter, req *http.Request) { - logrus.WithField("handler", "handleStart").Infof("From %v", req.RemoteAddr) - - if req.Method != http.MethodPost { - msg := fmt.Sprintf("Method %v, /acquire only accepts POST.", req.Method) - logrus.Warning(msg) - http.Error(res, msg, http.StatusMethodNotAllowed) - return - } - - // TODO(krzyzacy) - sanitize user input - rtype := req.URL.Query().Get("type") - state := req.URL.Query().Get("state") - dest := req.URL.Query().Get("dest") - owner := req.URL.Query().Get("owner") - requestID := req.URL.Query().Get("request_id") - if rtype == "" || state == "" || dest == "" || owner == "" { - msg := fmt.Sprintf("Type: %v, state: %v, dest: %v, owner: %v, all of them must be set in the request.", rtype, state, dest, owner) - logrus.Warning(msg) - http.Error(res, msg, http.StatusBadRequest) - return - } - - logrus.Infof("Request for a %v %v from %v, dest %v", state, rtype, owner, dest) - - resource, err := r.Acquire(rtype, state, dest, owner, requestID) - - if err != nil { - logrus.WithError(err).Errorf("No available resource") - http.Error(res, err.Error(), ErrorToStatus(err)) - return - } - - resJSON, err := json.Marshal(resource) - if err != nil { - logrus.WithError(err).Errorf("json.Marshal failed: %v, resource will be released", resource) - http.Error(res, err.Error(), ErrorToStatus(err)) - // release the resource, though this is not expected to happen. - err = r.Release(resource.Name, state, owner) - if err != nil { - logrus.WithError(err).Warningf("unable to release resource %s", resource.Name) - } - return - } - logrus.Infof("Resource leased: %v", string(resJSON)) - fmt.Fprint(res, string(resJSON)) - } -} - -// handleAcquireByState: Handler for /acquirebystate -// Method: POST -// URLParams: -// Required: state=[string] : current state of the requested resource -// Required: dest=[string] : destination state of the requested resource -// Required: owner=[string] : requester of the resource -// Required: names=[string] : expected resources names -func handleAcquireByState(r *ranch.Ranch) http.HandlerFunc { - return func(res http.ResponseWriter, req *http.Request) { - logrus.WithField("handler", "handleStart").Infof("From %v", req.RemoteAddr) - - if req.Method != http.MethodPost { - msg := fmt.Sprintf("Method %v, /acquire only accepts POST.", req.Method) - logrus.Warning(msg) - http.Error(res, msg, http.StatusMethodNotAllowed) - return - } - - // TODO(krzyzacy) - sanitize user input - state := req.URL.Query().Get("state") - dest := req.URL.Query().Get("dest") - owner := req.URL.Query().Get("owner") - names := req.URL.Query().Get("names") - if state == "" || dest == "" || owner == "" || names == "" { - msg := fmt.Sprintf( - "state: %v, dest: %v, owner: %v, names: %v - all of them must be set in the request.", - state, dest, owner, names) - logrus.Warning(msg) - http.Error(res, msg, http.StatusBadRequest) - return - } - rNames := strings.Split(names, ",") - logrus.Infof("Request resources %s at state %v from %v, to state %v", - strings.Join(rNames, ", "), state, owner, dest) - - resources, err := r.AcquireByState(state, dest, owner, rNames) - - if err != nil { - logrus.WithError(err).Errorf("No available resources") - http.Error(res, err.Error(), ErrorToStatus(err)) - return - } - - resBytes := new(bytes.Buffer) - - if err := json.NewEncoder(resBytes).Encode(resources); err != nil { - logrus.WithError(err).Errorf("json.Marshal failed: %v, resources will be released", resources) - http.Error(res, err.Error(), ErrorToStatus(err)) - for _, resource := range resources { - err := r.Release(resource.Name, state, owner) - if err != nil { - logrus.WithError(err).Warningf("unable to release resource %s", resource.Name) - } - } - return - } - logrus.Infof("Resource leased: %v", resBytes.String()) - fmt.Fprint(res, resBytes.String()) - } -} - -// handleRelease: Handler for /release -// Method: POST -// URL Params: -// Required: name=[string] : name of finished resource -// Required: owner=[string] : owner of the resource -// Required: dest=[string] : dest state -func handleRelease(r *ranch.Ranch) http.HandlerFunc { - return func(res http.ResponseWriter, req *http.Request) { - logrus.WithField("handler", "handleDone").Infof("From %v", req.RemoteAddr) - - if req.Method != http.MethodPost { - msg := fmt.Sprintf("Method %v, /release only accepts POST.", req.Method) - logrus.Warning(msg) - http.Error(res, msg, http.StatusMethodNotAllowed) - return - } - - name := req.URL.Query().Get("name") - dest := req.URL.Query().Get("dest") - owner := req.URL.Query().Get("owner") - if name == "" || dest == "" || owner == "" { - msg := fmt.Sprintf("Name: %v, dest: %v, owner: %v, all of them must be set in the request.", name, dest, owner) - logrus.Warning(msg) - http.Error(res, msg, http.StatusBadRequest) - return - } - - if err := r.Release(name, dest, owner); err != nil { - logrus.WithError(err).Errorf("Done failed: %v - %v (from %v)", name, dest, owner) - http.Error(res, err.Error(), ErrorToStatus(err)) - return - } - - logrus.Infof("Done with resource %v, set to state %v", name, dest) - } -} - -// handleReset: Handler for /reset -// Method: POST -// URL Params: -// Required: type=[string] : type of resource in interest -// Required: state=[string] : original state -// Required: dest=[string] : dest state, for expired resource -// Required: expire=[durationStr*] resource has not been updated since before {expire}. -func handleReset(r *ranch.Ranch) http.HandlerFunc { - return func(res http.ResponseWriter, req *http.Request) { - logrus.WithField("handler", "handleReset").Infof("From %v", req.RemoteAddr) - - if req.Method != http.MethodPost { - msg := fmt.Sprintf("Method %v, /reset only accepts POST.", req.Method) - logrus.Warning(msg) - http.Error(res, msg, http.StatusMethodNotAllowed) - return - } - - rtype := req.URL.Query().Get("type") - state := req.URL.Query().Get("state") - expireStr := req.URL.Query().Get("expire") - dest := req.URL.Query().Get("dest") - - logrus.Infof("%v, %v, %v, %v", rtype, state, expireStr, dest) - - if rtype == "" || state == "" || expireStr == "" || dest == "" { - msg := fmt.Sprintf("Type: %v, state: %v, expire: %v, dest: %v, all of them must be set in the request.", rtype, state, expireStr, dest) - logrus.Warning(msg) - http.Error(res, msg, http.StatusBadRequest) - return - } - - expire, err := time.ParseDuration(expireStr) - if err != nil { - logrus.WithError(err).Errorf("Invalid expiration: %v", expireStr) - http.Error(res, err.Error(), http.StatusBadRequest) - return - } - - rmap, err := r.Reset(rtype, state, expire, dest) - if err != nil { - logrus.WithError(err).Errorf("could not reset states") - http.Error(res, err.Error(), http.StatusBadRequest) - return - } - resJSON, err := json.Marshal(rmap) - if err != nil { - logrus.WithError(err).Errorf("json.Marshal failed: %v", rmap) - http.Error(res, err.Error(), ErrorToStatus(err)) - return - } - logrus.Infof("Resource %v reset successful, %d items moved to state %v", rtype, len(rmap), dest) - fmt.Fprint(res, string(resJSON)) - } -} - -// handleUpdate: Handler for /update -// Method: POST -// URLParams -// Required: name=[string] : name of target resource -// Required: owner=[string] : owner of the resource -// Required: state=[string] : current state of the resource -// Optional: userData=[common.UserData] : user data id to update -func handleUpdate(r *ranch.Ranch) http.HandlerFunc { - return func(res http.ResponseWriter, req *http.Request) { - logrus.WithField("handler", "handleUpdate").Infof("From %v", req.RemoteAddr) - - if req.Method != http.MethodPost { - msg := fmt.Sprintf("Method %v, /update only accepts POST.", req.Method) - logrus.Warning(msg) - http.Error(res, msg, http.StatusMethodNotAllowed) - return - } - - name := req.URL.Query().Get("name") - owner := req.URL.Query().Get("owner") - state := req.URL.Query().Get("state") - - if name == "" || owner == "" || state == "" { - msg := fmt.Sprintf("Name: %v, owner: %v, state : %v, all of them must be set in the request.", name, owner, state) - logrus.Warning(msg) - http.Error(res, msg, http.StatusBadRequest) - return - } - - var userData common.UserData - - if req.Body != nil { - err := json.NewDecoder(req.Body).Decode(&userData) - switch { - case err == io.EOF: - // empty body - case err != nil: - logrus.WithError(err).Warning("Unable to read from response body") - http.Error(res, err.Error(), http.StatusBadRequest) - return - } - } - - if err := r.Update(name, owner, state, &userData); err != nil { - logrus.WithError(err).Errorf("Update failed: %v - %v (%v)", name, state, owner) - http.Error(res, err.Error(), ErrorToStatus(err)) - return - } - - logrus.Infof("Updated resource %v", name) - } -} - -// handleMetric: Handler for /metric -// Method: GET -func handleMetric(r *ranch.Ranch) http.HandlerFunc { - return func(res http.ResponseWriter, req *http.Request) { - logrus.WithField("handler", "handleMetric").Infof("From %v", req.RemoteAddr) - - if req.Method != http.MethodGet { - logrus.Warningf("[BadRequest]method %v, expect GET", req.Method) - http.Error(res, "/metric only accepts GET", http.StatusMethodNotAllowed) - return - } - - rtype := req.URL.Query().Get("type") - if rtype == "" { - msg := "Type must be set in the request." - logrus.Warning(msg) - http.Error(res, msg, http.StatusBadRequest) - return - } - - metric, err := r.Metric(rtype) - if err != nil { - logrus.WithError(err).Errorf("Metric for %s failed", rtype) - http.Error(res, err.Error(), ErrorToStatus(err)) - return - } - - js, err := json.Marshal(metric) - if err != nil { - logrus.WithError(err).Error("Fail to marshal metric") - http.Error(res, err.Error(), ErrorToStatus(err)) - return - } - - res.Header().Set("Content-Type", "application/json") - res.Write(js) - } -} diff --git a/boskos/handlers/BUILD.bazel b/boskos/handlers/BUILD.bazel new file mode 100644 index 000000000000..fb82bb4ec357 --- /dev/null +++ b/boskos/handlers/BUILD.bazel @@ -0,0 +1,43 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["handlers.go"], + importpath = "k8s.io/test-infra/boskos/handlers", + visibility = ["//visibility:public"], + deps = [ + "//boskos/common:go_default_library", + "//boskos/ranch:go_default_library", + "//prow/simplifypath:go_default_library", + "@com_github_sirupsen_logrus//:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = [ + "handlers_test.go", + "server_client_test.go", + ], + embed = [":go_default_library"], + deps = [ + "//boskos/client:go_default_library", + "//boskos/common:go_default_library", + "//boskos/crds:go_default_library", + "//boskos/ranch:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/boskos/handlers/handlers.go b/boskos/handlers/handlers.go new file mode 100644 index 000000000000..c8193afb72b4 --- /dev/null +++ b/boskos/handlers/handlers.go @@ -0,0 +1,388 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package handlers + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "time" + + "github.com/sirupsen/logrus" + "k8s.io/test-infra/boskos/common" + "k8s.io/test-infra/boskos/ranch" + "k8s.io/test-infra/prow/simplifypath" +) + +// l keeps the tree legible +func l(fragment string, children ...simplifypath.Node) simplifypath.Node { + return simplifypath.L(fragment, children...) +} + +// NewBoskosSimplifier returns a Simplifier for all Boskos URIs to be used with metrics collection. +func NewBoskosSimplifier() simplifypath.Simplifier { + return simplifypath.NewSimplifier(l("", // shadow element mimicing the root + l("acquire"), + l("acquirebystate"), + l("release"), + l("reset"), + l("update"), + l("metric"), + )) +} + +//NewBoskosHandler constructs the boskos handler. +func NewBoskosHandler(r *ranch.Ranch) *http.ServeMux { + mux := http.NewServeMux() + mux.Handle("/", handleDefault(r)) + mux.Handle("/acquire", handleAcquire(r)) + mux.Handle("/acquirebystate", handleAcquireByState(r)) + mux.Handle("/release", handleRelease(r)) + mux.Handle("/reset", handleReset(r)) + mux.Handle("/update", handleUpdate(r)) + mux.Handle("/metric", handleMetric(r)) + return mux +} + +// errorToStatus translates error into http code +func errorToStatus(err error) int { + switch err.(type) { + default: + return http.StatusInternalServerError + case *ranch.OwnerNotMatch: + return http.StatusUnauthorized + case *ranch.ResourceNotFound: + return http.StatusNotFound + case *ranch.ResourceTypeNotFound: + return http.StatusNotFound + case *ranch.StateNotMatch: + return http.StatusConflict + } +} + +// handleDefault: Handler for /, always pass with 200 +func handleDefault(r *ranch.Ranch) http.HandlerFunc { + return func(res http.ResponseWriter, req *http.Request) { + logrus.WithField("handler", "handleDefault").Infof("From %v", req.RemoteAddr) + } +} + +// handleAcquire: Handler for /acquire +// Method: POST +// URLParams: +// Required: type=[string] : type of requested resource +// Required: state=[string] : current state of the requested resource +// Required: dest=[string] : destination state of the requested resource +// Required: owner=[string] : requester of the resource +func handleAcquire(r *ranch.Ranch) http.HandlerFunc { + return func(res http.ResponseWriter, req *http.Request) { + logrus.WithField("handler", "handleStart").Infof("From %v", req.RemoteAddr) + + if req.Method != http.MethodPost { + msg := fmt.Sprintf("Method %v, /acquire only accepts POST.", req.Method) + logrus.Warning(msg) + http.Error(res, msg, http.StatusMethodNotAllowed) + return + } + + // TODO(krzyzacy) - sanitize user input + rtype := req.URL.Query().Get("type") + state := req.URL.Query().Get("state") + dest := req.URL.Query().Get("dest") + owner := req.URL.Query().Get("owner") + requestID := req.URL.Query().Get("request_id") + if rtype == "" || state == "" || dest == "" || owner == "" { + msg := fmt.Sprintf("Type: %v, state: %v, dest: %v, owner: %v, all of them must be set in the request.", rtype, state, dest, owner) + logrus.Warning(msg) + http.Error(res, msg, http.StatusBadRequest) + return + } + + logrus.Infof("Request for a %v %v from %v, dest %v", state, rtype, owner, dest) + + resource, err := r.Acquire(rtype, state, dest, owner, requestID) + + if err != nil { + logrus.WithError(err).Errorf("No available resource") + http.Error(res, err.Error(), errorToStatus(err)) + return + } + + resJSON, err := json.Marshal(resource) + if err != nil { + logrus.WithError(err).Errorf("json.Marshal failed: %v, resource will be released", resource) + http.Error(res, err.Error(), errorToStatus(err)) + // release the resource, though this is not expected to happen. + err = r.Release(resource.Name, state, owner) + if err != nil { + logrus.WithError(err).Warningf("unable to release resource %s", resource.Name) + } + return + } + logrus.Infof("Resource leased: %v", string(resJSON)) + fmt.Fprint(res, string(resJSON)) + } +} + +// handleAcquireByState: Handler for /acquirebystate +// Method: POST +// URLParams: +// Required: state=[string] : current state of the requested resource +// Required: dest=[string] : destination state of the requested resource +// Required: owner=[string] : requester of the resource +// Required: names=[string] : expected resources names +func handleAcquireByState(r *ranch.Ranch) http.HandlerFunc { + return func(res http.ResponseWriter, req *http.Request) { + logrus.WithField("handler", "handleStart").Infof("From %v", req.RemoteAddr) + + if req.Method != http.MethodPost { + msg := fmt.Sprintf("Method %v, /acquire only accepts POST.", req.Method) + logrus.Warning(msg) + http.Error(res, msg, http.StatusMethodNotAllowed) + return + } + + // TODO(krzyzacy) - sanitize user input + state := req.URL.Query().Get("state") + dest := req.URL.Query().Get("dest") + owner := req.URL.Query().Get("owner") + names := req.URL.Query().Get("names") + if state == "" || dest == "" || owner == "" || names == "" { + msg := fmt.Sprintf( + "state: %v, dest: %v, owner: %v, names: %v - all of them must be set in the request.", + state, dest, owner, names) + logrus.Warning(msg) + http.Error(res, msg, http.StatusBadRequest) + return + } + rNames := strings.Split(names, ",") + logrus.Infof("Request resources %s at state %v from %v, to state %v", + strings.Join(rNames, ", "), state, owner, dest) + + resources, err := r.AcquireByState(state, dest, owner, rNames) + + if err != nil { + logrus.WithError(err).Errorf("No available resources") + http.Error(res, err.Error(), errorToStatus(err)) + return + } + + resBytes := new(bytes.Buffer) + + if err := json.NewEncoder(resBytes).Encode(resources); err != nil { + logrus.WithError(err).Errorf("json.Marshal failed: %v, resources will be released", resources) + http.Error(res, err.Error(), errorToStatus(err)) + for _, resource := range resources { + err := r.Release(resource.Name, state, owner) + if err != nil { + logrus.WithError(err).Warningf("unable to release resource %s", resource.Name) + } + } + return + } + logrus.Infof("Resource leased: %v", resBytes.String()) + fmt.Fprint(res, resBytes.String()) + } +} + +// handleRelease: Handler for /release +// Method: POST +// URL Params: +// Required: name=[string] : name of finished resource +// Required: owner=[string] : owner of the resource +// Required: dest=[string] : dest state +func handleRelease(r *ranch.Ranch) http.HandlerFunc { + return func(res http.ResponseWriter, req *http.Request) { + logrus.WithField("handler", "handleDone").Infof("From %v", req.RemoteAddr) + + if req.Method != http.MethodPost { + msg := fmt.Sprintf("Method %v, /release only accepts POST.", req.Method) + logrus.Warning(msg) + http.Error(res, msg, http.StatusMethodNotAllowed) + return + } + + name := req.URL.Query().Get("name") + dest := req.URL.Query().Get("dest") + owner := req.URL.Query().Get("owner") + if name == "" || dest == "" || owner == "" { + msg := fmt.Sprintf("Name: %v, dest: %v, owner: %v, all of them must be set in the request.", name, dest, owner) + logrus.Warning(msg) + http.Error(res, msg, http.StatusBadRequest) + return + } + + if err := r.Release(name, dest, owner); err != nil { + logrus.WithError(err).Errorf("Done failed: %v - %v (from %v)", name, dest, owner) + http.Error(res, err.Error(), errorToStatus(err)) + return + } + + logrus.Infof("Done with resource %v, set to state %v", name, dest) + } +} + +// handleReset: Handler for /reset +// Method: POST +// URL Params: +// Required: type=[string] : type of resource in interest +// Required: state=[string] : original state +// Required: dest=[string] : dest state, for expired resource +// Required: expire=[durationStr*] resource has not been updated since before {expire}. +func handleReset(r *ranch.Ranch) http.HandlerFunc { + return func(res http.ResponseWriter, req *http.Request) { + logrus.WithField("handler", "handleReset").Infof("From %v", req.RemoteAddr) + + if req.Method != http.MethodPost { + msg := fmt.Sprintf("Method %v, /reset only accepts POST.", req.Method) + logrus.Warning(msg) + http.Error(res, msg, http.StatusMethodNotAllowed) + return + } + + rtype := req.URL.Query().Get("type") + state := req.URL.Query().Get("state") + expireStr := req.URL.Query().Get("expire") + dest := req.URL.Query().Get("dest") + + logrus.Infof("%v, %v, %v, %v", rtype, state, expireStr, dest) + + if rtype == "" || state == "" || expireStr == "" || dest == "" { + msg := fmt.Sprintf("Type: %v, state: %v, expire: %v, dest: %v, all of them must be set in the request.", rtype, state, expireStr, dest) + logrus.Warning(msg) + http.Error(res, msg, http.StatusBadRequest) + return + } + + expire, err := time.ParseDuration(expireStr) + if err != nil { + logrus.WithError(err).Errorf("Invalid expiration: %v", expireStr) + http.Error(res, err.Error(), http.StatusBadRequest) + return + } + + rmap, err := r.Reset(rtype, state, expire, dest) + if err != nil { + logrus.WithError(err).Errorf("could not reset states") + http.Error(res, err.Error(), http.StatusBadRequest) + return + } + resJSON, err := json.Marshal(rmap) + if err != nil { + logrus.WithError(err).Errorf("json.Marshal failed: %v", rmap) + http.Error(res, err.Error(), errorToStatus(err)) + return + } + logrus.Infof("Resource %v reset successful, %d items moved to state %v", rtype, len(rmap), dest) + fmt.Fprint(res, string(resJSON)) + } +} + +// handleUpdate: Handler for /update +// Method: POST +// URLParams +// Required: name=[string] : name of target resource +// Required: owner=[string] : owner of the resource +// Required: state=[string] : current state of the resource +// Optional: userData=[common.UserData] : user data id to update +func handleUpdate(r *ranch.Ranch) http.HandlerFunc { + return func(res http.ResponseWriter, req *http.Request) { + logrus.WithField("handler", "handleUpdate").Infof("From %v", req.RemoteAddr) + + if req.Method != http.MethodPost { + msg := fmt.Sprintf("Method %v, /update only accepts POST.", req.Method) + logrus.Warning(msg) + http.Error(res, msg, http.StatusMethodNotAllowed) + return + } + + name := req.URL.Query().Get("name") + owner := req.URL.Query().Get("owner") + state := req.URL.Query().Get("state") + + if name == "" || owner == "" || state == "" { + msg := fmt.Sprintf("Name: %v, owner: %v, state : %v, all of them must be set in the request.", name, owner, state) + logrus.Warning(msg) + http.Error(res, msg, http.StatusBadRequest) + return + } + + var userData common.UserData + + if req.Body != nil { + err := json.NewDecoder(req.Body).Decode(&userData) + switch { + case err == io.EOF: + // empty body + case err != nil: + logrus.WithError(err).Warning("Unable to read from response body") + http.Error(res, err.Error(), http.StatusBadRequest) + return + } + } + + if err := r.Update(name, owner, state, &userData); err != nil { + logrus.WithError(err).Errorf("Update failed: %v - %v (%v)", name, state, owner) + http.Error(res, err.Error(), errorToStatus(err)) + return + } + + logrus.Infof("Updated resource %v", name) + } +} + +// handleMetric: Handler for /metric +// Method: GET +func handleMetric(r *ranch.Ranch) http.HandlerFunc { + return func(res http.ResponseWriter, req *http.Request) { + logrus.WithField("handler", "handleMetric").Infof("From %v", req.RemoteAddr) + + if req.Method != http.MethodGet { + logrus.Warningf("[BadRequest]method %v, expect GET", req.Method) + http.Error(res, "/metric only accepts GET", http.StatusMethodNotAllowed) + return + } + + rtype := req.URL.Query().Get("type") + if rtype == "" { + msg := "Type must be set in the request." + logrus.Warning(msg) + http.Error(res, msg, http.StatusBadRequest) + return + } + + metric, err := r.Metric(rtype) + if err != nil { + logrus.WithError(err).Errorf("Metric for %s failed", rtype) + http.Error(res, err.Error(), errorToStatus(err)) + return + } + + js, err := json.Marshal(metric) + if err != nil { + logrus.WithError(err).Error("Fail to marshal metric") + http.Error(res, err.Error(), errorToStatus(err)) + return + } + + res.Header().Set("Content-Type", "application/json") + res.Write(js) + } +} diff --git a/boskos/cmd/boskos/boskos_test.go b/boskos/handlers/handlers_test.go similarity index 98% rename from boskos/cmd/boskos/boskos_test.go rename to boskos/handlers/handlers_test.go index a174f1abc9e4..1d4092a7cdbc 100644 --- a/boskos/cmd/boskos/boskos_test.go +++ b/boskos/handlers/handlers_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main +package handlers import ( "encoding/json" @@ -30,6 +30,13 @@ import ( "k8s.io/test-infra/boskos/ranch" ) +// json does not serialized time with nanosecond precision +func now() time.Time { + format := "2006-01-02 15:04:05.000" + now, _ := time.Parse(format, time.Now().Format(format)) + return now +} + var ( fakeNow = now() testTTL = time.Millisecond diff --git a/boskos/cmd/boskos/server_client_test.go b/boskos/handlers/server_client_test.go similarity index 97% rename from boskos/cmd/boskos/server_client_test.go rename to boskos/handlers/server_client_test.go index f34ab0c469c0..754c9592bea5 100644 --- a/boskos/cmd/boskos/server_client_test.go +++ b/boskos/handlers/server_client_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main +package handlers import ( "fmt" @@ -31,13 +31,6 @@ import ( "k8s.io/test-infra/boskos/ranch" ) -// json does not serialized time with nanosecond precision -func now() time.Time { - format := "2006-01-02 15:04:05.000" - now, _ := time.Parse(format, time.Now().Format(format)) - return now -} - func makeTestBoskos(r *ranch.Ranch) *httptest.Server { handler := NewBoskosHandler(r) return httptest.NewServer(handler) diff --git a/boskos/storage/BUILD.bazel b/boskos/storage/BUILD.bazel index e71c9659a5e9..bc424c30560c 100644 --- a/boskos/storage/BUILD.bazel +++ b/boskos/storage/BUILD.bazel @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", @@ -8,6 +8,16 @@ go_library( deps = ["//boskos/common:go_default_library"], ) +go_test( + name = "go_default_test", + srcs = ["storage_test.go"], + embed = [":go_default_library"], + deps = [ + "//boskos/common:go_default_library", + "//boskos/crds:go_default_library", + ], +) + filegroup( name = "package-srcs", srcs = glob(["**"]), diff --git a/boskos/cmd/boskos/storage_test.go b/boskos/storage/storage_test.go similarity index 96% rename from boskos/cmd/boskos/storage_test.go rename to boskos/storage/storage_test.go index 25aa27e588ff..4400aefd660a 100644 --- a/boskos/cmd/boskos/storage_test.go +++ b/boskos/storage/storage_test.go @@ -14,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main +// This is an xtest because it imports the crds package, but the crds package +// also imports storage, creating a cycle. +package storage_test import ( "fmt"