From e682f5dd733183efcfea8cc4a9089fc9fdfc2995 Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Tue, 6 Jul 2021 13:28:55 +0000 Subject: [PATCH 01/16] delete scala based project --- .scalafmt.conf | 13 - .travis.yml | 29 - .travis/deploy.sh | 6 - .travis/test.sh | 11 - build.sbt | 102 -- project/Dependencies.scala | 24 - project/build.properties | 1 - project/plugins.sbt | 7 - release.sbt | 22 - src/main/resources/reference.conf | 60 - .../com/github/everpeace/k8s/package.scala | 98 -- .../k8s/throttler/KubeThrottler.scala | 87 -- .../k8s/throttler/KubeThrottlerConfig.scala | 61 - .../everpeace/k8s/throttler/Routes.scala | 239 --- .../ClusterThrottleControllerLogic.scala | 74 - .../controller/ThrottleController.scala | 823 ---------- .../controller/ThrottleControllerLogic.scala | 57 - .../controller/ThrottleRequestHandler.scala | 177 --- .../everpeace/k8s/throttler/crd/package.scala | 34 - .../crd/v1alpha1/CalculatedThreshold.scala | 53 - .../crd/v1alpha1/ClusterThrottle.scala | 133 -- .../crd/v1alpha1/ResourceAmount.scala | 160 -- .../v1alpha1/TemporaryThresholdOverride.scala | 113 -- .../k8s/throttler/crd/v1alpha1/Throttle.scala | 120 -- .../k8s/throttler/crd/v1alpha1/package.scala | 164 -- .../ClusterThrottleControllerMetrics.scala | 87 -- .../k8s/throttler/metrics/MetricsBase.scala | 120 -- .../metrics/ThrottleControllerMetrics.scala | 86 - .../github/everpeace/util/ActorWatcher.scala | 40 - .../com/github/everpeace/util/Injection.scala | 64 - .../github/everpeace/util/Isomorphism.scala | 52 - .../io/k8s/pkg/scheduler/api/v1/package.scala | 94 -- src/test/resources/reference.conf | 2 - .../k8s/LabelSelectorMatcherSpec.scala | 78 - .../everpeace/k8s/PodTotalRequestsSpec.scala | 59 - .../everpeace/k8s/ResourceListSpec.scala | 44 - .../Skuber2_1_0CanParseMatchFieldsSpec.scala | 116 -- .../everpeace/k8s/throttler/RoutesSpec.scala | 411 ----- .../ClusterThrottleControllerLogicSpec.scala | 1389 ----------------- .../ThrottleControllerLogicSpec.scala | 1362 ---------------- .../crd/AbstractThrottleSyntaxSpec.scala | 123 -- .../crd/CalculateThresholdSyntaxSpec.scala | 121 -- .../crd/ClusterThrottleFormatSpec.scala | 183 --- .../crd/ClusterThrottleSelectorSpec.scala | 112 -- .../crd/TemporaryThresholdOverrideSpec.scala | 136 -- .../throttler/crd/ThrottleFormatSpec.scala | 183 --- .../throttler/crd/ThrottleSelectorSpec.scala | 64 - .../scheduler/api/v1/ExtenderFormatSpec.scala | 145 -- version.sbt | 1 - 49 files changed, 7740 deletions(-) delete mode 100644 .scalafmt.conf delete mode 100644 .travis.yml delete mode 100755 .travis/deploy.sh delete mode 100755 .travis/test.sh delete mode 100644 build.sbt delete mode 100644 project/Dependencies.scala delete mode 100644 project/build.properties delete mode 100644 project/plugins.sbt delete mode 100644 release.sbt delete mode 100644 src/main/resources/reference.conf delete mode 100644 src/main/scala/com/github/everpeace/k8s/package.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/KubeThrottler.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/KubeThrottlerConfig.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/Routes.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/controller/ClusterThrottleControllerLogic.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/controller/ThrottleController.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/controller/ThrottleControllerLogic.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/controller/ThrottleRequestHandler.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/crd/package.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/CalculatedThreshold.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/ClusterThrottle.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/ResourceAmount.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/TemporaryThresholdOverride.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/Throttle.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/package.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/metrics/ClusterThrottleControllerMetrics.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/metrics/MetricsBase.scala delete mode 100644 src/main/scala/com/github/everpeace/k8s/throttler/metrics/ThrottleControllerMetrics.scala delete mode 100644 src/main/scala/com/github/everpeace/util/ActorWatcher.scala delete mode 100644 src/main/scala/com/github/everpeace/util/Injection.scala delete mode 100644 src/main/scala/com/github/everpeace/util/Isomorphism.scala delete mode 100644 src/main/scala/io/k8s/pkg/scheduler/api/v1/package.scala delete mode 100644 src/test/resources/reference.conf delete mode 100644 src/test/scala/com/github/everpeace/k8s/LabelSelectorMatcherSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/PodTotalRequestsSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/ResourceListSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/Skuber2_1_0CanParseMatchFieldsSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/throttler/RoutesSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/throttler/controller/ClusterThrottleControllerLogicSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/throttler/controller/ThrottleControllerLogicSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/throttler/crd/AbstractThrottleSyntaxSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/throttler/crd/CalculateThresholdSyntaxSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/throttler/crd/ClusterThrottleFormatSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/throttler/crd/ClusterThrottleSelectorSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/throttler/crd/TemporaryThresholdOverrideSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/throttler/crd/ThrottleFormatSpec.scala delete mode 100644 src/test/scala/com/github/everpeace/k8s/throttler/crd/ThrottleSelectorSpec.scala delete mode 100644 src/test/scala/io/k8s/pkg/scheduler/api/v1/ExtenderFormatSpec.scala delete mode 100644 version.sbt diff --git a/.scalafmt.conf b/.scalafmt.conf deleted file mode 100644 index d66b47b..0000000 --- a/.scalafmt.conf +++ /dev/null @@ -1,13 +0,0 @@ -# Only format files tracked by git. -project.git = true # Default: false - -# Manually exclude files to format. -project.excludeFilters = ["target/"] - -# format configuration -style = defaultWithAlign -align = most -maxColumn = 100 # Default: 80 -verticalMultilineAtDefinitionSite = true # Default: false -newlines.beforeImplicitKWInVerticalMultiline = false # Default: false -newlines.afterImplicitKWInVerticalMultiline = true # Default: false \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2b08c24..0000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -sudo: true -services: -- docker -language: scala -scala: -- 2.12.6 -jdk: -- openjdk8 -cache: - directories: - - "$HOME/.ivy2/cache" - - "$HOME/.sbt/launchers" -before_cache: -- find $HOME/.sbt -name "*.lock" | xargs rm -- find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm -script: -- ".travis/test.sh" -deploy: - provider: script - script: ".travis/deploy.sh" - on: - tags: true - scala: 2.12.6 -branches: - only: - - master - - develop - # release tags - - "/^v?[0-9\\.]+/" diff --git a/.travis/deploy.sh b/.travis/deploy.sh deleted file mode 100755 index c23d04b..0000000 --- a/.travis/deploy.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -set -e - -grep -v "SNAPSHOT" version.sbt >/dev/null 2>&1 -echo "$DOCKER_PASSWORD" | docker login -u everpeace --password-stdin -sbt ++${TRAVIS_SCALA_VERSION} docker:stage docker:publish diff --git a/.travis/test.sh b/.travis/test.sh deleted file mode 100755 index 2353ec4..0000000 --- a/.travis/test.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -e - -sbt ++${TRAVIS_SCALA_VERSION} test - -if [ "${TRAVIS_PULL_REQUEST}" == "false" ]; then - if grep "SNAPSHOT" version.sbt >/dev/null 2>&1; then - echo "$DOCKER_PASSWORD" | docker login -u everpeace --password-stdin - sbt ++${TRAVIS_SCALA_VERSION} docker:stage docker:publish - fi -fi diff --git a/build.sbt b/build.sbt deleted file mode 100644 index 46a2a98..0000000 --- a/build.sbt +++ /dev/null @@ -1,102 +0,0 @@ -import Dependencies._ - -lazy val root = (project in file(".")) - .enablePlugins(JavaAgent, JavaAppPackaging, AshScriptPlugin, DockerPlugin, AutomateHeaderPlugin) - .settings( - name := "kube-throttler", - inThisBuild( - List( - organization := "com.github.everpeace", - scalaVersion := "2.12.8" - )), - // run options - fork in run := true, - javaAgents += "org.aspectj" % "aspectjweaver" % "1.8.13", - javaOptions in Universal += "-Dorg.aspectj.tracing.factory=default", - // - // compile options - // - scalacOptions ++= Seq( - "-unchecked", - "-deprecation", - "-feature", - "-language:_", - "-encoding", - "UTF-8", - "-Ywarn-unused-import", -// "-Xlog-implicits", - "-Ypartial-unification" - ), - // - // dependencies - // - resolvers ++= Seq( - Resolver.bintrayRepo("everpeace", "maven"), - Resolver.bintrayRepo("hseeberger", "maven") - ), - libraryDependencies ++= Seq( - skuber, - catsCore, - akkHttpPlayJson, - healthchecks.core, - healthchecks.probe, - kamon.core, - kamon.systemMetrics, - kamon.akka, - kamon.akkaHttp, - kamon.prometheus, - logback, - scalaLogging - ) ++ Seq( - scalaTest % Test, - akkaHttpTestKit % Test, - kamon.logReporter % Test - ), - // - // sbt-header - // - licenses += ("Apache-2.0", new URL("https://www.apache.org/licenses/LICENSE-2.0.txt")), - organizationName := "Shingo Omura ", - startYear := Some(2018), - headerLicense := Some( - HeaderLicense.ALv2("2018", "Shingo Omura ")), - homepage := Some(url("https://github.com/everpeace/kube-throttler")), - // - // Scalafmt setting - // - scalafmtOnCompile := true, - scalafmtTestOnCompile := true, - // - // pom - // - pomIncludeRepository := (_ => false), - pomExtra := - https://github.com/everpeace/kube-throttler - scm:git:git@github.com:everpeace/kube-throttler - - - - everpeace - Shingo Omura - https://github.com/everpeace/ - - , - // - // sbt-native-packager docker plugin - // - dockerUsername := Some("everpeace"), - packageName in Docker := "kube-throttler", - maintainer in Docker := "Shingo Omura ", - dockerBaseImage := "adoptopenjdk/openjdk8:x86_64-alpine-jdk8u191-b12", - dockerExposedPorts := Seq( - 4321 /* kube-throttle (kube-scheduler extender) */, - 5005 /* for jvm debug */, - 9095 /* prometheus */ - ), - dockerUpdateLatest := true, - bashScriptExtraDefines += """addJava "-Dconfig.file=${app_home}/../conf/application.conf"""", - // - // sbt-release (release step is defined at release.sbt) - // - releaseVersionBump := sbtrelease.Version.Bump.Next - ) diff --git a/project/Dependencies.scala b/project/Dependencies.scala deleted file mode 100644 index 1a44a7b..0000000 --- a/project/Dependencies.scala +++ /dev/null @@ -1,24 +0,0 @@ -import sbt._ - -object Dependencies { - lazy val skuber = "io.skuber" %% "skuber" % "2.2.0" - lazy val catsCore = "org.typelevel" %% "cats-core" % "1.2.0" - lazy val akkHttpPlayJson = "de.heikoseeberger" %% "akka-http-play-json" % "1.21.0" - object healthchecks { - lazy val version = "0.4.0" - lazy val core = "com.github.everpeace" %% "healthchecks-core" % version - lazy val probe = "com.github.everpeace" %% "healthchecks-k8s-probes" % version - } - lazy val logback = "ch.qos.logback" % "logback-classic" % "1.2.3" - lazy val scalaLogging = "com.typesafe.scala-logging" %% "scala-logging" % "3.9.0" - object kamon { - lazy val core = "io.kamon" %% "kamon-core" % "1.1.1" - lazy val systemMetrics = "io.kamon" %% "kamon-system-metrics" % "1.0.0" - lazy val akka = "io.kamon" %% "kamon-akka-2.5" % "1.1.2" - lazy val akkaHttp = "io.kamon" %% "kamon-akka-http-2.5" % "1.0.1" - lazy val prometheus = "io.kamon" %% "kamon-prometheus" % "1.1.1" - lazy val logReporter = "io.kamon" %% "kamon-log-reporter" % "0.6.8" - } - lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.0.5" - lazy val akkaHttpTestKit = "com.typesafe.akka" %% "akka-http-testkit" % "10.1.4" -} diff --git a/project/build.properties b/project/build.properties deleted file mode 100644 index 31334bb..0000000 --- a/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.1.1 diff --git a/project/plugins.sbt b/project/plugins.sbt deleted file mode 100644 index 8d8adac..0000000 --- a/project/plugins.sbt +++ /dev/null @@ -1,7 +0,0 @@ -resolvers += Resolver.bintrayRepo("kamon-io", "sbt-plugins") -addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.15") -addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.9") -addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.15") -addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.0.0") -addSbtPlugin("io.kamon" % "sbt-aspectj-runner" % "1.1.0") -addSbtPlugin("com.lightbend.sbt" % "sbt-javaagent" % "0.1.4") diff --git a/release.sbt b/release.sbt deleted file mode 100644 index 837cc02..0000000 --- a/release.sbt +++ /dev/null @@ -1,22 +0,0 @@ -import sbtrelease._ -import sbtrelease.ReleasePlugin.autoImport.ReleaseTransformations._ - -releaseProcess := Seq[ReleaseStep]( - releaseStepCommand("headerCheck"), - checkSnapshotDependencies, // : ReleaseStep - inquireVersions, // : ReleaseStep - runClean, // : ReleaseStep - runTest, // : ReleaseStep - setReleaseVersion, // : ReleaseStep - commitReleaseVersion, // : ReleaseStep, performs the initial git checks - tagRelease, // : ReleaseStep -// NOTE: Travis CI will buld/publish docker images. -// See .travis.yml -// releaseStepCommand("docker:clean"), -// releaseStepCommand("docker:stage"), -// releaseStepCommand("docker:build"), -// releaseStepCommand("docker:publish"), - setNextVersion, // : ReleaseStep - commitNextVersion, // : ReleaseStep - pushChanges // : ReleaseStep, also checks that an upstream branch is properly configured -) diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf deleted file mode 100644 index 9f0fdfa..0000000 --- a/src/main/resources/reference.conf +++ /dev/null @@ -1,60 +0,0 @@ -kube-throttler { - # throttler name, the throttler instance is responsible for 'Throttle's with spec.throttleName equals to the name - throttler-name = null - # target scheduler names, the throttler instance only counts running pods which is responsible for this scheduler names. - target-scheduler-names = null - - # dispatcher name for http server(healthcheck and kubernetes scheduler extender) - # server-dispatcher-name = "my-blocking-dispatcher" - - # in bytes - watch-buffer-size = 10000 - graceful-shutdown-duration = 30 s - - reconcile-temporary-threshold-overrides-interval = 5 s - status-force-update-interval = 15 m - - ask-timeout = 10 s - host = "0.0.0.0" - port = 4321 -} - -skuber { - akka { - # The ID of the dispatcher to use by Skuber. If undefined or empty the default Akka dispatcher is used. - dispatcher = "" - } - - watch { - # The idle timeout duration for any connections used by skuber `watch` requests - if null the timeout is infinite. - idle-timeout = null - } - - watch-continuously { - # Timeout that is passed to the kubernetes cluster for all list/watch calls. This limits the duration of the call, - # regardless of any activity or inactivity. - request-timeout = 1800s - - # The idle timeout for the connection before if closes due to inactivity. The idle-timeout must be a great value - # than that used for timeout-seconds. - idle-timeout = 3600s - - # The idle timeout for the connection pool used by the Watch Source (each source has its own connection pool). - # When the pool is no longer used by the source and the idle time has been exceeded the pool will shutdown and - # reclaim the unused resources. - pool-idle-timeout = 7200s - } -} - -kamon.util.filters { - "akka.tracked-actor" { - includes = [ "kube-throttler/user/**"] - excludes = [ "kube-throttler/system/**" ] - } - "akka.tracked-dispatcher" { - includes = [ "akka.actor.default-dispatcher" ] - } - "akka.tracked-router" { - includes = [ "kube-throttler/user/**" ] - } -} diff --git a/src/main/scala/com/github/everpeace/k8s/package.scala b/src/main/scala/com/github/everpeace/k8s/package.scala deleted file mode 100644 index 0cee3fb..0000000 --- a/src/main/scala/com/github/everpeace/k8s/package.scala +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace - -import com.github.everpeace.util.Injection.==> -import skuber.LabelSelector._ -import skuber.Resource.{Quantity, ResourceList} -import skuber.{LabelSelector, ObjectResource, Pod} - -package object k8s { - - type ObjectKey = (String, String) - - def isScheduledAndNotFinished(pod: Pod): Boolean = { - pod.status.nonEmpty && pod.spec.nonEmpty && - pod.status.get.phase.nonEmpty && - pod.status.get.phase.get != Pod.Phase.Succeeded && - pod.status.get.phase.get != Pod.Phase.Failed && - pod.spec.get.nodeName.nonEmpty - } - - // TODO: change explicit convertion(__.key) to context aware conversion(__.==>) - implicit val or2key: ObjectResource ==> ObjectKey = new ==>[ObjectResource, ObjectKey] { - override def to: ObjectResource => ObjectKey = o => o.namespace -> o.name - } - - implicit class ObjectKeyExtractor(o: ObjectResource) { - def key: ObjectKey = o.namespace -> o.name - } - - implicit class LabelSelectorMatcher(selector: LabelSelector) { - def matches(labels: Map[String, String]): Boolean = - selector.requirements.forall(_.matches(labels)) - } - - implicit class LabelSelectorRequirementMatcher(requirement: LabelSelector.Requirement) { - def matches(labels: Map[String, String]): Boolean = requirement match { - case ExistsRequirement(key) => - labels.keys.exists(_ == key) - case NotExistsRequirement(key) => - !labels.keys.exists(_ == key) - case IsEqualRequirement(key, value) => - labels.get(key).contains(value) - case IsNotEqualRequirement(key, value) => - !labels.get(key).contains(value) - case InRequirement(key, values) => - labels.get(key).exists(values.contains(_)) - case NotInRequirement(key, values) => - !labels.get(key).exists(values.contains(_)) - } - } - - implicit class QuantityAddition(qa: Quantity) { - def add(qb: Quantity) = { - Quantity((qa.amount + qb.amount).toString()) - } - } - - val zeroResourceList = Map.empty[String, Quantity] - implicit class ResourceListAddtion(ra: ResourceList) { - def add(rb: ResourceList): ResourceList = (ra.toList ++ rb.toList).groupBy(_._1).map { - case (k, vs) => - k -> vs.map(_._2).foldLeft(Quantity("0"))(_ add _) - } - } - - implicit class PodTotalRequests(pod: Pod) { - def totalRequests: ResourceList = { - pod.spec - .map { status => - (for { - c <- status.containers - res <- c.resources - } yield res.requests).foldLeft(Map.empty: ResourceList)(_ add _) - } - .getOrElse(Map.empty: ResourceList) - } - } - - implicit val quantityOrdering: cats.Order[Quantity] = new cats.Order[Quantity] { - override def compare(x: Quantity, y: Quantity): Int = x.amount.compare(y.amount) - } - -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/KubeThrottler.scala b/src/main/scala/com/github/everpeace/k8s/throttler/KubeThrottler.scala deleted file mode 100644 index 274a160..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/KubeThrottler.scala +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler - -import akka.actor.ActorSystem -import akka.http.scaladsl.Http -import akka.stream.ActorMaterializer -import com.github.everpeace.k8s.throttler.controller.{ThrottleController, ThrottleRequestHandler} -import com.github.everpeace.util.ActorWatcher -import kamon.Kamon -import kamon.prometheus.PrometheusReporter -import kamon.system.SystemMetrics - -import scala.concurrent.duration._ -import scala.concurrent.Await -import scala.util.{Failure, Success} - -object KubeThrottler extends App { - Kamon.addReporter(new PrometheusReporter()) - SystemMetrics.startCollecting() - - private def gracefulShutdown( - system: ActorSystem, - gracefulShutdownDuration: FiniteDuration - ): Unit = { - system.terminate() - Await.result(system.whenTerminated, gracefulShutdownDuration) - } - - implicit val system: ActorSystem = ActorSystem("kube-throttler") - implicit val mat: ActorMaterializer = ActorMaterializer() - val logger = system.log - logger.info("starting kube-throttler") - - val k8s = skuber.k8sInit - val config = KubeThrottleConfig(system.settings.config) - - logger.info(s"throttler-name = ${config.throttlerName}") - logger.info(s"target-scheduler-names = ${config.targetSchedulerNames}") - - scala.sys.addShutdownHook { - logger.info( - "detected a signal. shutting down kube-throttler (graceful period = {}).", - config.gracefulShutdownDuration - ) - gracefulShutdown(system, config.gracefulShutdownDuration) - } - - val requestHandleActor = - system.actorOf(ThrottleRequestHandler.props(), "throttle-request-handler") - val throttler = - system.actorOf(ThrottleController.props(requestHandleActor, k8s, config), "throttle-controller") - val throttlerWatcher = - system.actorOf(ActorWatcher.props(throttler), name = "throttle-controller-watcher") - val routes = new Routes(requestHandleActor, - throttlerWatcher, - config.throttlerAskTimeout, - config.serverDispatcherName).all - - Http() - .bindAndHandle(routes, config.host, config.port) - .onComplete { - case Success(binding) => - logger.info("successfully started kube-throttler on {}", binding.localAddress) - case Failure(_) => - logger.error( - "failed creating http server. shutting down kube-throttler. (graceful period = {})", - config.gracefulShutdownDuration - ) - gracefulShutdown(system, config.gracefulShutdownDuration) - sys.exit(1) - }(system.dispatcher) -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/KubeThrottlerConfig.scala b/src/main/scala/com/github/everpeace/k8s/throttler/KubeThrottlerConfig.scala deleted file mode 100644 index 2562207..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/KubeThrottlerConfig.scala +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler - -import akka.util.Timeout -import com.typesafe.config.Config - -import scala.concurrent.duration.{Duration, FiniteDuration} -import scala.collection.JavaConverters._ - -case class KubeThrottleConfig(config: Config) { - private val basePath = "kube-throttler" - private val throttlerConfig = config.getConfig(basePath) - - def throttlerName: String = throttlerConfig.getString("throttler-name") - - def serverDispatcherName: Option[String] = - if (throttlerConfig.hasPath("server-dispatcher-name")) { - Some(throttlerConfig.getString("server-dispatcher-name")) - } else { - None - } - def watchBufferSize: Int = throttlerConfig.getInt("watch-buffer-size") - - def targetSchedulerNames: List[String] = - throttlerConfig.getStringList("target-scheduler-names").asScala.toList - - def gracefulShutdownDuration: FiniteDuration = Duration.fromNanos( - throttlerConfig.getDuration(s"graceful-shutdown-duration").toNanos - ) - def throttlerAskTimeout: Timeout = - Timeout( - Duration.fromNanos( - throttlerConfig.getDuration("ask-timeout").toNanos - )) - - def reconcileTemporaryThresholdInterval: FiniteDuration = Duration.fromNanos( - throttlerConfig.getDuration("reconcile-temporary-threshold-overrides-interval").toNanos - ) - - def statusForceUpdateInterval: FiniteDuration = Duration.fromNanos( - throttlerConfig.getDuration("status-force-update-interval").toNanos - ) - - def host: String = throttlerConfig.getString("host") - def port: Int = throttlerConfig.getInt("port") -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/Routes.scala b/src/main/scala/com/github/everpeace/k8s/throttler/Routes.scala deleted file mode 100644 index 8416841..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/Routes.scala +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler - -import akka.actor.{ActorRef, ActorSystem} -import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.model.ContentTypes._ -import akka.http.scaladsl.model.headers.`Content-Type` -import akka.http.scaladsl.server.Directives._ -import akka.pattern.ask -import akka.stream.ActorMaterializer -import akka.util.Timeout -import com.github.everpeace.healthchecks._ -import com.github.everpeace.healthchecks.k8s._ -import com.github.everpeace.k8s._ -import com.github.everpeace.k8s.throttler.controller.ThrottleRequestHandler._ -import de.heikoseeberger.akkahttpplayjson.PlayJsonSupport -import io.k8s.pkg.scheduler.api.v1 -import io.k8s.pkg.scheduler.api.v1.ExtenderArgs -import com.github.everpeace.healthchecks - -import scala.util.{Failure, Success} -import cats.implicits._ -import com.github.everpeace.util.ActorWatcher.IsTargetAlive - -class Routes( - requestHandleActor: ActorRef, - controllerWatcher: ActorRef, - askTimeout: Timeout, - serverDispatcherName: Option[String] = None, -)(implicit - system: ActorSystem, - materializer: ActorMaterializer) - extends PlayJsonSupport { - - import v1.Implicits._ - - implicit private val _askTimeout = askTimeout - implicit private val ec = - serverDispatcherName.map(system.dispatchers.lookup(_)).getOrElse(system.dispatcher) - - private val controllerAlive = asyncHealthCheck("isThrottleControllerLive") { - (controllerWatcher ? IsTargetAlive).mapTo[Boolean].map { isAlive => - if (isAlive) { - healthchecks.healthy - } else { - healthchecks.unhealthy("throttle-controller is not alive.") - } - } - } - private val requestHandlerReady = asyncHealthCheck("isThrottleControllerReady") { - (requestHandleActor ? IsReady).mapTo[Boolean].map { isReady => - if (isReady) { - healthchecks.healthy - } else { - healthchecks.unhealthy("throttle-request-handler is not ready.") - } - } - } - - def all = - readinessProbe(requestHandlerReady).toRoute ~ livenessProbe(controllerAlive).toRoute ~ checkThrottle ~ preemptIfNotThrottled - - def errorResult(arg: ExtenderArgs, message: String): v1.ExtenderFilterResult = { - val nodeNames = if (arg.nodes.nonEmpty) { - arg.nodes.get.items.map(_.name) - } else { - arg.nodenames - } - - v1.ExtenderFilterResult( - nodes = arg.nodes.map( - _.copy( - items = List.empty - )), - nodenames = List.empty, - failedNodes = nodeNames.map(nodeName => nodeName -> message).toMap, - error = Option(message) - ) - } - - def unSchedulableResult(arg: ExtenderArgs, message: String): v1.ExtenderFilterResult = { - val nodeNames = if (arg.nodes.nonEmpty) { - arg.nodes.get.items.map(_.name) - } else { - arg.nodenames - } - - v1.ExtenderFilterResult( - nodes = arg.nodes.map( - _.copy( - items = List.empty - )), - nodenames = List.empty, - failedNodes = nodeNames.map(nodeName => nodeName -> message).toMap - ) - } - - def schedulableResult(arg: ExtenderArgs): v1.ExtenderFilterResult = { - v1.ExtenderFilterResult( - nodes = arg.nodes, - nodenames = arg.nodenames, - failedNodes = Map.empty - ) - } - - def checkThrottle = path("check_throttle") { - post { - logRequestResult("preempt_if_not_throttled") { - entity(as[v1.ExtenderArgs]) { extenderArgs => - val pod = extenderArgs.pod - system.log.info("checking throttle status for pod {}", pod.key) - onComplete((requestHandleActor ? CheckThrottleRequest(pod)).mapTo[CheckThrottleResponse]) { - // some throttles are active!! no nodes are schedulable - case Success( - Throttled(p, - activeThrottles, - activeClusterThrottles, - noSpaceThrottles, - noSpaceClusterThrottles)) if p == pod => - val activeThrottleMessage = activeThrottles.toList.toNel - .map { thrs => - val names = thrs.map(thr => thr.namespace -> thr.name).toList - s"throttles[active]=${names.mkString(",")}" - } - - val activeClusterThrottleMessage = activeClusterThrottles.toList.toNel - .map { thrs => - val names = thrs.map(_.name).toList - s"clusterthrottles[active]=${names.mkString(",")}" - } - - val noSpaceThrottleMessage = noSpaceThrottles.toList.toNel - .map { thrs => - val names = thrs.map(thr => thr.namespace -> thr.name).toList - s"throttles[insufficient]=${names.mkString(",")}" - } - - val noSpaceClusterThrottleMessage = noSpaceClusterThrottles.toList.toNel - .map { thrs => - val names = thrs.map(_.name).toList - s"clusterthrottles[insufficient]=${names.mkString(",")}" - } - - val aggregatedMessage = - List(activeThrottleMessage, - activeClusterThrottleMessage, - noSpaceThrottleMessage, - noSpaceClusterThrottleMessage).filter(_.nonEmpty).map(_.get).mkString(", ") - - val message = s"pod ${pod.key} is unschedulable due to $aggregatedMessage" - system.log.info(message) - complete(unSchedulableResult(extenderArgs, message)) - - // no throttles are active!! all nodes are schedulable. - case Success(NotThrottled(p)) if p == pod => - system.log.info( - "pod {} is schedulable because no 'throttled' throttles/clusterthrottles for the pod.", - pod.key) - complete(schedulableResult(extenderArgs)) - - case Success(NotReady) => - val message = s"throttler is not ready in checking throttle status of pod ${pod.key}" - system.log.error(message) - complete(errorResult(extenderArgs, message)) - - // failure. no nodes are schedulable. - case Failure(exp) => - val message = - s"exception occurred in checking throttles for pod ${pod.key}: ${exp.getMessage}" - system.log.error(message) - complete(errorResult(extenderArgs, message)) - } - } - } - } - } - - val noVictims = v1.ExtenderPreemptionResult(Map.empty) - def echoedResult(args: v1.ExtenderPreemptionArgs): v1.ExtenderPreemptionResult = - v1.ExtenderPreemptionResult( - args.nodeNameToVictims.mapValues( - victims => - v1.MetaVictims( - pods = victims.pods.map(p => v1.MetaPod(p.uid)), - numPDBViolations = victims.numPDBViolations - ))) - - def preemptIfNotThrottled = path("preempt_if_not_throttled") { - post { - logRequestResult("preempt_if_not_throttled") { - entity(as[v1.ExtenderPreemptionArgs]) { extenderArgs => - val pod = extenderArgs.pod - system.log.info("checking throttle status of pod {} for preemption", pod.key) - onComplete((requestHandleActor ? CheckThrottleRequest(pod)).mapTo[CheckThrottleResponse]) { - case Success(Throttled(_, _, _, _, _)) => - val message = s"pod ${pod.key} is throttled. no victims should be selected." - system.log.info(message) - complete(noVictims) - case Success(NotThrottled(_)) => - val result = echoedResult(extenderArgs) - val numVictims = result.nodeNameToMetaVictims.mapValues(_.pods.length) - val message = s"pod ${pod.key} is throttled. echo victims: $numVictims" - system.log.info(message) - complete(result) - case Success(NotReady) => - val message = - s"throttler is not ready in checking throttle status of pod ${pod.key} for preemption" - system.log.error(message) - complete(StatusCodes.InternalServerError, - List(`Content-Type`(`application/json`)), - "{}") - case Failure(exp) => - val message = - s"exception occurred in checking throttles of pod ${pod.key} for preemption: ${exp.getMessage}" - system.log.error(message) - complete(StatusCodes.InternalServerError, - List(`Content-Type`(`application/json`)), - "{}") - } - } - } - } - } -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/controller/ClusterThrottleControllerLogic.scala b/src/main/scala/com/github/everpeace/k8s/throttler/controller/ClusterThrottleControllerLogic.scala deleted file mode 100644 index 887cc92..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/controller/ClusterThrottleControllerLogic.scala +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.controller -import com.github.everpeace.k8s._ -import com.github.everpeace.k8s.throttler.crd.v1alpha1 -import com.github.everpeace.k8s.throttler.crd.v1alpha1.Implicits._ -import com.github.everpeace.k8s.throttler.crd.v1alpha1.ResourceAmount -import com.github.everpeace.util.Injection._ -import skuber._ -import cats.instances.list._ -import com.github.everpeace.k8s.throttler.KubeThrottleConfig - -trait ClusterThrottleControllerLogic { - - def isClusterThrottleAlreadyActiveFor( - pod: Pod, - ns: Namespace, - clthrottle: v1alpha1.ClusterThrottle - ): Boolean = - clthrottle.isAlreadyActiveFor(pod, clthrottle.isTarget(pod, ns)) - - def isClusterThrottleInsufficientFor( - pod: Pod, - ns: Namespace, - clthrottle: v1alpha1.ClusterThrottle - ): Boolean = - clthrottle.isInsufficientFor(pod, clthrottle.isTarget(pod, ns)) - - def calcNextClusterThrottleStatuses( - targetClusterThrottles: Set[v1alpha1.ClusterThrottle], - podsInAllNamespaces: => Set[Pod], - namespaces: Map[String, Namespace], - at: skuber.Timestamp - )(implicit - conf: KubeThrottleConfig - ): List[(ObjectKey, v1alpha1.ClusterThrottle.Status)] = { - - for { - clthrottle <- targetClusterThrottles.toList - - matchedPods = podsInAllNamespaces.filter { p => - if (namespaces.contains(p.namespace)) { - clthrottle.spec.selector.matches(p, namespaces(p.namespace)) - } else { - false - } - } - runningPods = matchedPods.filter(isScheduledAndNotFinished).toList - runningTotal = runningPods.==>[List[ResourceAmount]].foldLeft(zeroResourceAmount)(_ add _) - nextStatus = clthrottle.spec.statusFor(runningTotal, at)( - v1alpha1.ClusterThrottle.Status.apply) - - toUpdate <- if (clthrottle.status.needToUpdateWith(nextStatus)) { - List(clthrottle.key -> nextStatus) - } else { - List.empty - } - } yield toUpdate - } -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/controller/ThrottleController.scala b/src/main/scala/com/github/everpeace/k8s/throttler/controller/ThrottleController.scala deleted file mode 100644 index dcd7bb1..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/controller/ThrottleController.scala +++ /dev/null @@ -1,823 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.controller - -import akka.Done -import akka.actor.{Actor, ActorLogging, ActorRef, Cancellable, PoisonPill, Props} -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{Merge, Sink, Source} -import com.github.everpeace.k8s._ -import com.github.everpeace.k8s.throttler.KubeThrottleConfig -import com.github.everpeace.k8s.throttler.controller.ThrottleController._ -import com.github.everpeace.k8s.throttler.controller.ThrottleRequestHandler.Initialize -import com.github.everpeace.k8s.throttler.crd.v1alpha1 -import com.github.everpeace.k8s.throttler.crd.v1alpha1.Implicits._ -import com.github.everpeace.k8s.throttler.metrics.{ - ClusterThrottleControllerMetrics, - ThrottleControllerMetrics -} -import play.api.libs.json._ -import skuber._ -import skuber.ResourceSpecification.Subresources -import skuber.api.client.EventType -import skuber.api.client.LoggingContext._ -import skuber.json.format.{namespaceFormat, namespaceListFmt, podFormat, podListFmt} - -import scala.collection.mutable -import scala.concurrent.{ExecutionContext, Future} -import scala.util.{Failure, Success, Try} - -class ThrottleController( - requestHandlerActor: ActorRef - )(implicit - val k8s: K8SRequestContext, - config: KubeThrottleConfig) - extends Actor - with ActorLogging - with ThrottleControllerLogic - with ThrottleControllerMetrics - with ClusterThrottleControllerLogic - with ClusterThrottleControllerMetrics { - - implicit private val system = context.system - implicit private val mat = ActorMaterializer() - implicit private val ec = context.dispatcher - - private var cancelWhenRestart: mutable.ListBuffer[Cancellable] = mutable.ListBuffer.empty - - private val k8sMap: mutable.Map[String, K8SRequestContext] = mutable.Map(k8s.namespaceName -> k8s) - - private def schedulerName(pod: Pod): Option[String] = pod.spec.flatMap(_.schedulerName) - - private val isPodResponsible = (pod: Pod) => - schedulerName(pod).exists(n => config.targetSchedulerNames.contains(n)) - private val isPodCompleted = (pod: Pod) => - pod.status.exists(st => - st.phase.contains(Pod.Phase.Succeeded) || st.phase.contains(Pod.Phase.Failed)) - private val isThrottleResponsible = (thr: v1alpha1.Throttle) => - config.throttlerName == thr.spec.throttlerName - private val isClusterThrottleResponsible = (clthr: v1alpha1.ClusterThrottle) => - config.throttlerName == clthr.spec.throttlerName - - private val cache = new { - val namespaces = new ObjectResourceCache[Namespace]() - val pods = new ObjectResourceCache[Pod](isPodResponsible) - val throttles = new ObjectResourceCache[v1alpha1.Throttle](isThrottleResponsible) - val clusterThrottles = - new ObjectResourceCache[v1alpha1.ClusterThrottle](isClusterThrottleResponsible) - } - - override def preRestart(reason: Throwable, message: Option[Any]): Unit = { - cancelWhenRestart.foreach(cancellable => - Try { - if (!cancellable.isCancelled) cancellable.cancel() - }) - super.preRestart(reason, message) - } - - override def preStart(): Unit = { - super.preStart() - log.info("starting ThrottleController actor (path = {})", self.path) - val now = java.time.ZonedDateTime.now() - val syncAll = for { - // init cache - _ <- cache.namespaces.init() - _ <- cache.clusterThrottles.init() - _ <- cache.throttles.init() - _ <- cache.pods.init(p => !isPodCompleted(p)) // completed pods need not to cache - // reconcile all throttles/clthrottles - _ <- reconcileAllClusterThrottles(at = now) - _ <- reconcileAllThrottles(at = now) - // get the latest resource versions again to watch - nsVersion <- latestResourceList[Namespace].map(_.metadata.map(_.resourceVersion)) - clthrottleVersion <- latestResourceList[v1alpha1.ClusterThrottle] - .map(_.metadata.map(_.resourceVersion)) - throttleVersion <- latestResourceList[v1alpha1.Throttle] - .map(_.metadata.map(_.resourceVersion)) - podVersion <- latestResourceList[Pod].map(_.metadata.map(_.resourceVersion)) - } yield (nsVersion, clthrottleVersion, throttleVersion, podVersion) - - syncAll.onComplete { - case scala.util.Success(v) => - val (namespaceVersion, clthrottleVersion, throttleVersion, podVersion) = v - log.info( - s"latest throttle list resource version = $throttleVersion, latest clusterthrottle list resource version = $clthrottleVersion, latest pod list resource version = $podVersion, namespace list resource version = $namespaceVersion") - - log.info( - "starting periodic syncs for throttles/clusterthrottles with temporaryThresholdOverrides") - - requestHandlerActor ! Initialize(cache.throttles.toImmutableMap, - cache.clusterThrottles.toImmutableMap, - cache.namespaces.toImmutableMap) - - cancelWhenRestart += system.scheduler.schedule(config.reconcileTemporaryThresholdInterval, - config.reconcileTemporaryThresholdInterval) { - - self ! ReconcileClusterThrottlesEvent(_.hasTemporaryOverridesToReconcile, - "hasTemporaryThresholdOverridesToReconcile") - } - cancelWhenRestart += system.scheduler.schedule(config.reconcileTemporaryThresholdInterval, - config.reconcileTemporaryThresholdInterval) { - self ! ReconcileThrottlesEvent(_.hasTemporaryOverridesToReconcile, - "hasTemporaryThresholdOverridesToReconcile") - } - - case scala.util.Failure(th) => - log.error("error in syncing throttles and pods: {}, {}", - th, - th.getStackTrace.mkString("\n")) - log.error("{} will commit suicide", self.path) - self ! PoisonPill - } - - for { - (namespaceVersion, clthrottleVersion, throttleVersion, podVersion) <- syncAll - // watch namespace - nsWatch = k8s - .watchAllContinuously[Namespace]( - sinceResourceVersion = namespaceVersion, - bufSize = config.watchBufferSize - )( - implicitly[Format[Namespace]], - clusterScopedResourceDefinition[Namespace], - lc - ) - .map(NamespaceWatchEvent(_)) - // watch clusterthrottle - clthrottleWatch = k8s - .watchAllContinuously[v1alpha1.ClusterThrottle]( - sinceResourceVersion = clthrottleVersion, - bufSize = config.watchBufferSize - )( - implicitly[Format[v1alpha1.ClusterThrottle]], - clusterScopedResourceDefinition[v1alpha1.ClusterThrottle], - lc - ) - .map(ClusterThrottleWatchEvent(_)) - // watch throttle in all namespaces - throttleWatch = k8s - .watchAllContinuously[v1alpha1.Throttle]( - sinceResourceVersion = throttleVersion, - bufSize = config.watchBufferSize - )( - implicitly[Format[v1alpha1.Throttle]], - clusterScopedResourceDefinition[v1alpha1.Throttle], - lc - ) - .map(ThrottleWatchEvent(_)) - // watch pods in all namespaces - podWatch = k8s - .watchAllContinuously[Pod]( - sinceResourceVersion = podVersion, - bufSize = config.watchBufferSize - )( - implicitly[Format[Pod]], - clusterScopedResourceDefinition[Pod], - lc - ) - .map(PodWatchEvent(_)) - done <- { - val fut = Source - .combine(nsWatch, clthrottleWatch, throttleWatch, podWatch)( - Merge(_, eagerComplete = true)) - .runWith( - Sink.foreach { - case event: PodWatchEvent => self ! event - case event: ThrottleWatchEvent => self ! event - case event: ClusterThrottleWatchEvent => self ! event - case event: NamespaceWatchEvent => self ! event - case _ => - // drop - } - ) - fut.onComplete { done => - self ! ResourceWatchDone(done) - } - fut - } - } yield done - } - - private def _calcNextThrottleStatuses( - targetThrottles: Set[v1alpha1.Throttle], - podsInNs: => Set[Pod], - at: skuber.Timestamp - ): List[(ObjectKey, v1alpha1.Throttle.Status)] = { - val nextStatuses = calcNextThrottleStatuses(targetThrottles, podsInNs, at) - if (nextStatuses.isEmpty) { - log.info("All throttle statuses are up-to-date. No updates.") - } - nextStatuses - } - - private def syncThrottle( - key: ObjectKey, - st: v1alpha1.Throttle.Status - ): Future[v1alpha1.Throttle] = { - val (ns, n) = key - - val k8sNs = k8sMap.getOrElseUpdate( - ns, - skuber.k8sInit(skuber.api.client.defaultK8sConfig.setCurrentNamespace(ns))) - val fut = for { - latest <- k8sNs.get[v1alpha1.Throttle](n) - nextState = latest.copy(status = Option(st)) - _ <- Future { log.info("updating throttle {} with status ({})", key, st) } - result <- k8sNs.updateStatus(nextState) - } yield result - - fut.onComplete { - case Success(thr) => - log.info("successfully updated throttle {} with status {}", thr.key, thr.status) - recordThrottleStatusMetric(thr) - case Failure(ex) => - log.error("failed updating throttle {} with status {} by {}", key, Option(st), ex) - } - - fut - } - - def reconcileAllThrottles(at: skuber.Timestamp) = - reconcileThrottlesWithFilterSync(_ => true, "all", at) - def reconcileThrottlesWithFilterSync( - filter: v1alpha1.Throttle => Boolean, - filterName: String, - at: skuber.Timestamp - ): Future[List[v1alpha1.Throttle]] = { - log.info(s"reconciling statuses of $filterName throttles") - - val throttleStatusesToReconcile = for { - (namespace, throttles) <- cache.throttles.toImmutable.toList - targetThrottles = throttles.filter(filter) - thr <- _calcNextThrottleStatuses(targetThrottles, - cache.pods.toImmutable.getOrElse(namespace, Set.empty), - at) - } yield thr - - val fut = Future.sequence(throttleStatusesToReconcile.map { - case (key, st) => - syncThrottle(key, st) - }) - - fut.onComplete { - case Success(_) => - // update all metrics when reconciling - cache.throttles.toImmutable.values.foreach(_.foreach { thr => - recordThrottleSpecMetric(thr) - recordThrottleStatusMetric(thr) - }) - log.info(s"finished reconciling statuses of $filterName throttle.") - case Failure(ex) => - log.error(s"failed reconciling statuses of $filterName throttle by: {}", ex) - } - - fut - } - - def reconcileThrottlesWithFilterAsync( - filter: v1alpha1.Throttle => Boolean, - filterName: String, - at: skuber.Timestamp - ) = { - log.info(s"sending reconcile requests for $filterName throttles") - for { - (_, throttles) <- cache.throttles.toImmutable.toList - targetThrottles = throttles.filter(filter) - thr <- targetThrottles - } yield { - self ! ReconcileOneThrottleEvent(thr.key, at) - } - } - - def reconcileOneThrottle(key: ObjectKey, at: skuber.Timestamp) = { - log.info(s"start reconciling statuses of throttle=$key.") - - val (namespace, _) = key - val throttleStatusToReconcile = for { - thr <- cache.throttles.get(key).map(List(_)).getOrElse(List.empty) - result <- calcNextThrottleStatuses(Set(thr), - cache.pods.toImmutable.getOrElse(namespace, Set.empty), - at) - } yield result - - if (throttleStatusToReconcile.nonEmpty) { - val fut = Future.sequence(throttleStatusToReconcile.map { - case (key, st) => - syncThrottle(key, st) - }) - fut.onComplete { - case Success(_) => - log.info(s"finished reconciling status of throttle=$key.") - case Failure(ex) => - log.error(s"failed reconciling status of throttle=$key by: {}", ex) - } - } else { - log.info(s"throttle=$key status are up-to-date. No updates.") - } - } - - // on detecting some pod change. - def updateThrottleBecauseOf(pod: Pod, at: skuber.Timestamp): Unit = { - val ns = pod.namespace - val allThrottlesInNs = cache.throttles.toImmutable.getOrElse(ns, Set.empty[v1alpha1.Throttle]) - val affectedThrottlesInNs = allThrottlesInNs.filter(_.spec.selector.matches(pod)) - - if (affectedThrottlesInNs.nonEmpty) { - for { - (key, st) <- _calcNextThrottleStatuses(affectedThrottlesInNs, - cache.pods.toImmutable.getOrElse(ns, Set.empty[Pod]), - at) - } yield { - syncThrottle(key, st) - } - } - } - - // on detecting some throttle change. - def updateThrottleBecauseOf(throttle: v1alpha1.Throttle, at: skuber.Timestamp): Unit = { - val ns = throttle.namespace - for { - (key, st) <- _calcNextThrottleStatuses(Set(throttle), - cache.pods.toImmutable.getOrElse(ns, Set.empty[Pod]), - at) - } yield { - syncThrottle(key, st) - } - } - - private def _calcNextClusterThrottleStatuses( - targetClusterThrottles: Set[v1alpha1.ClusterThrottle], - podsInAllNamespaces: => Set[Pod], - namespaces: Map[String, Namespace], - at: skuber.Timestamp - ): List[(ObjectKey, v1alpha1.ClusterThrottle.Status)] = { - val nextStatuses = - calcNextClusterThrottleStatuses(targetClusterThrottles, podsInAllNamespaces, namespaces, at) - if (nextStatuses.isEmpty) { - log.info("All clusterthrottle statuses are up-to-date. No updates.") - } - nextStatuses - } - - private def syncClusterThrottle( - key: ObjectKey, - st: v1alpha1.ClusterThrottle.Status - ): Future[v1alpha1.ClusterThrottle] = { - val (_, n) = key - - val k8s = k8sMap.getOrElseUpdate( - skuber.api.client.defaultK8sConfig.currentContext.namespace.name, - skuber.k8sInit(skuber.api.client.defaultK8sConfig)) - val fut = for { - latest <- k8s.get[v1alpha1.ClusterThrottle](n) - nextState = latest.copy(status = Option(st)) - _ <- Future { - log.info("updating clusterthrottle {} with status ({})", key, st) - } - result <- k8s.updateStatus(nextState) - } yield result - - fut.onComplete { - case Success(clthr) => - log.info("successfully updated clusterthrottle {} with status {}", clthr.key, clthr.status) - recordClusterThrottleStatusMetric(clthr) - case Failure(ex) => - log.error("failed updating clusterthrottle {} with status {} by {}", key, Option(st), ex) - } - - fut - } - - def reconcileAllClusterThrottles(at: skuber.Timestamp) = - reconcileClusterThrottlesWithFilterSync(_ => true, "all", at) - - def reconcileClusterThrottlesWithFilterSync( - filter: v1alpha1.ClusterThrottle => Boolean, - filterName: String, - at: skuber.Timestamp - ): Future[List[v1alpha1.ClusterThrottle]] = { - log.info(s"start reconciling statuses of $filterName clusterthrottle.") - - val clusterThrottleStatusesToReconcile = for { - (_, throttles) <- cache.clusterThrottles.toImmutable.toList - filtered = throttles.filter(filter) - namespaces = cache.namespaces.toImmutable.flatMap(kv => kv._2.map(ns => ns.name -> ns).toMap) - clthr <- _calcNextClusterThrottleStatuses( - filtered, - cache.pods.toImmutable.values.fold(Set.empty)(_ ++ _), - namespaces, - at) - } yield clthr - - val fut = Future.sequence(clusterThrottleStatusesToReconcile.map { - case (key, st) => - syncClusterThrottle(key, st) - }) - - fut.onComplete { - case Success(_) => - // update all metrics when reconciling - cache.clusterThrottles.toImmutable.values.foreach(_.foreach { clthr => - recordClusterThrottleSpecMetric(clthr) - recordClusterThrottleStatusMetric(clthr) - }) - log.info(s"finished reconciling statuses of $filterName clusterthrottle.") - case Failure(ex) => - log.error(s"failed reconciling statuses of $filterName clusterthrottle by: {}", ex) - } - - fut - } - - def reconcileClusterThrottlesWithFilterAsync( - filter: v1alpha1.ClusterThrottle => Boolean, - filterName: String, - at: skuber.Timestamp - ): Unit = { - log.info(s"sending reconcile request for $filterName clusterthrottle.") - for { - (_, clthrottles) <- cache.clusterThrottles.toImmutable.toList - filtered = clthrottles.filter(filter) - clthr <- filtered - } yield { - self ! ReconcileOneClusterThrottleEvent(clthr.key, at) - } - } - - def reconcileOneClusterThrottle(key: ObjectKey, at: skuber.Timestamp): Unit = { - log.info(s"start reconciling statuses of clusterthrottle=$key.") - - val clusterThrottleStatusToReconcile = for { - clthr <- cache.clusterThrottles.get(key).map(List(_)).getOrElse(List.empty) - namespaces = cache.namespaces.toImmutable.flatMap(kv => kv._2.map(ns => ns.name -> ns).toMap) - clthr <- calcNextClusterThrottleStatuses( - Set(clthr), - cache.pods.toImmutable.values.fold(Set.empty)(_ ++ _), - namespaces, - at) - } yield clthr - - if (clusterThrottleStatusToReconcile.nonEmpty) { - val fut = Future.sequence(clusterThrottleStatusToReconcile.map { - case (key, st) => - syncClusterThrottle(key, st) - }) - - fut.onComplete { - case Success(_) => - log.info(s"finished reconciling statuses of clusterthrottle=$key.") - case Failure(ex) => - log.error(s"failed reconciling statuses of clusterthrottle=$key by: {}", ex) - } - } else { - log.info(s"clusterthrottle=$key statuses are up-to-date. No updates.") - } - } - - // on detecting some pod change. - def updateClusterThrottleBecauseOf(pod: Pod, at: skuber.Timestamp): Unit = { - val namespaces = - cache.namespaces.toImmutable.flatMap(kv => kv._2.map(ns => ns.name -> ns).toMap) - if (namespaces.contains(pod.namespace)) { - val allClusterThrottles = cache.clusterThrottles.toImmutable.values.fold(Set.empty)(_ ++ _) - val affectedClusterThrottles = - allClusterThrottles.filter(_.spec.selector.matches(pod, namespaces(pod.namespace))) - if (affectedClusterThrottles.nonEmpty) { - for { - (key, st) <- _calcNextClusterThrottleStatuses( - affectedClusterThrottles, - cache.pods.toImmutable.values.fold(Set.empty)(_ ++ _), - namespaces, - at) - } yield { - syncClusterThrottle(key, st) - } - } - } - } - - // on detecting some namespace change. - def updateClusterThrottleBecauseOf(namespace: Namespace, at: skuber.Timestamp): Unit = { - val podsInAllNamespaces = cache.pods.toImmutable.values.fold(Set.empty)(_ ++ _) - val allClusterThrottles = cache.clusterThrottles.toImmutable.values.fold(Set.empty)(_ ++ _) - val namespaces = - cache.namespaces.toImmutable.flatMap(kv => kv._2.map(ns => ns.name -> ns).toMap) - - for { - (key, st) <- _calcNextClusterThrottleStatuses(allClusterThrottles, - podsInAllNamespaces, - namespaces, - at) - } yield { - syncClusterThrottle(key, st) - } - } - - // on detecting some throttle change. - def updateClusterThrottleBecauseOf( - clthrottle: v1alpha1.ClusterThrottle, - at: skuber.Timestamp - ): Unit = { - val podsInAllNamespaces = cache.pods.toImmutable.values.fold(Set.empty)(_ ++ _) - val namespaces = - cache.namespaces.toImmutable.flatMap(kv => kv._2.map(ns => ns.name -> ns).toMap) - - for { - (key, st) <- _calcNextClusterThrottleStatuses(Set(clthrottle), - podsInAllNamespaces, - namespaces, - at) - } yield { - syncClusterThrottle(key, st) - } - } - - override def receive: Receive = - eventHandler orElse resourceWatchHandler orElse reconcileHandler - - def eventHandler: Receive = { - case PodWatchEvent(e, at) if isPodResponsible(e._object) => - log.info("detected pod {} was {}", e._object.key, e._type) - val updateOrRemoveThen = e._type match { - case EventType.DELETED => - cache.pods.removeThen(e._object)(_) - case _ => - // Completed pods is not needed anymore - if (isPodCompleted(e._object)) { - cache.pods.removeThen(e._object)(_) - } else { - cache.pods.addOrUpdateThen(e._object)(_) - } - } - updateOrRemoveThen { pod => - updateThrottleBecauseOf(pod, at) - updateClusterThrottleBecauseOf(pod, at) - } - - case PodWatchEvent(e, at) if !isPodResponsible(e._object) => - log.info( - "detected pod {} was {}. but it will be ignored because it is not responsible for {}", - e._object.key, - e._type, - config.targetSchedulerNames) - - case evt @ ClusterThrottleWatchEvent(e, at) if isClusterThrottleResponsible(e._object) => - log.info("detected clusterthrottle {} was {}", e._object.key, e._type) - val prev = cache.clusterThrottles.get(e._object.key) - val updateOrRemoveCacheThen = e._type match { - case EventType.DELETED => - cache.clusterThrottles.removeThen(e._object)(_) - case _ => - cache.clusterThrottles.addOrUpdateThen(e._object)(_) - } - updateOrRemoveCacheThen { clusterThrottle => - if (prev.isEmpty || prev.get.spec != clusterThrottle.spec) { - updateClusterThrottleBecauseOf(clusterThrottle, at) - } - e._type match { - case EventType.DELETED => - log.info( - s"resetting all metrics for ${clusterThrottle.key} because detecting ${clusterThrottle.key} was DELETED.") - resetClusterThrottleMetric(e._object) - case _ => - recordClusterThrottleSpecMetric(e._object) - } - } - requestHandlerActor forward evt - - case ClusterThrottleWatchEvent(e, at) if !isClusterThrottleResponsible(e._object) => - log.info( - "detected clusterthrottle {} was {}. but it will be ignored because it is not responsible for {}", - e._object.key, - e._type, - config.throttlerName) - - case evt @ ThrottleWatchEvent(e, at) if isThrottleResponsible(e._object) => - log.info("detected throttle {} was {}", e._object.key, e._type) - val prev = cache.throttles.get(e._object.key) - val updateOrRemoveCacheThen = e._type match { - case EventType.DELETED => - cache.throttles.removeThen(e._object)(_) - case _ => - cache.throttles.addOrUpdateThen(e._object)(_) - } - updateOrRemoveCacheThen { throttle => - if (prev.isEmpty || prev.get.spec != throttle.spec) { - updateThrottleBecauseOf(throttle, at) - } - e._type match { - case EventType.DELETED => - log.info( - s"resetting all metrics for ${throttle.key} because detecting ${throttle.key} was DELETED.") - resetThrottleMetric(e._object) - case _ => - recordThrottleSpecMetric(e._object) - } - } - requestHandlerActor forward evt - - case ThrottleWatchEvent(e, at) if !isThrottleResponsible(e._object) => - log.info( - "detected throttle {} was {}. but it will be ignored because it is not responsible for {}", - e._object.key, - e._type, - config.throttlerName) - - case evt @ NamespaceWatchEvent(e, at) => - log.info("detected namespace {} was {}", e._object.key, e._type) - val updateOrRemoveThen = e._type match { - case EventType.DELETED => - cache.namespaces.removeThen(e._object)(_) - case _ => - cache.namespaces.addOrUpdateThen(e._object)(_) - } - updateOrRemoveThen { namespace => - updateClusterThrottleBecauseOf(namespace, at) - } - requestHandlerActor forward evt - } - - def resourceWatchHandler: Receive = { - case ResourceWatchDone(done) => - done match { - case scala.util.Success(_) => - log.error("watch api connection is closed. committing suicide.") - self ! PoisonPill - case scala.util.Failure(ex) => - log.error( - "watch api connection was closed by an exceptions. " + - "committing suicide. (cause = {})", - ex - ) - self ! PoisonPill - } - } - - def reconcileHandler: Receive = { - case ReconcileThrottlesEvent(f, fName, at) => reconcileThrottlesWithFilterAsync(f, fName, at) - case ReconcileClusterThrottlesEvent(f, fName, at) => - reconcileClusterThrottlesWithFilterAsync(f, fName, at) - case ReconcileOneThrottleEvent(key, at) => reconcileOneThrottle(key, at) - case ReconcileOneClusterThrottleEvent(key, at) => reconcileOneClusterThrottle(key, at) - } - - private def latestResourceList[R <: ObjectResource]( - implicit - k8s: K8SRequestContext, - ec: ExecutionContext, - fmt: Format[R], - rd: ResourceDefinition[R], - listfmt: Format[ListResource[R]], - listrd: ResourceDefinition[ListResource[R]] - ): Future[skuber.ListResource[R]] = - k8s.list[ListResource[R]]()(implicitly[Format[ListResource[R]]], - clusterScopedResourceDefinition[ListResource[R]], - lc) - - private[controller] class ObjectResourceCache[R <: ObjectResource]( - val isResponsible: R => Boolean = (_: R) => true, - val map: mutable.Map[String, mutable.Map[ObjectKey, R]] = - mutable.Map.empty[String, mutable.Map[ObjectKey, R]]) { - - def init( - initFilter: R => Boolean = _ => true - )(implicit - k8s: K8SRequestContext, - ec: ExecutionContext, - fmt: Format[R], - rd: ResourceDefinition[R], - listfmt: Format[ListResource[R]], - listrd: ResourceDefinition[ListResource[R]] - ): Future[Option[String]] = { - - for { - _ <- Future { log.info("syncing {} status", rd.spec.names.singular) } - // extract resource list in all namespaces - resourceList <- latestResourceList[R] - latestResourceVersion = resourceList.metadata map { - _.resourceVersion - } - _ = resourceList.items.filter(r => isResponsible(r) && initFilter(r)).foreach(addOrUpdate) - _ <- Future { log.info("finished syncing {} status", rd.spec.names.singular) } - } yield latestResourceVersion - } - - def addOrUpdate(r: R): Unit = addOrUpdateThen(r)(_ => ()) - def remove(r: R): Unit = removeThen(r)(_ => ()) - - def addOrUpdateThen(r: R)(f: R => Unit): Unit = if (isResponsible(r)) { - val key @ (ns, _) = r.key - - if (!map.contains(ns)) { - map += ns -> mutable.Map(key -> r) - } else { - map(ns)(key) = r - } - - f(r) - } - def removeThen(r: R)(f: R => Unit): Unit = if (isResponsible(r)) { - val key @ (ns, _) = r.key - - if (map.contains(ns)) { - map(ns).remove(key) - } - - f(r) - } - - def toImmutable: Map[String, Set[R]] = map.toMap.mapValues(m => m.values.toSet) - - def toImmutableMap: Map[String, Map[ObjectKey, R]] = map.toMap.mapValues(_.toMap) - - def get(key: ObjectKey): Option[R] = { - map.get(key._1).flatMap(_.get(key)) - } - } -} - -object ThrottleController { - - def clusterScopedResourceDefinition[O <: TypeMeta](implicit rd: ResourceDefinition[O]) = - rd.spec.scope match { - case ResourceSpecification.Scope.Cluster => - rd - case ResourceSpecification.Scope.Namespaced => - new ResourceDefinition[O] { - def spec = new ResourceSpecification { - override def apiPathPrefix: String = rd.spec.apiPathPrefix - - override def group: Option[String] = rd.spec.group - - override def defaultVersion: String = rd.spec.defaultVersion - - override def prioritisedVersions: List[String] = rd.spec.prioritisedVersions - - override def scope: ResourceSpecification.Scope.Value = - ResourceSpecification.Scope.Cluster - - override def names: ResourceSpecification.Names = rd.spec.names - - override def subresources: Option[Subresources] = rd.spec.subresources - } - } - } - - // messages - case class ReconcileThrottlesEvent( - filter: v1alpha1.Throttle => Boolean, - filterName: String, - at: skuber.Timestamp = java.time.ZonedDateTime.now()) - - case class ReconcileOneThrottleEvent( - key: ObjectKey, - at: skuber.Timestamp = java.time.ZonedDateTime.now()) - - case class ReconcileClusterThrottlesEvent( - filter: v1alpha1.ClusterThrottle => Boolean, - filterName: String, - at: skuber.Timestamp = java.time.ZonedDateTime.now()) - - case class ReconcileOneClusterThrottleEvent( - key: ObjectKey, - at: skuber.Timestamp = java.time.ZonedDateTime.now()) - - // messages for controls - private case class ResourceWatchDone(done: Try[Done]) - - case class PodWatchEvent( - e: K8SWatchEvent[Pod], - at: skuber.Timestamp = java.time.ZonedDateTime.now()) - - case class ThrottleWatchEvent( - e: K8SWatchEvent[v1alpha1.Throttle], - at: skuber.Timestamp = java.time.ZonedDateTime.now()) - - case class ClusterThrottleWatchEvent( - e: K8SWatchEvent[v1alpha1.ClusterThrottle], - at: skuber.Timestamp = java.time.ZonedDateTime.now()) - - case class NamespaceWatchEvent( - e: K8SWatchEvent[Namespace], - at: skuber.Timestamp = java.time.ZonedDateTime.now()) - - def props(requestHandlerActor: ActorRef, k8s: K8SRequestContext, config: KubeThrottleConfig) = { - implicit val _k8s = k8s - implicit val _config = config - Props(new ThrottleController(requestHandlerActor)) - } -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/controller/ThrottleControllerLogic.scala b/src/main/scala/com/github/everpeace/k8s/throttler/controller/ThrottleControllerLogic.scala deleted file mode 100644 index 214dc27..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/controller/ThrottleControllerLogic.scala +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.controller -import com.github.everpeace.k8s._ -import com.github.everpeace.k8s.throttler.crd.v1alpha1 -import com.github.everpeace.k8s.throttler.crd.v1alpha1.Implicits._ -import com.github.everpeace.k8s.throttler.crd.v1alpha1.ResourceAmount -import com.github.everpeace.util.Injection._ -import skuber._ -import cats.instances.list._ -import com.github.everpeace.k8s.throttler.KubeThrottleConfig - -trait ThrottleControllerLogic { - def isThrottleAlreadyActiveFor(pod: Pod, throttle: v1alpha1.Throttle): Boolean = - throttle.isAlreadyActiveFor(pod, throttle.isTarget(pod)) - - def isThrottleInsufficientFor(pod: Pod, throttle: v1alpha1.Throttle): Boolean = - throttle.isInsufficientFor(pod, throttle.isTarget(pod)) - - def calcNextThrottleStatuses( - targetThrottles: Set[v1alpha1.Throttle], - podsInNs: => Set[Pod], - at: skuber.Timestamp - )(implicit - conf: KubeThrottleConfig - ): List[(ObjectKey, v1alpha1.Throttle.Status)] = { - - for { - throttle <- targetThrottles.toList - - matchedPods = podsInNs.filter(pod => throttle.spec.selector.matches(pod)) - runningPods = matchedPods.filter(isScheduledAndNotFinished).toList - runningTotal = runningPods.==>[List[ResourceAmount]].foldLeft(zeroResourceAmount)(_ add _) - nextStatus = throttle.spec.statusFor(runningTotal, at)(v1alpha1.Throttle.Status.apply) - - toUpdate <- if (throttle.status.needToUpdateWith(nextStatus)) { - List(throttle.key -> nextStatus) - } else { - List.empty - } - } yield toUpdate - } -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/controller/ThrottleRequestHandler.scala b/src/main/scala/com/github/everpeace/k8s/throttler/controller/ThrottleRequestHandler.scala deleted file mode 100644 index fe5b20d..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/controller/ThrottleRequestHandler.scala +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.controller -import akka.actor.{Actor, Props} -import com.github.everpeace.k8s._ -import com.github.everpeace.k8s.throttler.controller.ThrottleController.{ - ClusterThrottleWatchEvent, - NamespaceWatchEvent, - ThrottleWatchEvent -} -import com.github.everpeace.k8s.throttler.controller.ThrottleRequestHandler._ -import com.github.everpeace.k8s.throttler.crd.v1alpha1 -import skuber.{K8SWatchEvent, ObjectResource, Pod} -import skuber.api.client.EventType - -class ThrottleRequestHandler - extends Actor - with ThrottleControllerLogic - with ClusterThrottleControllerLogic { - - var initialized = false - var throttlesMap: Map[String, Map[ObjectKey, v1alpha1.Throttle]] = Map.empty - var clusterThrottlesMap: Map[String, Map[ObjectKey, v1alpha1.ClusterThrottle]] = Map.empty - var namespacesMap: Map[String, Map[ObjectKey, skuber.Namespace]] = Map.empty - var throttles: Map[String, Set[v1alpha1.Throttle]] = Map.empty - var clusterThrottles: Set[v1alpha1.ClusterThrottle] = Set.empty - var namespaces: Map[String, skuber.Namespace] = Map.empty - - def updateThrottleMap(map: Map[String, Map[ObjectKey, v1alpha1.Throttle]]): Unit = { - throttlesMap = map - throttles = map.mapValues(m => m.values.toSet) - } - def updateClusterThrottleMap(map: Map[String, Map[ObjectKey, v1alpha1.ClusterThrottle]]): Unit = { - clusterThrottlesMap = map - clusterThrottles = map.mapValues(m => m.values.toSet).values.fold(Set.empty)(_ ++ _) - } - def updateNamespaceMap(map: Map[String, Map[ObjectKey, skuber.Namespace]]): Unit = { - namespacesMap = map - namespaces = map.mapValues(m => m.values.toSet).flatMap { kv => - kv._2.map(ns => ns.name -> ns).toMap - } - } - - def receive: Receive = requestHandler orElse updateMessageHandler orElse readinessHandler - - def readinessHandler: Receive = { - case IsReady => sender ! initialized - } - - def updateMessageHandler: Receive = { - case ThrottleWatchEvent(e, at) => - updateThrottleMap(upsertOrDelete(throttlesMap, e)) - case ClusterThrottleWatchEvent(e, at) => - updateClusterThrottleMap(upsertOrDelete(clusterThrottlesMap, e)) - case NamespaceWatchEvent(e, at) => - updateNamespaceMap(upsertOrDelete(namespacesMap, e)) - } - - def requestHandler: Receive = { - case Initialize(thrmap, clthrmap, nsmap) => - updateThrottleMap(thrmap) - updateClusterThrottleMap(clthrmap) - updateNamespaceMap(nsmap) - initialized = true - - case CheckThrottleRequest(_) if !initialized => - sender ! NotReady - - case CheckThrottleRequest(pod) if initialized => - val throttlesInNs = throttles.getOrElse(pod.namespace, Set.empty) - val activeThrottles = throttlesInNs.filter(isThrottleAlreadyActiveFor(pod, _)) - val insufficientThrottles = - throttlesInNs.filter(t => - !isThrottleAlreadyActiveFor(pod, t) && isThrottleInsufficientFor(pod, t)) - - val activeClusterThrottles = clusterThrottles.filter { clthr => - if (namespaces.contains(pod.namespace)) { - isClusterThrottleAlreadyActiveFor(pod, namespaces(pod.namespace), clthr) - } else { - false - } - } - val insufficientClusterThrottles = clusterThrottles.filter { clthr => - if (namespaces.contains(pod.namespace)) { - val podsNs = namespaces(pod.namespace) - !isClusterThrottleAlreadyActiveFor(pod, podsNs, clthr) && isClusterThrottleInsufficientFor( - pod, - podsNs, - clthr) - } else { - false - } - } - - val isThrottled = activeThrottles.nonEmpty || activeClusterThrottles.nonEmpty || insufficientThrottles.nonEmpty || insufficientClusterThrottles.nonEmpty - if (isThrottled) { - sender ! Throttled(pod, - activeThrottles, - activeClusterThrottles, - insufficientThrottles, - insufficientClusterThrottles) - } else { - sender ! NotThrottled(pod) - } - } - - private def delete[R <: ObjectResource]( - map: Map[String, Map[ObjectKey, R]], - r: R - ): Map[String, Map[ObjectKey, R]] = { - val key @ (ns, _) = r.key - if (map.contains(ns)) { - val inNs = map(ns) - map.updated(ns, inNs - key) - } else { - map - } - } - - private def upsert[R <: ObjectResource]( - map: Map[String, Map[ObjectKey, R]], - r: R - ): Map[String, Map[ObjectKey, R]] = { - val key @ (ns, _) = r.key - val inNs = map.getOrElse(ns, Map.empty) - map.updated(ns, inNs.updated(key, r)) - } - - private def upsertOrDelete[R <: ObjectResource]( - map: Map[String, Map[ObjectKey, R]], - e: K8SWatchEvent[R] - ): Map[String, Map[ObjectKey, R]] = { - e._type match { - case EventType.DELETED => - delete(map, e._object) - case _ => - upsert(map, e._object) - } - } -} - -object ThrottleRequestHandler { - case class Initialize( - initialThrottlesMap: Map[String, Map[ObjectKey, v1alpha1.Throttle]], - initialclusterThrottlesMap: Map[String, Map[ObjectKey, v1alpha1.ClusterThrottle]], - initialNamespacesMap: Map[String, Map[ObjectKey, skuber.Namespace]]) - case object IsReady - - case class CheckThrottleRequest(pod: Pod) - sealed trait CheckThrottleResponse - case object NotReady extends CheckThrottleResponse - case class Throttled( - pod: Pod, - activeThrottles: Set[v1alpha1.Throttle], - activeClusterThrottles: Set[v1alpha1.ClusterThrottle], - insufficientThrottles: Set[v1alpha1.Throttle], - insufficientClusterThrottles: Set[v1alpha1.ClusterThrottle]) - extends CheckThrottleResponse - - case class NotThrottled(pod: Pod) extends CheckThrottleResponse - - def props() = Props(new ThrottleRequestHandler()) -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/crd/package.scala b/src/main/scala/com/github/everpeace/k8s/throttler/crd/package.scala deleted file mode 100644 index 9b97c10..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/crd/package.scala +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler - -package object crd { - final val Group = "schedule.k8s.everpeace.github.com" - object Throttle { - final val Kind = "Throttle" - final val SingularName = "throttle" - final val PluralName = "throttles" - final val ShortNames = List("thr", "thrs") - } - - object ClusterThrottle { - final val Kind = "ClusterThrottle" - final val SingularName = "clusterthrottle" - final val PluralName = "clusterthrottles" - final val ShortNames = List("clthr", "clthrs") - } -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/CalculatedThreshold.scala b/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/CalculatedThreshold.scala deleted file mode 100644 index 2fb6321..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/CalculatedThreshold.scala +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd.v1alpha1 - -case class CalculatedThreshold( - threshold: ResourceAmount, - calculatedAt: skuber.Timestamp, - messages: List[String] = List.empty) - -object CalculatedThreshold { - trait JsonFormat extends ResourceAmount.JsonFormat { - import play.api.libs.json._ - import play.api.libs.functional.syntax._ - import skuber.json.format.{timeReads, timewWrites, maybeEmptyFormatMethods} - - implicit val calculatedThresholdFmt: Format[CalculatedThreshold] = ( - (JsPath \ "threshold").format[ResourceAmount] and - (JsPath \ "calculatedAt").format[skuber.Timestamp] and - (JsPath \ "messages").formatMaybeEmptyList[String] - )(CalculatedThreshold.apply, unlift(CalculatedThreshold.unapply)) - } - - trait Syntax extends TemporaryThresholdOverride.Syntax with ResourceAmount.Syntax { - implicit class CalculatedThresholdSyntax( - tup: (ResourceAmount, List[TemporaryThresholdOverride])) { - def thresholdAt(at: skuber.Timestamp): ResourceAmount = { - val threshold = tup._1 - val overrides = tup._2 - overrides.foldRight(threshold) { (thresholdOverride, calculated) => - if (thresholdOverride.isActiveAt(at)) { - calculated.merge(thresholdOverride.threshold) - } else { - calculated - } - } - } - } - } -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/ClusterThrottle.scala b/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/ClusterThrottle.scala deleted file mode 100644 index 25d7ed9..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/ClusterThrottle.scala +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd.v1alpha1 - -import com.github.everpeace.k8s.throttler -import com.github.everpeace.k8s.throttler.crd.v1alpha1 -import com.github.everpeace.k8s.throttler.crd.v1alpha1.Implicits._ -import skuber.ResourceSpecification.Subresources -import skuber.apiextensions.CustomResourceDefinition -import skuber.{ - CustomResource, - HasStatusSubresource, - LabelSelector, - Namespace, - Pod, - ResourceDefinition, - ResourceSpecification -} - -object ClusterThrottle { - - case class Selector(selectorTerms: List[SelectorItem]) - case class SelectorItem( - podSelector: LabelSelector, - namespaceSelector: Option[LabelSelector] = None) - - case class Spec( - throttlerName: String, - selector: Selector, - threshold: ResourceAmount, - temporaryThresholdOverrides: List[TemporaryThresholdOverride] = List.empty) - extends v1alpha1.Spec[Selector] - - case class Status( - throttled: IsResourceAmountThrottled, - used: ResourceAmount, - calculatedThreshold: Option[CalculatedThreshold] = None) - extends v1alpha1.Status - - val crd: CustomResourceDefinition = CustomResourceDefinition[v1alpha1.ClusterThrottle] - - def apply(name: String, spec: Spec) = CustomResource[Spec, Status](spec).withName(name) - - trait JsonFormats - extends CalculatedThreshold.JsonFormat - with TemporaryThresholdOverride.JsonFormat - with ResourceAmount.JsonFormat { - import play.api.libs.functional.syntax._ - import play.api.libs.json._ - import skuber.json.format.{maybeEmptyFormatMethods, jsPath2LabelSelFormat} - - implicit val clusterThrottleSelectorItemFmt: Format[v1alpha1.ClusterThrottle.SelectorItem] = ( - (JsPath \ "podSelector").formatLabelSelector and - (JsPath \ "namespaceSelector").formatNullableLabelSelector - )(v1alpha1.ClusterThrottle.SelectorItem.apply, - unlift(v1alpha1.ClusterThrottle.SelectorItem.unapply)) - - implicit val clusterThrottleSelectorFmt: Format[v1alpha1.ClusterThrottle.Selector] = - (JsPath \ "selectorTerms") - .formatMaybeEmptyList[v1alpha1.ClusterThrottle.SelectorItem] - .inmap(v1alpha1.ClusterThrottle.Selector.apply, - unlift(v1alpha1.ClusterThrottle.Selector.unapply)) - - implicit val clusterThrottleSpecFmt: Format[v1alpha1.ClusterThrottle.Spec] = ( - (JsPath \ "throttlerName").formatMaybeEmptyString(true) and - (JsPath \ "selector").format[v1alpha1.ClusterThrottle.Selector] and - (JsPath \ "threshold").format[ResourceAmount] and - (JsPath \ "temporaryThresholdOverrides") - .formatMaybeEmptyList[v1alpha1.TemporaryThresholdOverride] - )(v1alpha1.ClusterThrottle.Spec.apply, unlift(v1alpha1.ClusterThrottle.Spec.unapply)) - - implicit val clusterThrottleStatusFmt: Format[v1alpha1.ClusterThrottle.Status] = - Json.format[v1alpha1.ClusterThrottle.Status] - } - - trait Syntax { - import com.github.everpeace.k8s._ - - implicit class ThrottleSelectorItemSyntax(selectorItem: SelectorItem) { - def matches(pod: skuber.Pod, namespace: skuber.Namespace): Boolean = { - (namespace.name == pod.namespace) && { - val namespaceSelectorEmpty = selectorItem.namespaceSelector.isEmpty - val namespaceMatched = - selectorItem.namespaceSelector.exists(_.matches(namespace.metadata.labels)) - val podMatched = selectorItem.podSelector.matches(pod.metadata.labels) - (namespaceSelectorEmpty || namespaceMatched) && podMatched - } - } - } - - implicit class ThrottleSelectorSyntax(selector: Selector) { - def matches(pod: skuber.Pod, namespace: skuber.Namespace): Boolean = { - (namespace.name == pod.namespace) && selector.selectorTerms.exists( - _.matches(pod, namespace)) - } - } - - implicit class ClusterThrottleSyntax(clthrottle: ClusterThrottle) { - def isTarget(pod: Pod, ns: Namespace): Boolean = clthrottle.spec.selector.matches(pod, ns) - } - } - - trait ResourceDefinitions { - implicit val clusterThrottleResourceDefinition: ResourceDefinition[ClusterThrottle] = - ResourceDefinition[ClusterThrottle]( - group = throttler.crd.Group, - version = "v1alpha1", - kind = throttler.crd.ClusterThrottle.Kind, - scope = ResourceSpecification.Scope.Cluster, - singular = Option(throttler.crd.ClusterThrottle.SingularName), - plural = Option(throttler.crd.ClusterThrottle.PluralName), - shortNames = throttler.crd.ClusterThrottle.ShortNames, - subresources = Some(Subresources().withStatusSubresource) - ) - - implicit val clusterThrottleStatusSubEnabled: HasStatusSubresource[ClusterThrottle] = - CustomResource.statusMethodsEnabler[ClusterThrottle] - } -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/ResourceAmount.scala b/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/ResourceAmount.scala deleted file mode 100644 index ade941b..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/ResourceAmount.scala +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd.v1alpha1 - -import skuber.Resource.ResourceList -import com.github.everpeace.k8s._ -import com.github.everpeace.util.Injection._ -import skuber.Pod -import cats.syntax.order._ - -case class ResourceAmount( - resourceCounts: Option[ResourceCount] = None, - resourceRequests: ResourceList = Map.empty) - -case class ResourceCount(pod: Option[Int] = None) - -case class IsResourceCountThrottled(pod: Option[Boolean] = None) - -case class IsResourceAmountThrottled( - resourceCounts: Option[IsResourceCountThrottled] = None, - resourceRequests: Map[String, Boolean] = Map.empty) - -object ResourceAmount { - trait JsonFormat { - import play.api.libs.json._ - import play.api.libs.functional.syntax._ - import skuber.json.format.{quantityFormat, maybeEmptyFormatMethods} - - implicit val resourceCountsFmt: Format[ResourceCount] = - Json.format[ResourceCount] - - implicit val resourceAmountFmt: Format[ResourceAmount] = ( - (JsPath \ "resourceCounts").formatNullable[ResourceCount] and - (JsPath \ "resourceRequests").formatMaybeEmptyMap[skuber.Resource.Quantity] - )(ResourceAmount.apply, unlift(ResourceAmount.unapply)) - - implicit val isResourceCountThrottledFmt: Format[IsResourceCountThrottled] = - Json.format[IsResourceCountThrottled] - - implicit val isResourceThrottledFmt: Format[IsResourceAmountThrottled] = - Json.format[IsResourceAmountThrottled] - } - - trait Syntax { - val zeroResourceCount = ResourceCount() - implicit class ResourceCountAddition(rc: ResourceCount) { - def add(rc2: ResourceCount) = ResourceCount( - pod = for { - i <- rc.pod.orElse(Option(0)) - j <- rc2.pod - } yield i + j - ) - } - - val zeroResourceAmount = ResourceAmount() - implicit class ResourceAmountAddition(ra: ResourceAmount) { - def add(ra2: ResourceAmount) = ResourceAmount( - resourceCounts = for { - a <- ra.resourceCounts.orElse(Option(zeroResourceCount)) - b <- ra2.resourceCounts - } yield a add b, - resourceRequests = ra.resourceRequests add ra2.resourceRequests - ) - } - - implicit val podCanBeResourceAmount: Pod ==> ResourceAmount = new ==>[Pod, ResourceAmount] { - override def to: Pod => ResourceAmount = - pod => - ResourceAmount( - resourceCounts = Option(ResourceCount(pod = Option(1))), - resourceRequests = pod.totalRequests - ) - } - - implicit class IsResourceAmountThrottledSyntax(throttled: IsResourceAmountThrottled) { - def isAlreadyThrottled(pod: Pod): Boolean = { - val podAmount = pod.==>[ResourceAmount] - val isPodCountThrottled = throttled.resourceCounts.flatMap(_.pod).getOrElse(false) - val isSomePodRequestedResourceThrottled = throttled.resourceRequests - .filterKeys(key => podAmount.resourceRequests.contains(key)) - .exists(_._2) - isPodCountThrottled || isSomePodRequestedResourceThrottled - } - } - - implicit class ResourceAmountSyntax(ra: ResourceAmount) { - def isThrottledFor( - threshold: ResourceAmount, - isThrottledOnEqual: Boolean = true - ): IsResourceAmountThrottled = { - val used = ra - IsResourceAmountThrottled( - resourceCounts = for { - rc <- threshold.resourceCounts - th <- rc.pod - c <- used.resourceCounts.flatMap(_.pod).orElse(Option(0)) - } yield - IsResourceCountThrottled( - pod = Option( - if (isThrottledOnEqual) th <= c else th < c - )), - resourceRequests = threshold.resourceRequests.keys.map { resource => - if (used.resourceRequests.contains(resource)) { - resource -> (if (isThrottledOnEqual) { - threshold.resourceRequests(resource) <= used.resourceRequests(resource) - } else { - threshold.resourceRequests(resource) < used.resourceRequests(resource) - }) - } else { - resource -> false - } - }.toMap - ) - } - - def merge(rb: ResourceAmount): ResourceAmount = { - val mergedResourceCounts = rb.resourceCounts.orElse(ra.resourceCounts) - val mergedResourceRequests = rb.resourceRequests.foldLeft(ra.resourceRequests) { - (merged, req) => - merged + req - } - ResourceAmount( - resourceCounts = mergedResourceCounts, - resourceRequests = mergedResourceRequests - ) - } - - def filterEffectiveOn(threshold: ResourceAmount): ResourceAmount = { - val used = ra - ResourceAmount( - resourceCounts = for { - rc <- threshold.resourceCounts - urc <- used.resourceCounts - } yield { - val pc = for { - _ <- rc.pod - p <- urc.pod - } yield p - ResourceCount(pod = pc) - }, - resourceRequests = used.resourceRequests.filterKeys(threshold.resourceRequests.contains) - ) - } - } - } -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/TemporaryThresholdOverride.scala b/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/TemporaryThresholdOverride.scala deleted file mode 100644 index 73f7d99..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/TemporaryThresholdOverride.scala +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd.v1alpha1 - -import scala.util.Try - -case class TemporaryThresholdOverride( - beginString: String, - begin: skuber.Timestamp, - endString: String, - end: skuber.Timestamp, - threshold: ResourceAmount, - parseError: Option[String]) - -object TemporaryThresholdOverride { - import java.time.ZonedDateTime - import java.time.format.DateTimeFormatter.{ISO_OFFSET_DATE_TIME, ISO_DATE_TIME} - - private def parse(str: String): Try[ZonedDateTime] = - Try(ZonedDateTime.parse(str, ISO_DATE_TIME)) - private def format(zt: ZonedDateTime): String = zt.format(ISO_OFFSET_DATE_TIME) - private def errorMessage(t: Try[ZonedDateTime]): Option[String] = - t.failed.toOption.map(th => s"${th.getMessage()}") - - def apply( - beginString: String, - endString: String, - threshold: ResourceAmount - ): TemporaryThresholdOverride = { - val parsedBegin = parse(beginString) - val parsedEnd = parse(endString) - val message = { - val beginMesssage = errorMessage(parsedBegin).map(msg => s"begin: $msg") - val endMessage = errorMessage(parsedEnd).map(msg => s"end: $msg") - val combined = List(beginMesssage, endMessage).filter(_.nonEmpty).map(_.get).mkString(", ") - Option(combined).filter(_.trim.nonEmpty) - } - - new TemporaryThresholdOverride( - beginString, - parsedBegin.getOrElse(epoch), - endString, - parsedEnd.getOrElse(epoch), - threshold, - message - ) - } - - def apply( - begin: skuber.Timestamp, - end: skuber.Timestamp, - threshold: ResourceAmount - ): TemporaryThresholdOverride = { - new TemporaryThresholdOverride( - format(begin), - begin, - format(end), - end, - threshold, - None - ) - } - - def unapply(arg: TemporaryThresholdOverride): Option[(String, String, ResourceAmount)] = - Option(arg.beginString, arg.endString, arg.threshold) - - trait JsonFormat extends ResourceAmount.JsonFormat { - import play.api.libs.json._ - import play.api.libs.functional.syntax._ - - implicit val temporaryThrottleOverrideFmt: Format[TemporaryThresholdOverride] = ( - (JsPath \ "begin").format[String] and - (JsPath \ "end").format[String] and - (JsPath \ "threshold").format[ResourceAmount] - )(TemporaryThresholdOverride.apply, unlift(TemporaryThresholdOverride.unapply)) - - } - - trait Syntax { - implicit class TemporaryThresholdOverridesSyntax(ovrds: List[TemporaryThresholdOverride]) { - def collectParseError(): List[String] = { - ovrds.zipWithIndex.foldRight(List.empty[String]) { (ovrd, msgs) => - if (ovrd._1.parseError.nonEmpty) { - s"[${ovrd._2}]: ${ovrd._1.parseError.get}" :: msgs - } else { - msgs - } - } - } - } - - implicit class TemporaryThresholdOverrideSyntax(ovrd: TemporaryThresholdOverride) { - def isActiveAt(at: skuber.Timestamp): Boolean = - ovrd.parseError.isEmpty && - (ovrd.begin.isEqual(at) || ovrd.begin.isBefore(at)) && - (at.isEqual(ovrd.end) || at.isBefore(ovrd.end)) - } - } -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/Throttle.scala b/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/Throttle.scala deleted file mode 100644 index d660686..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/Throttle.scala +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd.v1alpha1 - -import com.github.everpeace.k8s.throttler -import com.github.everpeace.k8s.throttler.crd.v1alpha1 -import com.github.everpeace.k8s.throttler.crd.v1alpha1.Implicits._ -import skuber.ResourceSpecification.Subresources -import skuber.apiextensions.CustomResourceDefinition -import skuber.{ - CustomResource, - HasStatusSubresource, - LabelSelector, - Pod, - ResourceDefinition, - ResourceSpecification -} -import com.github.everpeace.k8s._ - -object Throttle { - - case class Selector(selectorTerms: List[SelectorItem]) - case class SelectorItem(podSelector: LabelSelector) - - case class Spec( - throttlerName: String, - selector: Selector, - threshold: ResourceAmount, - temporaryThresholdOverrides: List[TemporaryThresholdOverride] = List.empty) - extends v1alpha1.Spec[Selector] - - case class Status( - throttled: IsResourceAmountThrottled, - used: ResourceAmount, - calculatedThreshold: Option[CalculatedThreshold] = None) - extends v1alpha1.Status - - val crd: CustomResourceDefinition = CustomResourceDefinition[v1alpha1.Throttle] - - def apply(name: String, spec: Spec) = CustomResource[Spec, Status](spec).withName(name) - - trait JsonFormat - extends CalculatedThreshold.JsonFormat - with TemporaryThresholdOverride.JsonFormat - with ResourceAmount.JsonFormat { - import play.api.libs.functional.syntax._ - import play.api.libs.json._ - import skuber.json.format.{maybeEmptyFormatMethods, jsPath2LabelSelFormat} - - implicit val throttleSelectorItemFmt: Format[SelectorItem] = - (JsPath \ "podSelector").formatLabelSelector - .inmap(v1alpha1.Throttle.SelectorItem.apply, unlift(v1alpha1.Throttle.SelectorItem.unapply)) - - implicit val throttleSelectorFmt: Format[Selector] = - (JsPath \ "selectorTerms") - .formatMaybeEmptyList[SelectorItem] - .inmap(v1alpha1.Throttle.Selector.apply, unlift(v1alpha1.Throttle.Selector.unapply)) - - implicit val throttleSpecFmt: Format[v1alpha1.Throttle.Spec] = ( - (JsPath \ "throttlerName").formatMaybeEmptyString(true) and - (JsPath \ "selector").format[v1alpha1.Throttle.Selector] and - (JsPath \ "threshold").format[ResourceAmount] and - (JsPath \ "temporaryThresholdOverrides") - .formatMaybeEmptyList[v1alpha1.TemporaryThresholdOverride] - )(v1alpha1.Throttle.Spec.apply, unlift(v1alpha1.Throttle.Spec.unapply)) - - implicit val throttleStatusFmt: Format[v1alpha1.Throttle.Status] = - Json.format[v1alpha1.Throttle.Status] - } - - trait Syntax { - - implicit class ThrottleSelectorItemSyntax(selectorItem: SelectorItem) { - def matches(pod: skuber.Pod): Boolean = { - selectorItem.podSelector.matches(pod.metadata.labels) - } - } - - implicit class ThrottleSelectorSyntax(selector: Selector) { - def matches(pod: skuber.Pod): Boolean = { - selector.selectorTerms.exists(_.matches(pod)) - } - } - - implicit class ThrottleSyntax(throttle: Throttle) { - def isTarget(pod: Pod): Boolean = throttle.spec.selector.matches(pod) - } - } - - trait ResourceDefinitions { - implicit val throttleResourceDefinition: ResourceDefinition[Throttle] = - ResourceDefinition[Throttle]( - group = throttler.crd.Group, - version = "v1alpha1", - kind = throttler.crd.Throttle.Kind, - scope = ResourceSpecification.Scope.Namespaced, - singular = Option(throttler.crd.Throttle.SingularName), - plural = Option(throttler.crd.Throttle.PluralName), - shortNames = throttler.crd.Throttle.ShortNames, - subresources = Some(Subresources().withStatusSubresource) - ) - - implicit val throttleStatusSubEnabled: HasStatusSubresource[Throttle] = - CustomResource.statusMethodsEnabler[Throttle] - } -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/package.scala b/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/package.scala deleted file mode 100644 index bedcb14..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/crd/v1alpha1/package.scala +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd - -import java.time.{Instant, ZoneOffset, ZonedDateTime} - -import com.github.everpeace.k8s.throttler.KubeThrottleConfig -import skuber.{CustomResource, ListResource, Pod} -import com.github.everpeace.util.Injection._ - -package object v1alpha1 { - type Throttle = CustomResource[v1alpha1.Throttle.Spec, v1alpha1.Throttle.Status] - type ThrottleList = ListResource[Throttle] - - type ClusterThrottle = - CustomResource[v1alpha1.ClusterThrottle.Spec, v1alpha1.ClusterThrottle.Status] - type ClusterThrottleList = ListResource[ClusterThrottle] - - val epoch = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0), ZoneOffset.UTC) - - trait Spec[Selector] { - def throttlerName: String - def selector: Selector - def threshold: ResourceAmount - def temporaryThresholdOverrides: List[TemporaryThresholdOverride] - } - - trait Status { - def throttled: IsResourceAmountThrottled - def used: ResourceAmount - def calculatedThreshold: Option[CalculatedThreshold] - } - - trait JsonFormat - extends v1alpha1.Throttle.JsonFormat - with v1alpha1.ClusterThrottle.JsonFormats - with CalculatedThreshold.JsonFormat - with TemporaryThresholdOverride.JsonFormat - with ResourceAmount.JsonFormat - - trait Syntax - extends v1alpha1.Throttle.Syntax - with v1alpha1.ClusterThrottle.Syntax - with CalculatedThreshold.Syntax - with TemporaryThresholdOverride.Syntax - with ResourceAmount.Syntax - - trait ResourceDefinitions - extends v1alpha1.Throttle.ResourceDefinitions - with v1alpha1.ClusterThrottle.ResourceDefinitions - - object Implicits extends JsonFormat with Syntax with ResourceDefinitions { - - implicit class AbstractThrottleSyntax(abstThr: CustomResource[_ <: Spec[_], _ <: Status]) { - def isAlreadyActiveFor(pod: Pod, isTarget: => Boolean): Boolean = isTarget && { - (for { - st <- abstThr.status - throttled = st.throttled - } yield throttled.isAlreadyThrottled(pod)).getOrElse(false) - } - - def isInsufficientFor(pod: Pod, isTarget: => Boolean): Boolean = isTarget && { - val podResourceAmount = pod.==>[ResourceAmount] - val used = abstThr.status.map(_.used).getOrElse(zeroResourceAmount) - val threshold = (for { - st <- abstThr.status - calculated <- st.calculatedThreshold - } yield calculated.threshold).getOrElse(abstThr.spec.threshold) - val isThrottled = - (podResourceAmount add used).isThrottledFor(threshold, isThrottledOnEqual = false) - isThrottled.resourceCounts.flatMap(_.pod).getOrElse(false) || isThrottled.resourceRequests - .exists(_._2) - } - - def hasTemporaryOverridesToReconcile: Boolean = { - val isTargetOpt = for { - st <- abstThr.status - calculatedThreshold <- st.calculatedThreshold - lastCalculatedAt = calculatedThreshold.calculatedAt - } yield { - abstThr.spec.temporaryThresholdOverrides.exists(o => - lastCalculatedAt.isBefore(o.end) || lastCalculatedAt.isEqual(o.end)) - } - isTargetOpt.getOrElse(abstThr.spec.temporaryThresholdOverrides.nonEmpty) - } - } - - implicit class SpecSyntax[S](spec: Spec[S]) { - def statusFor[ST <: Status]( - used: ResourceAmount, - at: skuber.Timestamp - )(apply: (IsResourceAmountThrottled, ResourceAmount, Option[CalculatedThreshold]) => ST - ): ST = { - val calculated = spec.thresholdAt(at) - apply( - used.isThrottledFor(calculated, isThrottledOnEqual = true), - used.filterEffectiveOn(spec.threshold), - Option( - CalculatedThreshold(calculated, - at, - spec.temporaryThresholdOverrides.collectParseError()) - ) - ) - } - - def thresholdAt(at: skuber.Timestamp): ResourceAmount = - (spec.threshold, spec.temporaryThresholdOverrides).thresholdAt(at) - } - - implicit class StatusSyntax(observedOpt: Option[Status]) { - - def needToUpdateAt(at: skuber.Timestamp)(implicit conf: KubeThrottleConfig): Boolean = { - lazy val empty = observedOpt.isEmpty - lazy val noCalculated = observedOpt.get.calculatedThreshold.isEmpty - lazy val expired = observedOpt.get.calculatedThreshold.get.calculatedAt - .plusNanos(conf.statusForceUpdateInterval.toNanos) - .isBefore(at) - empty || noCalculated || expired - } - - def needToUpdateWith(desired: Status)(implicit conf: KubeThrottleConfig): Boolean = - if (observedOpt.isEmpty) { - true - } else { - val observedStatus = observedOpt.get - lazy val invariant = - (observedStatus.calculatedThreshold, desired.calculatedThreshold) match { - case (Some(o), Some(d)) => o.calculatedAt.isBefore(d.calculatedAt) - case _ => true - } - lazy val used = observedStatus.used != desired.used - lazy val throttled = observedStatus.throttled != desired.throttled - lazy val threshold = - (observedStatus.calculatedThreshold, desired.calculatedThreshold) match { - case (None, None) => false - case (None, Some(_)) => true - case (Some(_), None) => true - case (Some(o), Some(d)) => - lazy val expired = o.calculatedAt - .plusNanos(conf.statusForceUpdateInterval.toNanos) - .isBefore(d.calculatedAt) - lazy val notEquivalent = o.copy(calculatedAt = epoch) != d.copy( - calculatedAt = epoch) - expired || notEquivalent - } - invariant && (used || throttled || threshold) - } - } - } -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/metrics/ClusterThrottleControllerMetrics.scala b/src/main/scala/com/github/everpeace/k8s/throttler/metrics/ClusterThrottleControllerMetrics.scala deleted file mode 100644 index 0c89a3a..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/metrics/ClusterThrottleControllerMetrics.scala +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.metrics -import com.github.everpeace.k8s.throttler.crd.v1alpha1 -import com.github.everpeace.k8s.throttler.crd.v1alpha1.CalculatedThreshold - -trait ClusterThrottleControllerMetrics extends MetricsBase { - self: { - def log: { - def info(s: String): Unit - def debug(s: String): Unit - } - } => - - def resetClusterThrottleMetric(clthrottle: v1alpha1.ClusterThrottle): Unit = { - import com.github.everpeace.k8s.throttler.crd.v1alpha1.Implicits.ResourceAmountSyntax - - val zeroSpec = clthrottle.spec.copy( - threshold = zeroResourceAmount(clthrottle.spec.threshold) - ) - val zeroFalseStatus = clthrottle.status.map( - st => - st.copy( - throttled = falseIsResourceAmountThrottled(st.throttled), - used = zeroResourceAmount(st.used), - calculatedThreshold = Option(CalculatedThreshold( - zeroResourceAmount( - clthrottle.spec.temporaryThresholdOverrides.foldRight(clthrottle.spec.threshold) { - (o, acc) => - acc.merge(o.threshold) - } - ), - st.calculatedThreshold.map(_.calculatedAt).getOrElse(java.time.ZonedDateTime.now()) - )) - )) - val zero = clthrottle.copy( - spec = zeroSpec, - status = zeroFalseStatus - ) - recordClusterThrottleSpecMetric(zero) - recordClusterThrottleStatusMetric(zero) - } - - def recordClusterThrottleSpecMetric(clthrottle: v1alpha1.ClusterThrottle): Unit = { - val metadataTags = metadataTagsForClusterThrottle(clthrottle) - recordResourceAmountMetric("clusterthrottle.spec.threshold", - metadataTags, - clthrottle.spec.threshold) - } - - def recordClusterThrottleStatusMetric(clthrottle: v1alpha1.ClusterThrottle): Unit = { - val metadataTags = metadataTagsForClusterThrottle(clthrottle) - clthrottle.status.foreach { status => - recordIsThrottledMetric("clusterthrottle.status.throttled", metadataTags, status.throttled) - recordResourceAmountMetric("clusterthrottle.status.used", metadataTags, status.used) - status.calculatedThreshold.foreach { calculated => - recordResourceAmountMetric("clusterthrottle.status.calculated.threshold", - metadataTags, - calculated.threshold) - } - } - } - - def metadataTagsForClusterThrottle(throttle: v1alpha1.ClusterThrottle): kamon.Tags = { - val metadata = List( - "name" -> throttle.metadata.name, - "uuid" -> throttle.metadata.uid - ).toMap ++ throttle.metadata.clusterName.map(cn => Map("cluster" -> cn)).getOrElse(Map.empty) - - sanitizeTagKeys(metadata ++ throttle.metadata.labels) - } - -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/metrics/MetricsBase.scala b/src/main/scala/com/github/everpeace/k8s/throttler/metrics/MetricsBase.scala deleted file mode 100644 index 8208140..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/metrics/MetricsBase.scala +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.metrics -import com.github.everpeace.k8s.throttler.crd.v1alpha1.{IsResourceAmountThrottled, ResourceAmount} -import kamon.Kamon -import skuber.Resource - -trait MetricsBase { - self: { - def log: { - def info(s: String): Unit - def debug(s: String): Unit - } - } => - - def resourceQuantityToLong(q: (String, Resource.Quantity)): Long = q._1 match { - case Resource.cpu => - (q._2.amount * 1000).toLong // convert to milli - case _ => q._2.amount.toLong - } - - // prometheus restricts label name characters - // ref: https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels - def sanitizeTagKeys(tag: kamon.Tags): kamon.Tags = tag.map { - case (k, v) => - k.replaceAll("[^a-zA-z_]", "_").replaceAll("^[^a-zA-Z]", "X") -> v - } - - def resourceQuantityToTag(q: (String, _)): kamon.Tags = Map("resource" -> q._1) - def resourceCountsTag(resourceName: String): kamon.Tags = Map("resource" -> resourceName) - - def b2i(b: Boolean): Int = b compare false - - def recordIsThrottledMetric( - metricsPrefix: String, - tags: kamon.Tags, - throttled: IsResourceAmountThrottled - ): Unit = { - val statusRRGauge = Kamon.gauge(s"$metricsPrefix.resourceRequests") - val statusRCGauge = Kamon.gauge(s"$metricsPrefix.resourceCounts") - - throttled.resourceRequests foreach { rq => - val _tags = sanitizeTagKeys(tags ++ resourceQuantityToTag(rq)) - log.debug( - s"setting gauge '${statusRRGauge.name}{${_tags.values.mkString(",")}}' value with ${b2i(rq._2)}") - statusRRGauge.refine(_tags).set(b2i(rq._2)) - } - - for { - rc <- throttled.resourceCounts - value <- rc.pod - } yield { - val _tags = sanitizeTagKeys(tags ++ resourceCountsTag("pod")) - log.debug( - s"setting gauge '${statusRCGauge.name}{${tags.values.mkString(",")}}' value with ${b2i(value)}") - statusRCGauge.refine(_tags).set(b2i(value)) - } - } - - def recordResourceAmountMetric( - metricsPrefix: String, - tags: kamon.Tags, - ra: ResourceAmount - ): Unit = { - val rrGauge = Kamon.gauge(s"$metricsPrefix.resourceRequests") - val rcGauge = Kamon.gauge(s"$metricsPrefix.resourceCounts") - - ra.resourceRequests foreach { rq => - val _tags = sanitizeTagKeys(tags ++ resourceQuantityToTag(rq)) - val value = resourceQuantityToLong(rq) - log.debug( - s"setting gauge '${rrGauge.name}{${_tags.values.mkString(",")}}' value with ${value}") - rrGauge.refine(_tags).set(value) - } - - for { - rc <- ra.resourceCounts - value <- rc.pod - } yield { - val _tags = sanitizeTagKeys(tags ++ resourceCountsTag("pod")) - log.debug( - s"setting gauge '${rcGauge.name}{${_tags.values.mkString(",")}}' value with ${value}") - rcGauge.refine(_tags).set(value) - } - } - - def zeroResourceAmount(ra: ResourceAmount): ResourceAmount = ra.copy( - resourceCounts = ra.resourceCounts.map( - rc => - rc.copy( - pod = rc.pod.map(_ => 0) - )), - resourceRequests = ra.resourceRequests.mapValues(_ => Resource.Quantity("0")) - ) - - def falseIsResourceAmountThrottled( - throttled: IsResourceAmountThrottled - ): IsResourceAmountThrottled = throttled.copy( - resourceCounts = throttled.resourceCounts.map { rc => - rc.copy( - pod = rc.pod.map(_ => false) - ) - }, - resourceRequests = throttled.resourceRequests.mapValues(_ => false) - ) -} diff --git a/src/main/scala/com/github/everpeace/k8s/throttler/metrics/ThrottleControllerMetrics.scala b/src/main/scala/com/github/everpeace/k8s/throttler/metrics/ThrottleControllerMetrics.scala deleted file mode 100644 index 492e208..0000000 --- a/src/main/scala/com/github/everpeace/k8s/throttler/metrics/ThrottleControllerMetrics.scala +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.metrics -import com.github.everpeace.k8s.throttler.crd.v1alpha1 -import com.github.everpeace.k8s.throttler.crd.v1alpha1.CalculatedThreshold - -trait ThrottleControllerMetrics extends MetricsBase { - self: { - def log: { - def info(s: String): Unit - def debug(s: String): Unit - } - } => - - def resetThrottleMetric(throttle: v1alpha1.Throttle): Unit = { - import com.github.everpeace.k8s.throttler.crd.v1alpha1.Implicits.ResourceAmountSyntax - - val zeroSpec = throttle.spec.copy( - threshold = zeroResourceAmount(throttle.spec.threshold) - ) - val zeroFalseStatus = throttle.status.map( - st => - st.copy( - throttled = falseIsResourceAmountThrottled(st.throttled), - used = zeroResourceAmount(st.used), - calculatedThreshold = Option(CalculatedThreshold( - zeroResourceAmount( - throttle.spec.temporaryThresholdOverrides.foldRight(throttle.spec.threshold) { - (o, acc) => - acc.merge(o.threshold) - } - ), - st.calculatedThreshold.map(_.calculatedAt).getOrElse(java.time.ZonedDateTime.now()) - )) - )) - val zero = throttle.copy( - spec = zeroSpec, - status = zeroFalseStatus - ) - recordThrottleSpecMetric(zero) - recordThrottleStatusMetric(zero) - } - - def recordThrottleSpecMetric(throttle: v1alpha1.Throttle): Unit = { - val metadataTags = metadataTagsForThrottle(throttle) - recordResourceAmountMetric("throttle.spec.threshold", metadataTags, throttle.spec.threshold) - } - - def recordThrottleStatusMetric(throttle: v1alpha1.Throttle): Unit = { - val metadataTags = metadataTagsForThrottle(throttle) - throttle.status.foreach { status => - recordIsThrottledMetric("throttle.status.throttled", metadataTags, status.throttled) - recordResourceAmountMetric("throttle.status.used", metadataTags, status.used) - status.calculatedThreshold.foreach { calculated => - recordResourceAmountMetric("throttle.status.calculated.threshold", - metadataTags, - calculated.threshold) - } - } - } - - def metadataTagsForThrottle(throttle: v1alpha1.Throttle): kamon.Tags = { - val metadata = List( - "name" -> throttle.metadata.name, - "namespace" -> throttle.metadata.namespace, - "uuid" -> throttle.metadata.uid - ).toMap ++ throttle.metadata.clusterName.map(cn => Map("cluster" -> cn)).getOrElse(Map.empty) - - sanitizeTagKeys(metadata ++ throttle.metadata.labels) - } - -} diff --git a/src/main/scala/com/github/everpeace/util/ActorWatcher.scala b/src/main/scala/com/github/everpeace/util/ActorWatcher.scala deleted file mode 100644 index a11ef7b..0000000 --- a/src/main/scala/com/github/everpeace/util/ActorWatcher.scala +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.util -import akka.actor.{Actor, ActorRef, Props, Terminated} -import com.github.everpeace.util.ActorWatcher.IsTargetAlive - -class ActorWatcher(target: ActorRef) extends Actor { - var targetIsAlive = true - - override def preStart(): Unit = { - context.watch(target) - } - - override def receive: Receive = { - case Terminated(ref) => - if (ref == target) { - targetIsAlive = false - } - case IsTargetAlive => sender ! targetIsAlive - } -} - -object ActorWatcher { - def props(target: ActorRef) = Props(new ActorWatcher(target)) - object IsTargetAlive -} diff --git a/src/main/scala/com/github/everpeace/util/Injection.scala b/src/main/scala/com/github/everpeace/util/Injection.scala deleted file mode 100644 index 193316b..0000000 --- a/src/main/scala/com/github/everpeace/util/Injection.scala +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.util -import cats.Functor -import cats.arrow.{Arrow, Compose} -import cats.syntax.compose._ - -import scala.language.higherKinds - -object Injection { - trait Injection[+Arr[_, _], A, B] { self => - def to: Arr[A, B] - } - - type InjectionSet[A, B] = Injection[Function1, A, B] - type ==>[A, B] = InjectionSet[A, B] - object ==> { - def apply[A, B](a2b: A => B): A ==> B = new (A ==> B) { - override def to: A => B = a2b - } - } - - implicit def ArrowInjectionCanBeCompose[Arr[_, _]: Arrow] = - new Compose[({ type λ[A, B] = Injection[Arr, A, B] })#λ] { - override def compose[A, B, C]( - f: Injection[Arr, B, C], - g: Injection[Arr, A, B] - ): Injection[Arr, A, C] = new Injection[Arr, A, C] { - override def to: Arr[A, C] = g.to >>> f.to - } - } - - implicit def transitiveInjection[Arr[_, _], A, B, C]( - implicit - arrow: Arrow[Arr], - ab: Injection[Arr, A, B], - bc: Injection[Arr, B, C] - ): Injection[Arr, A, C] = ab >>> bc - - implicit def functorInjection[F[_], A, B]( - implicit - f: Functor[F], - inj: A ==> B - ): InjectionSet[F[A], F[B]] = ==>(f.map(_)(inj.to)) - - implicit class injectSyntax[A](a: A) { - def ==>[B](implicit a2b: A ==> B): B = a2b.to(a) - } - -} diff --git a/src/main/scala/com/github/everpeace/util/Isomorphism.scala b/src/main/scala/com/github/everpeace/util/Isomorphism.scala deleted file mode 100644 index 9985db9..0000000 --- a/src/main/scala/com/github/everpeace/util/Isomorphism.scala +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.util - -import cats.Functor -import cats.arrow.Arrow -import cats.syntax.compose._ - -import scala.language.higherKinds - -object Isomorphism { - - trait Isomorphism[+Arr[_, _], A, B] { self => - def to: Arr[A, B] - def from: Arr[B, A] - } - - type IsoSet[A, B] = Isomorphism[Function1, A, B] - type <=>[A, B] = IsoSet[A, B] - - implicit class ArrowIsomorphismSyntax[Arr[_, _]: Arrow, A, B](iso1: Isomorphism[Arr, A, B]) { - def composeIsomorphism[C](iso2: Isomorphism[Arr, B, C]): Isomorphism[Arr, A, C] = - new Isomorphism[Arr, A, C] { - def to = iso1.to >>> iso2.to - def from = iso1.from <<< iso2.from - } - } - - implicit def functorIso[F[_]: Functor, A, B](implicit iso: A <=> B) = new <=>[F[A], F[B]] { - override def to: F[A] => F[B] = fa => implicitly[Functor[F]].map(fa)(iso.to) - - override def from: F[B] => F[A] = fb => implicitly[Functor[F]].map(fb)(iso.from) - } - - implicit class isoSyntax[A](a: A) { - def <=>[B](implicit ab: A <=> B): B = ab.to(a) - } -} diff --git a/src/main/scala/io/k8s/pkg/scheduler/api/v1/package.scala b/src/main/scala/io/k8s/pkg/scheduler/api/v1/package.scala deleted file mode 100644 index 057bb21..0000000 --- a/src/main/scala/io/k8s/pkg/scheduler/api/v1/package.scala +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 io.k8s.pkg.scheduler.api - -import play.api.libs.functional.syntax._ -import play.api.libs.json._ -import skuber.json.format.{podSpecFmt => _, _} -import skuber.{ListMeta, ListResource, Node, NodeList, ObjectMeta, Pod} - -// ref: https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/api/v1/types.go -package object v1 { - - case class ExtenderArgs(pod: Pod, nodes: Option[NodeList], nodenames: List[String]) - - case class ExtenderFilterResult( - nodes: Option[NodeList] = None, - nodenames: List[String] = List.empty, - failedNodes: Map[String, String] = Map.empty, - error: Option[String] = None) - - case class MetaPod(uid: String) - case class Victims(pods: List[Pod], numPDBViolations: Int) - case class MetaVictims(pods: List[MetaPod], numPDBViolations: Int) - case class ExtenderPreemptionArgs( - pod: Pod, - nodeNameToVictims: Map[String, Victims], - nodeNameToMetaVictims: Map[String, MetaVictims]) - case class ExtenderPreemptionResult(nodeNameToMetaVictims: Map[String, MetaVictims]) - - object Implicits { - implicit val nodeFormat: Format[Node] = ( - (JsPath \ "metadata").lazyFormat[ObjectMeta](objectMetaFormat) and - (JsPath \ "spec").formatNullable[Node.Spec] and - (JsPath \ "status").formatNullable[Node.Status] - )( - Node("Node", "v1", _, _, _), - unlift((n: Node) => Option(n.metadata, n.spec, n.status)) - ) - implicit val nodeListFormat: Format[NodeList] = ( - (JsPath \ "metadata").formatNullable[ListMeta] and - (JsPath \ "items").formatMaybeEmptyList[Node](nodeFormat.reads(_), nodeFormat.writes(_)) - )( - ListResource[Node]("v1", "NodeList", _, _), - unlift((nl: NodeList) => Option(nl.metadata, nl.items)) - ) - implicit val extenderArgsFmt: Format[ExtenderArgs] = ( - (JsPath \ "Pod").format[Pod] and - (JsPath \ "Nodes").formatNullable[NodeList](nodeListFormat) and - (JsPath \ "NodeNames").formatMaybeEmptyList[String] - )(ExtenderArgs.apply, unlift(ExtenderArgs.unapply)) - - implicit val extenderFilterResult: Format[ExtenderFilterResult] = ( - (JsPath \ "Nodes").formatNullable[NodeList](nodeListFormat) and - (JsPath \ "NodeNames").formatMaybeEmptyList[String] and - (JsPath \ "FailedNodes").formatMaybeEmptyMap[String] and - (JsPath \ "Error").formatNullable[String] - )(ExtenderFilterResult.apply, unlift(ExtenderFilterResult.unapply)) - - implicit val metaPodFmt: Format[MetaPod] = - (JsPath \ "UID").format[String].inmap(MetaPod.apply, unlift(MetaPod.unapply)) - implicit val victimsFmt: Format[Victims] = ( - (JsPath \ "Pods").formatMaybeEmptyList[Pod] and - (JsPath \ "NumPDBViolations").format[Int] - )(Victims.apply, unlift(Victims.unapply)) - implicit val metaVictimsFmt: Format[MetaVictims] = ( - (JsPath \ "Pods").formatMaybeEmptyList[MetaPod] and - (JsPath \ "NumPDBViolations").format[Int] - )(MetaVictims.apply, unlift(MetaVictims.unapply)) - implicit val extenderPreemptionArgsFmt: Format[ExtenderPreemptionArgs] = ( - (JsPath \ "Pod").format[Pod] and - (JsPath \ "NodeNameToVictims").formatMaybeEmptyMap[Victims] and - (JsPath \ "NodeNameToMetaVictims").formatMaybeEmptyMap[MetaVictims] - )(ExtenderPreemptionArgs.apply, unlift(ExtenderPreemptionArgs.unapply)) - implicit val extenderPreemptionResultFmt: Format[ExtenderPreemptionResult] = - (JsPath \ "NodeNameToMetaVictims") - .formatMaybeEmptyMap[MetaVictims] - .inmap(ExtenderPreemptionResult.apply, unlift(ExtenderPreemptionResult.unapply)) - } - -} diff --git a/src/test/resources/reference.conf b/src/test/resources/reference.conf deleted file mode 100644 index 9bb5334..0000000 --- a/src/test/resources/reference.conf +++ /dev/null @@ -1,2 +0,0 @@ -akka.loglevel = "DEBUG" - diff --git a/src/test/scala/com/github/everpeace/k8s/LabelSelectorMatcherSpec.scala b/src/test/scala/com/github/everpeace/k8s/LabelSelectorMatcherSpec.scala deleted file mode 100644 index 04e3c9f..0000000 --- a/src/test/scala/com/github/everpeace/k8s/LabelSelectorMatcherSpec.scala +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s - -import org.scalatest.{FreeSpec, Matchers} -import skuber.LabelSelector -import skuber.LabelSelector.IsEqualRequirement - -class LabelSelectorMatcherSpec extends FreeSpec with Matchers { - - "LabelSelectorRequirementMatcher" - { - "works correctly for ExistsRequirement" in { - LabelSelector.ExistsRequirement("key").matches(Map("key" -> "value")) shouldBe true - LabelSelector.ExistsRequirement("key").matches(Map("key2" -> "value")) shouldBe false - LabelSelector.ExistsRequirement("key").matches(Map.empty) shouldBe false - } - "works correctly for NotExistsRequirement" in { - LabelSelector.NotExistsRequirement("key").matches(Map("key" -> "value")) shouldBe false - LabelSelector.NotExistsRequirement("key").matches(Map("key2" -> "value")) shouldBe true - LabelSelector.NotExistsRequirement("key").matches(Map.empty) shouldBe true - } - "works correctly for IsEqualRequirement" in { - LabelSelector.IsEqualRequirement("key", "value").matches(Map("key" -> "value")) should be( - true) - LabelSelector.IsEqualRequirement("key", "value").matches(Map("key" -> "value2")) should be( - false) - LabelSelector.IsEqualRequirement("key", "value").matches(Map.empty) shouldBe false - } - "works correctly for IsNotEqualRequirement" in { - LabelSelector.IsNotEqualRequirement("key", "value").matches(Map("key" -> "value")) should be( - false) - LabelSelector.IsNotEqualRequirement("key", "value").matches(Map("key" -> "value2")) should be( - true) - LabelSelector.IsNotEqualRequirement("key", "value").matches(Map.empty) shouldBe true - } - "works correctly for InRequirement" in { - LabelSelector.InRequirement("key", List("value")).matches(Map("key" -> "value")) should be( - true) - LabelSelector.InRequirement("key", List("value")).matches(Map("key" -> "value2")) should be( - false) - LabelSelector.InRequirement("key", List("value")).matches(Map.empty) shouldBe false - } - "works correctly for NotInRequirement" in { - LabelSelector.NotInRequirement("key", List("value")).matches(Map("key" -> "value")) should be( - false) - LabelSelector - .NotInRequirement("key", List("value")) - .matches(Map("key" -> "value2")) shouldBe true - LabelSelector.NotInRequirement("key", List("value")).matches(Map.empty) shouldBe true - } - } - - "LabelSelectorMatcher" - { - "works correctly" in { - LabelSelector(IsEqualRequirement("key", "value"), IsEqualRequirement("key2", "value2")) - .matches(Map("key" -> "value", "key2" -> "value2")) shouldBe true - LabelSelector(IsEqualRequirement("key", "value"), IsEqualRequirement("key2", "value2")) - .matches(Map("key" -> "value")) shouldBe false - LabelSelector(IsEqualRequirement("key", "value"), IsEqualRequirement("key2", "value2")) - .matches(Map.empty) shouldBe false - LabelSelector().matches(Map("key" -> "value")) shouldBe true - } - } -} diff --git a/src/test/scala/com/github/everpeace/k8s/PodTotalRequestsSpec.scala b/src/test/scala/com/github/everpeace/k8s/PodTotalRequestsSpec.scala deleted file mode 100644 index 39b516d..0000000 --- a/src/test/scala/com/github/everpeace/k8s/PodTotalRequestsSpec.scala +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s - -import org.scalatest.{FreeSpec, Matchers} -import skuber.Resource.Quantity -import skuber.{Container, Pod, Resource} - -class PodTotalRequestsSpec extends FreeSpec with Matchers { - - def pod(resourceRequirements: List[Option[Resource.Requirements]]): Pod = { - val containers = resourceRequirements.zipWithIndex.map { - case (rr, index) => - Container( - name = s"ctr$index", - image = "image", - resources = rr - ) - } - val spec = Pod.Spec( - containers = containers - ) - Pod(name = "dummy", spec = spec) - } - - "PodTotalResourceSpec" - { - "can sum up only 'requests'" in { - val requirements = List( - Some( - Resource.Requirements(limits = Map("rb" -> Quantity("1")), - requests = Map("ra" -> Quantity("1")))), - None, - Some( - Resource.Requirements(limits = Map("rb" -> Quantity("1")), - requests = Map("ra" -> Quantity("1")))), - ) - pod(requirements).totalRequests shouldBe Map("ra" -> Quantity("2")) - } - "can sum up even when no 'requests'" in { - val requirements = List(None, None) - pod(requirements).totalRequests shouldBe Map.empty - } - } - -} diff --git a/src/test/scala/com/github/everpeace/k8s/ResourceListSpec.scala b/src/test/scala/com/github/everpeace/k8s/ResourceListSpec.scala deleted file mode 100644 index 80b7100..0000000 --- a/src/test/scala/com/github/everpeace/k8s/ResourceListSpec.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s - -import cats.implicits._ -import org.scalatest.{FreeSpec, Matchers} -import skuber.Resource.Quantity - -class ResourceListSpec extends FreeSpec with Matchers { - "Quantity" - { - "can add" in { - (Quantity("1") add Quantity("2")) shouldBe Quantity("3") - } - - "can compare" in { - (Quantity("1") compare Quantity("2")) shouldBe -1 - (Quantity("1") compare Quantity("1")) shouldBe 0 - (Quantity("2") compare Quantity("1")) shouldBe 1 - } - } - - "ResourceList" - { - "can add" in { - val lhs = Map("ra" -> Quantity("1")) - val rhs = Map("ra" -> Quantity("3"), "rb" -> Quantity("1")) - val expected = Map("ra" -> Quantity("4"), "rb" -> Quantity("1")) - (lhs add rhs) shouldBe expected - } - } -} diff --git a/src/test/scala/com/github/everpeace/k8s/Skuber2_1_0CanParseMatchFieldsSpec.scala b/src/test/scala/com/github/everpeace/k8s/Skuber2_1_0CanParseMatchFieldsSpec.scala deleted file mode 100644 index a4a5090..0000000 --- a/src/test/scala/com/github/everpeace/k8s/Skuber2_1_0CanParseMatchFieldsSpec.scala +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s - -import org.scalatest.{FreeSpec, Matchers} -import play.api.libs.json._ -import skuber._ -import skuber.json.format._ -import Pod.Affinity._ -import NodeAffinity.{RequiredDuringSchedulingIgnoredDuringExecution => Required} - -class Skuber2_1_0CanParseMatchFieldsSpec extends FreeSpec with Matchers { - - "Skuber2_1_0" - { - "can parse nodeSelectorTerms properly (both matchFields and matchExpressions)" in { - val termJson = Json.parse( - """ - |{ - | "nodeSelectorTerms": [{ - | "matchFields": [{ - | "key": "some-key", - | "operator": "In", - | "values": ["some-value"] - | }] - | }] - |} - """.stripMargin - ) - val myTerm = Json.fromJson[Required](termJson).get - val term = Required( - nodeSelectorTerms = List( - NodeSelectorTerm( - List.empty, - NodeSelectorRequirements( - NodeSelectorRequirement("some-key", NodeSelectorOperator.In, List("some-value")) - ) - ))) - myTerm shouldBe term - } - - "can parse it only with matchExpressions" in { - val termJson = Json.parse( - """ - |{ - | "nodeSelectorTerms": [{ - | "matchExpressions": [{ - | "key": "some-key", - | "operator": "In", - | "values": ["some-value"] - | }] - | }] - |} - """.stripMargin - ) - val myTerm = Json.fromJson[Required](termJson).get - val term = Required( - nodeSelectorTerms = List( - NodeSelectorTerm( - NodeSelectorRequirements( - NodeSelectorRequirement("some-key", NodeSelectorOperator.In, List("some-value")) - )) - ) - ) - myTerm shouldBe term - } - - "can parse it with both matchExpressions and matchFields, but matchFields are ignored" in { - val termJson = Json.parse( - """ - |{ - | "nodeSelectorTerms": [{ - | "matchExpressions": [{ - | "key": "some-key", - | "operator": "In", - | "values": ["some-value"] - | }], - | "matchFields": [{ - | "key": "some-key", - | "operator": "In", - | "values": ["some-value"] - | }] - | }] - |} - """.stripMargin - ) - val myTerm = Json.fromJson[Required](termJson).get - val term = Required( - nodeSelectorTerms = List( - NodeSelectorTerm( - NodeSelectorRequirements( - NodeSelectorRequirement("some-key", NodeSelectorOperator.In, List("some-value")) - ), - NodeSelectorRequirements( - NodeSelectorRequirement("some-key", NodeSelectorOperator.In, List("some-value")) - ) - ) - ) - ) - myTerm shouldBe term - } - } -} diff --git a/src/test/scala/com/github/everpeace/k8s/throttler/RoutesSpec.scala b/src/test/scala/com/github/everpeace/k8s/throttler/RoutesSpec.scala deleted file mode 100644 index 7f34dd6..0000000 --- a/src/test/scala/com/github/everpeace/k8s/throttler/RoutesSpec.scala +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler -import akka.actor.{Actor, Props} -import akka.http.scaladsl.model._ -import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest} -import akka.testkit.TestDuration -import akka.util.{ByteString, Timeout} -import com.github.everpeace.k8s.throttler.controller.ThrottleRequestHandler.{ - CheckThrottleRequest, - NotThrottled, - Throttled -} -import com.github.everpeace.k8s.throttler.crd.v1alpha1 -import com.github.everpeace.k8s.throttler.crd.v1alpha1.{IsResourceAmountThrottled, ResourceAmount} -import com.github.everpeace.util.ActorWatcher -import de.heikoseeberger.akkahttpplayjson.PlayJsonSupport -import io.k8s.pkg.scheduler.api.v1.Implicits._ -import io.k8s.pkg.scheduler.api.v1._ -import org.scalatest.{FreeSpec, Matchers} -import skuber.Resource.Quantity -import skuber._ - -import scala.concurrent.duration._ - -class RoutesSpec extends FreeSpec with Matchers with ScalatestRouteTest with PlayJsonSupport { - - implicit val timeout = RouteTestTimeout(3.seconds.dilated) - - def dummyActiveThrottleFor(p: Pod) = { - v1alpha1 - .Throttle( - "throttle", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector( - List( - v1alpha1.Throttle.SelectorItem(LabelSelector()) - )), - threshold = ResourceAmount( - resourceRequests = Map("cpu" -> Quantity("1")) - ) - ) - ) - .withNamespace(p.namespace) - .withStatus( - v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("cpu" -> true) - ), - used = ResourceAmount( - resourceRequests = Map("cpu" -> Quantity("2")) - ) - )) - } - - def dummyInsufficientThrottleFor(p: Pod) = { - v1alpha1 - .Throttle( - "nospacethrottle", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector( - List( - v1alpha1.Throttle.SelectorItem(LabelSelector()) - )), - threshold = ResourceAmount( - resourceRequests = Map("cpu" -> Quantity("1")) - ) - ) - ) - .withNamespace(p.namespace) - } - - def dummyActiveClusterThrottleFor(p: Pod) = { - v1alpha1 - .ClusterThrottle( - "clusterthrottle", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem(LabelSelector()) - )), - threshold = ResourceAmount( - resourceRequests = Map("cpu" -> Quantity("1")) - ) - ) - ) - .withStatus( - v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("cpu" -> true) - ), - used = ResourceAmount( - resourceRequests = Map("cpu" -> Quantity("2")) - ) - )) - } - - def dummyInsufficientClusterThrottleFor(p: Pod) = { - v1alpha1 - .ClusterThrottle( - "nospacethrottle", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem(LabelSelector()) - )), - threshold = ResourceAmount( - resourceRequests = Map("cpu" -> Quantity("1")) - ) - ) - ) - } - - val dummyThrottleController = system.actorOf( - Props(new Actor { - override def receive: Receive = { - case CheckThrottleRequest(p) if p.name == "throttled" => - sender() ! Throttled( - p, - Set(dummyActiveThrottleFor(p)), - Set(dummyActiveClusterThrottleFor(p)), - Set(dummyInsufficientThrottleFor(p)), - Set(dummyInsufficientClusterThrottleFor(p)) - ) - case CheckThrottleRequest(p) if p.name == "not-throttled" => - sender() ! NotThrottled(p) - case CheckThrottleRequest(p) if p.name == "timeout" => - // timeout - } - }), - "dummyThrottleController" - ) - - val throttlerRoutes = new Routes(dummyThrottleController, - system.actorOf(ActorWatcher.props(dummyThrottleController)), - Timeout(1 second)).all - - "check_throttle" - { - "should return ExtenderFilterResult without FailedNodes and Errors" in { - - val requestBody = """|{ - | "Pod": { - | "metadata": { - | "name": "not-throttled", - | "namespace": "default" - | } - | }, - | "NodeNames": [ "minikube" ], - | "Nodes": { - | "metadata": {}, - | "items": [ - | { - | "metadata": { - | "name": "minikube" - | } - | } - | ] - | } - |} - |""".stripMargin - - val request = HttpRequest(HttpMethods.POST, - "/check_throttle", - entity = HttpEntity( - MediaTypes.`application/json`, - ByteString(requestBody) - )) - - request ~> throttlerRoutes ~> check { - status shouldBe StatusCodes.OK - responseAs[ExtenderFilterResult] shouldBe ExtenderFilterResult( - nodenames = List("minikube"), - nodes = Option( - ListResource[Node]( - apiVersion = "v1", - kind = "NodeList", - metadata = Option(ListMeta()), - items = List(Node(metadata = ObjectMeta(name = "minikube", namespace = ""))) - )) - ) - } - } - - "should return ExtenderFilterResult with FailedNodes and without Errors" in { - val requestBody = """|{ - | "Pod": { - | "metadata": { - | "name": "throttled", - | "namespace": "default" - | } - | }, - | "NodeNames": [ "minikube" ], - | "Nodes": { - | "metadata": {}, - | "items": [ - | { - | "metadata": { - | "name": "minikube" - | } - | } - | ] - | } - |} - |""".stripMargin - - val request = HttpRequest(HttpMethods.POST, - "/check_throttle", - entity = HttpEntity( - MediaTypes.`application/json`, - ByteString(requestBody) - )) - - request ~> throttlerRoutes ~> check { - status shouldBe StatusCodes.OK - responseAs[ExtenderFilterResult] shouldBe ExtenderFilterResult( - nodenames = List.empty, - nodes = Option( - ListResource[Node]( - apiVersion = "v1", - kind = "NodeList", - metadata = Option(ListMeta()), - items = List.empty - ) - ), - failedNodes = Map( - "minikube" -> "pod (default,throttled) is unschedulable due to throttles[active]=(default,throttle), clusterthrottles[active]=clusterthrottle, throttles[insufficient]=(default,nospacethrottle), clusterthrottles[insufficient]=nospacethrottle"), - error = None - ) - } - } - - "should return ExtenderFilterResult with FailedNodes and Errors" in { - val requestBody = """|{ - | "Pod": { - | "metadata": { - | "name": "timeout", - | "namespace": "default" - | } - | }, - | "NodeNames": [ "minikube" ], - | "Nodes": { - | "metadata": {}, - | "items": [ - | { - | "metadata": { - | "name": "minikube" - | } - | } - | ] - | } - |} - |""".stripMargin - - val request = HttpRequest(HttpMethods.POST, - "/check_throttle", - entity = HttpEntity( - MediaTypes.`application/json`, - ByteString(requestBody) - )) - - val messageHead = - """exception occurred in checking throttles for pod (default,timeout): Ask timed out on""" - - request ~> throttlerRoutes ~> check { - status shouldBe StatusCodes.OK - val result = responseAs[ExtenderFilterResult] - result.nodenames shouldBe List.empty - result.nodes shouldBe Option( - ListResource[Node]( - apiVersion = "v1", - kind = "NodeList", - metadata = Option(ListMeta()), - items = List.empty - ) - ) - result.failedNodes.size shouldBe 1 - result.failedNodes("minikube") should startWith(messageHead) - result.error.get should startWith(messageHead) - } - } - } - - "preempt" - { - "should return ExtenderPreemptionResult with empty victims when throttled" in { - val requestBody = """|{ - | "Pod": { - | "metadata": { - | "name": "throttled", - | "namespace": "default" - | } - | }, - | "NodeNameToVictims": { - | "node1": { - | "Pods": [{ - | "metadata": { - | "name": "pod-rzgq6", - | "namespace": "default", - | "uid": "xxx", - | "labels": { - | "throttle": "t1" - | } - | } - | }], - | "NumPDBViolations": 0 - | } - | } - |} - """.stripMargin - val request = HttpRequest(HttpMethods.POST, - "/preempt_if_not_throttled", - entity = HttpEntity( - MediaTypes.`application/json`, - ByteString(requestBody) - )) - request ~> throttlerRoutes ~> check { - status shouldBe StatusCodes.OK - responseAs[ExtenderPreemptionResult] shouldBe ExtenderPreemptionResult(Map.empty) - } - } - - "should return ExtenderPreemptionResult with passed victims when not-throttled" in { - val requestBody = """|{ - | "Pod": { - | "metadata": { - | "name": "not-throttled", - | "namespace": "default" - | } - | }, - | "NodeNameToVictims": { - | "node1": { - | "Pods": [{ - | "metadata": { - | "name": "pod-rzgq6", - | "uid": "xxx", - | "labels": { - | "throttle": "t1" - | } - | } - | }], - | "NumPDBViolations": 0 - | } - | } - |} - """.stripMargin - val request = HttpRequest(HttpMethods.POST, - "/preempt_if_not_throttled", - entity = HttpEntity( - MediaTypes.`application/json`, - ByteString(requestBody) - )) - request ~> throttlerRoutes ~> check { - status shouldBe StatusCodes.OK - responseAs[ExtenderPreemptionResult] shouldBe ExtenderPreemptionResult( - Map("node1" -> MetaVictims(List(MetaPod("xxx")), 0))) - } - } - "should return InternalServerError when Error" in { - val requestBody = """|{ - | "Pod": { - | "metadata": { - | "name": "timeout", - | "namespace": "default" - | } - | }, - | "NodeNameToVictims": { - | "node1": { - | "Pods": [{ - | "metadata": { - | "name": "pod-rzgq6", - | "uid": "xxx", - | "labels": { - | "throttle": "t1" - | } - | } - | }], - | "NumPDBViolations": 0 - | } - | } - |} - """.stripMargin - val request = HttpRequest(HttpMethods.POST, - "/preempt_if_not_throttled", - entity = HttpEntity( - MediaTypes.`application/json`, - ByteString(requestBody) - )) - request ~> throttlerRoutes ~> check { - status shouldBe StatusCodes.InternalServerError - responseAs[String] shouldBe "{}" - } - } - } -} diff --git a/src/test/scala/com/github/everpeace/k8s/throttler/controller/ClusterThrottleControllerLogicSpec.scala b/src/test/scala/com/github/everpeace/k8s/throttler/controller/ClusterThrottleControllerLogicSpec.scala deleted file mode 100644 index 03a5d1c..0000000 --- a/src/test/scala/com/github/everpeace/k8s/throttler/controller/ClusterThrottleControllerLogicSpec.scala +++ /dev/null @@ -1,1389 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.controller - -import akka.actor.ActorSystem -import akka.testkit.TestKit -import com.github.everpeace.k8s.throttler.KubeThrottleConfig -import com.github.everpeace.k8s.throttler.crd.v1alpha1 -import com.github.everpeace.k8s.throttler.crd.v1alpha1._ -import org.scalatest.{FreeSpecLike, Matchers} -import skuber.LabelSelector.IsEqualRequirement -import skuber.Resource.{Quantity, ResourceList} -import skuber.{Container, LabelSelector, Namespace, ObjectMeta, Pod, Resource} - -class ClusterThrottleControllerLogicSpec - extends TestKit(ActorSystem("ThrottleControllerLogicSpec")) - with FreeSpecLike - with Matchers - with ClusterThrottleControllerLogic { - - implicit val ktConfig = KubeThrottleConfig(system.settings.config) - - def mkNs(labels: Map[String, String] = Map.empty) = - Namespace.from( - ObjectMeta( - name = commonMeta.namespace, - labels = labels - )) - - def mkPod(resourceRequirements: List[Option[Resource.Requirements]]): Pod = { - val containers = resourceRequirements.zipWithIndex.map { - case (rr, index) => - Container( - name = s"ctr$index", - image = "image", - resources = rr - ) - } - val spec = Pod - .Spec( - nodeName = "somewhere", - containers = containers - ) - Pod("dummy", spec).copy( - status = Option(Pod.Status(phase = Some(Pod.Phase.Running))) - ) - } - - def phase(s: Pod.Phase.Phase) = Option(Pod.Status(Option(s))) - - def resourceRequirements(requests: ResourceList, limits: ResourceList = Map()) = - Some(Resource.Requirements(limits = limits, requests = requests)) - - val commonMeta = ObjectMeta( - namespace = "default", - name = "dummy", - labels = Map("key" -> "value") - ) - - val at = java.time.ZonedDateTime.parse("2019-03-01T00:00:00+09:00") - - "ClusterThrottleControllerLogic" - { - "isClusterThrottleAlreadyActiveFor" - { - "should evaluate active when the clusterthrottle's status for some of the 'requests' resource of pod are active" in { - val clthrottle = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - // throttle is defined on "r" and "s" - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2"), "s" -> Quantity("3")) - ) - ) - ) - .withStatus(v1alpha1.ClusterThrottle.Status( - // only "r" is throttled - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> true, "s" -> false) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("3")) - ) - )) - val ns = mkNs() - val podRequestsR = mkPod(List(resourceRequirements(Map("r" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleAlreadyActiveFor(podRequestsR, ns, clthrottle) shouldBe true - - val podRequestsS = mkPod(List(resourceRequirements(Map("s" -> Quantity("3"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleAlreadyActiveFor(podRequestsS, ns, clthrottle) shouldBe false - - val podRequestsT = mkPod(List(resourceRequirements(Map("t" -> Quantity("3"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleAlreadyActiveFor(podRequestsT, ns, clthrottle) shouldBe false - - val podNoLabel = mkPod(List(resourceRequirements(Map("s" -> Quantity("3"))))).copy( - metadata = commonMeta.copy(labels = Map.empty), - ) - isClusterThrottleAlreadyActiveFor(podNoLabel, ns, clthrottle) shouldBe false - } - - "should evaluate active when the clusterthrottle's status.podCounts is active" in { - val clusterthrottle = v1alpha1 - .ClusterThrottle( - "clt1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ) - ) - ) - .withStatus( - v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(true) - )), - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - resourceRequests = Map("r" -> Quantity("3")) - ) - ) - ) - .withName("clt1") - - val ns = mkNs() - - val podRequestsR = mkPod(List(resourceRequirements(Map("r" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleAlreadyActiveFor(podRequestsR, ns, clusterthrottle) shouldBe true - - val podNoLabel = mkPod(List(resourceRequirements(Map("s" -> Quantity("3"))))).copy( - metadata = commonMeta.copy(labels = Map.empty), - ) - isClusterThrottleAlreadyActiveFor(podNoLabel, ns, clusterthrottle) shouldBe false - } - } - "isClusterThrottleInsufficientFor" - { - "should evaluated 'insufficient' when there are no space for some of the 'requests' resource of pods" in { - val clthrottle = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - // throttle is defined on "r" and "s" - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("3"), "s" -> Quantity("2")) - ) - ) - ) - .withStatus(v1alpha1.ClusterThrottle.Status( - // only "r" is throttled - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> false, "s" -> false) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ) - )) - val clthrottleWithCalculated = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - // throttle is defined on "r" and "s" with high values - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("300"), "s" -> Quantity("200")) - ) - ) - ) - .withStatus(v1alpha1.ClusterThrottle.Status( - // only "r" is throttled - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> false, "s" -> false) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ), - calculatedThreshold = Option( - CalculatedThreshold( - ResourceAmount( - // throttle is overridden on "r" and "s" with high values - resourceRequests = Map("r" -> Quantity("3"), "s" -> Quantity("2")) - ), - at - )) - )) - val ns = mkNs() - - val podRequestsR1 = mkPod(List(resourceRequirements(Map("r" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleInsufficientFor(podRequestsR1, ns, clthrottle) shouldBe false - isClusterThrottleInsufficientFor(podRequestsR1, ns, clthrottleWithCalculated) shouldBe false - - val podRequestsR2 = mkPod(List(resourceRequirements(Map("r" -> Quantity("2"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleInsufficientFor(podRequestsR2, ns, clthrottle) shouldBe false - isClusterThrottleInsufficientFor(podRequestsR2, ns, clthrottleWithCalculated) shouldBe false - - val podRequestsR3 = mkPod(List(resourceRequirements(Map("r" -> Quantity("3"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleInsufficientFor(podRequestsR3, ns, clthrottle) shouldBe true - isClusterThrottleInsufficientFor(podRequestsR3, ns, clthrottleWithCalculated) shouldBe true - - val podRequestsS1 = mkPod(List(resourceRequirements(Map("s" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleInsufficientFor(podRequestsS1, ns, clthrottle) shouldBe false - isClusterThrottleInsufficientFor(podRequestsS1, ns, clthrottleWithCalculated) shouldBe false - - val podRequestsS2 = mkPod(List(resourceRequirements(Map("s" -> Quantity("2"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleInsufficientFor(podRequestsS2, ns, clthrottle) shouldBe false - isClusterThrottleInsufficientFor(podRequestsS2, ns, clthrottleWithCalculated) shouldBe false - - val podRequestsS3 = mkPod(List(resourceRequirements(Map("s" -> Quantity("3"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleInsufficientFor(podRequestsS3, ns, clthrottle) shouldBe true - isClusterThrottleInsufficientFor(podRequestsS3, ns, clthrottleWithCalculated) shouldBe true - - val podRequestR1S1 = - mkPod(List(resourceRequirements(Map("r" -> Quantity("1"), "s" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleInsufficientFor(podRequestR1S1, ns, clthrottle) shouldBe false - isClusterThrottleInsufficientFor(podRequestR1S1, ns, clthrottleWithCalculated) shouldBe false - - val podRequestR1S3 = - mkPod(List(resourceRequirements(Map("r" -> Quantity("1"), "s" -> Quantity("3"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleInsufficientFor(podRequestR1S3, ns, clthrottle) shouldBe true - isClusterThrottleInsufficientFor(podRequestR1S3, ns, clthrottleWithCalculated) shouldBe true - - val podRequestR3S1 = - mkPod(List(resourceRequirements(Map("r" -> Quantity("3"), "s" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleInsufficientFor(podRequestR3S1, ns, clthrottle) shouldBe true - isClusterThrottleInsufficientFor(podRequestR3S1, ns, clthrottleWithCalculated) shouldBe true - - val podRequestR3S3 = - mkPod(List(resourceRequirements(Map("r" -> Quantity("3"), "s" -> Quantity("3"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleInsufficientFor(podRequestR3S3, ns, clthrottle) shouldBe true - isClusterThrottleInsufficientFor(podRequestR3S3, ns, clthrottleWithCalculated) shouldBe true - - val podNoRequests = mkPod(List.empty).copy( - metadata = commonMeta, - ) - isClusterThrottleInsufficientFor(podNoRequests, ns, clthrottle) shouldBe false - isClusterThrottleInsufficientFor(podNoRequests, ns, clthrottleWithCalculated) shouldBe false - - val podNoLabel = mkPod(List(resourceRequirements(Map("s" -> Quantity("3"))))).copy( - metadata = commonMeta.copy(labels = Map.empty), - ) - isClusterThrottleInsufficientFor(podNoLabel, ns, clthrottle) shouldBe false - isClusterThrottleInsufficientFor(podNoLabel, ns, clthrottleWithCalculated) shouldBe false - } - - "should evaluate 'insufficient' when there are no space for podCount" in { - val clusterthrottleNoSpace = v1alpha1 - .ClusterThrottle( - "clt1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )) - ) - ) - ) - .withStatus( - v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(true) - )) - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )) - ) - ) - ) - .withName("clt1") - val clusterthrottleOvrriddenNoSpace = v1alpha1 - .ClusterThrottle( - "clt1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(100) - )) - ) - ) - ) - .withStatus( - v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(true) - )) - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )) - ), - calculatedThreshold = Option( - CalculatedThreshold( - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(1) - )) - ), - at - )) - ) - ) - .withName("clt1") - - val ns = mkNs() - val podRequestsR = mkPod(List(resourceRequirements(Map("r" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isClusterThrottleInsufficientFor(podRequestsR, ns, clusterthrottleNoSpace) shouldBe true - isClusterThrottleInsufficientFor(podRequestsR, ns, clusterthrottleOvrriddenNoSpace) shouldBe true - - val podNoLabel = mkPod(List(resourceRequirements(Map("s" -> Quantity("3"))))).copy( - metadata = commonMeta.copy(labels = Map.empty), - ) - isClusterThrottleInsufficientFor(podNoLabel, ns, clusterthrottleNoSpace) shouldBe false - isClusterThrottleInsufficientFor(podNoLabel, ns, clusterthrottleOvrriddenNoSpace) shouldBe false - - val clusterthrottleWithSpace = v1alpha1 - .ClusterThrottle( - "clt1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(2) - )) - ) - ) - ) - .withStatus( - v1alpha1.ClusterThrottle.Status( - // only "r" is throttled - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(false) - )) - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )) - ) - ) - ) - .withName("clt1") - val clusterthrottleOverriddenWithSpace = v1alpha1 - .ClusterThrottle( - "clt1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )) - ) - ) - ) - .withStatus( - v1alpha1.ClusterThrottle.Status( - // only "r" is throttled - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(false) - )) - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )) - ), - calculatedThreshold = Option( - CalculatedThreshold( - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(2) - )) - ), - at - )) - ) - ) - .withName("clt1") - isClusterThrottleInsufficientFor(podRequestsR, ns, clusterthrottleWithSpace) shouldBe false - isClusterThrottleInsufficientFor(podRequestsR, ns, clusterthrottleOverriddenWithSpace) shouldBe false - } - } - "calcNextClusterThrottleStatuses" - { - "should not clusterthrottle when total `requests` of running/scheduled pods can't compare threshold" in { - val basePod = mkPod(List(resourceRequirements(Map("r" -> Quantity("2"))))) - val runningPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val scheduledPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Pending) - ) - - def testOnPod(pod: Pod) = { - val clthrottle = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("s" -> Quantity("3")) - ) - ) - ) - val clthrottleWithActiveOverrides = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("s" -> Quantity("30")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(1), - ResourceAmount( - resourceRequests = Map("s" -> Quantity("3")) - ) - )) - ) - ) - val clthrottleWithoutActiveOverrides = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("s" -> Quantity("3")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.plusDays(1), - end = at.plusDays(2), - ResourceAmount( - resourceRequests = Map("s" -> Quantity("30")) - ) - )) - ) - ) - val nss = Map("" -> mkNs()) - val pods = Set(pod) - val expectedStatus = v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("s" -> false) - ), - used = ResourceAmount(), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = Map("s" -> Quantity("3")) - ), - at)) - ) - - val actual = calcNextClusterThrottleStatuses(Set(clthrottle), pods, nss, at) - actual.size shouldBe 1 - actual.head shouldBe clthrottle.key -> expectedStatus - - val actualWithActiveOverrides = - calcNextClusterThrottleStatuses(Set(clthrottleWithActiveOverrides), pods, nss, at) - actualWithActiveOverrides.size shouldBe 1 - actualWithActiveOverrides.head shouldBe clthrottleWithActiveOverrides.key -> expectedStatus - - val actualWithoutActiveOverrides = - calcNextClusterThrottleStatuses(Set(clthrottleWithoutActiveOverrides), pods, nss, at) - actualWithoutActiveOverrides.size shouldBe 1 - actualWithoutActiveOverrides.head shouldBe clthrottleWithoutActiveOverrides.key -> expectedStatus - } - - testOnPod(runningPod) - testOnPod(scheduledPod) - } - - "should throttle when total `requests` of running/scheduled pods equal to its threshold" in { - val basePod = mkPod(List(resourceRequirements(Map("r" -> Quantity("1"))))) - val runningPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val scheduledPod = basePod.copy( - metadata = commonMeta.copy(name = "dummy2"), - status = phase(Pod.Phase.Running) - ) - - def testOnPods(_pods: Pod*) = { - val clthrottle = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2"), "s" -> Quantity("3")) - ) - ) - ) - val clthrottleWithActiveOverrides = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("20"), "s" -> Quantity("30")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(1), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("2"), "s" -> Quantity("3")) - ) - )) - ) - ) - val clthrottleWithoutActiveOverrides = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2"), "s" -> Quantity("3")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.plusDays(1), - end = at.plusDays(2), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("20"), "s" -> Quantity("30")) - ) - )) - ) - ) - - val ns = mkNs() - val nss = Map(ns.name -> ns) - val pods = _pods.toSet - val expectedStatus = v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> true, "s" -> false) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2")) - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = - Map("r" -> Quantity("2"), "s" -> Quantity("3")) - ), - at)) - ) - - val actual = calcNextClusterThrottleStatuses(Set(clthrottle), pods, nss, at) - actual.size shouldBe 1 - actual.head shouldBe clthrottle.key -> expectedStatus - - val actualWithActiveOverrides = - calcNextClusterThrottleStatuses(Set(clthrottleWithActiveOverrides), pods, nss, at) - actualWithActiveOverrides.size shouldBe 1 - actualWithActiveOverrides.head shouldBe clthrottleWithActiveOverrides.key -> expectedStatus - - val actualWithoutActiveOverrides = - calcNextClusterThrottleStatuses(Set(clthrottleWithoutActiveOverrides), pods, nss, at) - actualWithoutActiveOverrides.size shouldBe 1 - actualWithoutActiveOverrides.head shouldBe clthrottleWithoutActiveOverrides.key -> expectedStatus - } - - testOnPods(runningPod, scheduledPod) - } - - "should throttle when total 'requests' of running/scheduled pods exceeds threshold" in { - val basePod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )) - val runningPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val scheduledPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Pending) - ) - - def testOnPod(pod: Pod) = { - val clthrottle = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ) - ) - ) - val clthrottleWithActiveOverrides = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("10")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(1), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ) - )) - ) - ) - val clthrottleWithoutActiveOverrides = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.plusDays(1), - end = at.plusDays(2), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("10")) - ) - )) - ) - ) - - val ns = mkNs() - val nss = Map(ns.name -> ns) - val pods = Set(pod) - val expectedStatus = v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> true) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2")) - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ), - at)) - ) - - val actual = calcNextClusterThrottleStatuses(Set(clthrottle), pods, nss, at) - actual.size shouldBe 1 - actual.head shouldBe clthrottle.key -> expectedStatus - - val actualWithActiveOverrides = - calcNextClusterThrottleStatuses(Set(clthrottleWithActiveOverrides), pods, nss, at) - actualWithActiveOverrides.size shouldBe 1 - actualWithActiveOverrides.head shouldBe clthrottleWithActiveOverrides.key -> expectedStatus - - val actualWithoutActiveOverrides = - calcNextClusterThrottleStatuses(Set(clthrottleWithoutActiveOverrides), pods, nss, at) - actualWithoutActiveOverrides.size shouldBe 1 - actualWithoutActiveOverrides.head shouldBe clthrottleWithoutActiveOverrides.key -> expectedStatus - } - - testOnPod(runningPod) - testOnPod(scheduledPod) - } - - "should not throttled when total 'requests' of running/scheduled pods fall below threshold" in { - val basePod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("5"))) - )) - val runningPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Succeeded) - ) - val scheduledPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Pending) - ) - - def testOnPod(pod: Pod) = { - val clthrottle = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("3")) - ) - ) - ) - val clthrottleWithActiveOverrides = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("30")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(1), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("3")) - ) - )) - ) - ) - val clthrottleWithoutActiveOverrides = v1alpha1 - .ClusterThrottle( - "ct1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("3")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.plusDays(1), - end = at.plusDays(2), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("30")) - ) - )) - ) - ) - - val nss = Map("" -> mkNs()) - val pods = Set(pod) - val expectedStatus = v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> false) - ), - used = ResourceAmount( - resourceRequests = Map() - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = Map("r" -> Quantity("3")) - ), - at)) - ) - - val actual = calcNextClusterThrottleStatuses(Set(clthrottle), pods, nss, at) - actual.size shouldBe 1 - actual.head shouldBe clthrottle.key -> expectedStatus - - val actualWithActiveOverrides = - calcNextClusterThrottleStatuses(Set(clthrottleWithActiveOverrides), pods, nss, at) - actualWithActiveOverrides.size shouldBe 1 - actualWithActiveOverrides.head shouldBe clthrottleWithActiveOverrides.key -> expectedStatus - - val actualWithoutActiveOverrides = - calcNextClusterThrottleStatuses(Set(clthrottleWithoutActiveOverrides), pods, nss, at) - actualWithoutActiveOverrides.size shouldBe 1 - actualWithoutActiveOverrides.head shouldBe clthrottleWithoutActiveOverrides.key -> expectedStatus - } - - testOnPod(runningPod) - testOnPod(scheduledPod) - } - - "should throttle when total number of running/scheduled pods exceeds threshold" in { - val basePod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )) - val runningPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val scheduledPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Pending) - ) - - def testOnPod(pod: Pod) = { - - val clthrottle = v1alpha1 - .ClusterThrottle( - "clt1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ) - ) - ) - .withName("clt1") - val clthrottleWithActiveOverrides = v1alpha1 - .ClusterThrottle( - "clt1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(10) - )), - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(1), - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(1) - )) - ) - )) - ) - ) - .withName("clt1") - val clthrottleWithoutActiveOverrides = v1alpha1 - .ClusterThrottle( - "clt1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.plusDays(1), - end = at.plusDays(2), - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(10) - )) - ) - )) - ) - ) - .withName("clt1") - - val ns = mkNs() - val nss = Map(ns.name -> ns) - val pods = Set(pod) - val expectedStatus = v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(true) - )), - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ), - at)) - ) - - val actual = calcNextClusterThrottleStatuses(Set(clthrottle), pods, nss, at) - actual.size shouldBe 1 - actual.head shouldBe clthrottle.key -> expectedStatus - - val actualWithActiveOverrides = - calcNextClusterThrottleStatuses(Set(clthrottleWithActiveOverrides), pods, nss, at) - actualWithActiveOverrides.size shouldBe 1 - actualWithActiveOverrides.head shouldBe clthrottleWithActiveOverrides.key -> expectedStatus - - val actualWithoutActiveOverrides = - calcNextClusterThrottleStatuses(Set(clthrottleWithoutActiveOverrides), pods, nss, at) - actualWithoutActiveOverrides.size shouldBe 1 - actualWithoutActiveOverrides.head shouldBe clthrottleWithoutActiveOverrides.key -> expectedStatus - } - - testOnPod(runningPod) - testOnPod(scheduledPod) - } - - "should not throttle when total number of running/scheduled pods fall below threshold" in { - val basePod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )) - val runningPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val scheduledPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - def testOnPod(pod: Pod) = { - - val clthrottle = v1alpha1 - .ClusterThrottle( - "clt1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(2) - )), - ) - ) - ) - .withName("clt1") - val clthrottleWithActiveOverrides = v1alpha1 - .ClusterThrottle( - "clt1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(20) - )), - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(1), - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(2) - )) - ) - )) - ) - ) - .withName("clt1") - val clthrottleWithoutActiveOverrides = v1alpha1 - .ClusterThrottle( - "clt1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(2) - )) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.plusDays(1), - end = at.plusDays(2), - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(20) - )) - ) - )) - ) - ) - .withName("clt1") - - val ns = mkNs() - val nss = Map(ns.name -> ns) - val pods = Set(pod) - val expectedStatus = v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(false) - )), - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(2) - )), - ), - at)) - ) - - val actual = calcNextClusterThrottleStatuses(Set(clthrottle), pods, nss, at) - actual.size shouldBe 1 - actual.head shouldBe clthrottle.key -> expectedStatus - - val actualWithActiveOverrides = - calcNextClusterThrottleStatuses(Set(clthrottleWithActiveOverrides), pods, nss, at) - actualWithActiveOverrides.size shouldBe 1 - actualWithActiveOverrides.head shouldBe clthrottleWithActiveOverrides.key -> expectedStatus - - val actualWithoutActiveOverrides = - calcNextClusterThrottleStatuses(Set(clthrottleWithoutActiveOverrides), pods, nss, at) - actualWithoutActiveOverrides.size shouldBe 1 - actualWithoutActiveOverrides.head shouldBe clthrottleWithoutActiveOverrides.key -> expectedStatus - } - - testOnPod(runningPod) - testOnPod(scheduledPod) - } - - "should generate calculatedThreshold with messages when parse failure exists" in { - val pod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )).copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val clthrottle = v1alpha1 - .ClusterThrottle( - "clt1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - "malformat", - "malformat", - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(2) - )) - ) - )) - ) - ) - .withName("clt1") - - val ns = mkNs() - val nss = Map(ns.name -> ns) - val pods = Set(pod) - val expectedStatus = v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(true) - )), - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ), - calculatedThreshold = Option( - CalculatedThreshold( - ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ), - at, - List("[0]: begin: Text 'malformat' could not be parsed at index 0, end: Text 'malformat' could not be parsed at index 0") - )) - ) - val actual = - calcNextClusterThrottleStatuses(Set(clthrottle), pods, nss, at) - actual.size shouldBe 1 - actual.head shouldBe clthrottle.key -> expectedStatus - } - - "should return empty when next status is the same except for calculatedAt" in { - val pod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )).copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val clthrottle = v1alpha1 - .ClusterThrottle( - "t1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ) - ) - ) - .withName("t1") - - val ns = mkNs() - val nss = Map(ns.name -> ns) - val pods = Set(pod) - - val status = v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> true) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2")) - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ), - at)) - ) - - val actual = calcNextClusterThrottleStatuses(Set(clthrottle.withStatus(status)), - pods, - nss, - at.plusMinutes(1)) - actual shouldBe empty - } - - "should return new status when next status is the same except for calculatedAt but observed is too old" in { - val pod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )).copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val clthrottle = v1alpha1 - .ClusterThrottle( - "t1", - v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ) - ) - ) - .withName("t1") - - val ns = mkNs() - val nss = Map(ns.name -> ns) - val pods = Set(pod) - - val status = v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> true) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2")) - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ), - at)) - ) - - val actual = calcNextClusterThrottleStatuses(Set(clthrottle.withStatus(status)), - pods, - nss, - at.plusMinutes(16)) - actual.size shouldBe 1 - actual.head shouldBe clthrottle.key -> status.copy(calculatedThreshold = - status.calculatedThreshold.map(_.copy(calculatedAt = at.plusMinutes(16)))) - } - } - } -} diff --git a/src/test/scala/com/github/everpeace/k8s/throttler/controller/ThrottleControllerLogicSpec.scala b/src/test/scala/com/github/everpeace/k8s/throttler/controller/ThrottleControllerLogicSpec.scala deleted file mode 100644 index fc0b459..0000000 --- a/src/test/scala/com/github/everpeace/k8s/throttler/controller/ThrottleControllerLogicSpec.scala +++ /dev/null @@ -1,1362 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.controller - -import akka.actor.ActorSystem -import akka.testkit.TestKit -import com.github.everpeace.k8s.throttler.KubeThrottleConfig -import com.github.everpeace.k8s.throttler.crd.v1alpha1 -import com.github.everpeace.k8s.throttler.crd.v1alpha1._ -import org.scalatest.{FreeSpecLike, Matchers} -import skuber.LabelSelector.IsEqualRequirement -import skuber.Resource.{Quantity, ResourceList} -import skuber.{Container, LabelSelector, ObjectMeta, Pod, Resource} - -class ThrottleControllerLogicSpec - extends TestKit(ActorSystem("ThrottleControllerLogicSpec")) - with FreeSpecLike - with Matchers - with ThrottleControllerLogic { - - implicit val ktConfig = KubeThrottleConfig(system.settings.config) - - def mkPod(resourceRequirements: List[Option[Resource.Requirements]]): Pod = { - val containers = resourceRequirements.zipWithIndex.map { - case (rr, index) => - Container( - name = s"ctr$index", - image = "image", - resources = rr - ) - } - val spec = Pod.Spec( - nodeName = "somewhere", - containers = containers - ) - Pod("dummy", spec).copy( - status = phase(Pod.Phase.Running) - ) - } - - def phase(s: Pod.Phase.Phase) = Option(Pod.Status(Option(s))) - - def resourceRequirements(requests: ResourceList, limits: ResourceList = Map()) = - Some(Resource.Requirements(limits = limits, requests = requests)) - - val commonMeta = ObjectMeta( - namespace = "default", - name = "dummy", - labels = Map("key" -> "value") - ) - - val at = java.time.ZonedDateTime.parse("2019-03-01T00:00:00+09:00") - - "ThrottleControllerLogic" - { - "isThrottleAlreadyActiveFor" - { - "should evaluate active when the throttle's status for all the 'requests' resource of pod are active" in { - val throttle = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - // throttle is defined on "r" and "s" - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2"), "s" -> Quantity("3")) - ) - ) - ) - .withStatus( - v1alpha1.Throttle.Status( - // only "r" is throttled - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> true, "s" -> false) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("3")) - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = - Map("r" -> Quantity("2"), "s" -> Quantity("3")) - ), - at)) - ) - ) - .withNamespace("default") - .withName("t1") - - val podRequestsR = mkPod(List(resourceRequirements(Map("r" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isThrottleAlreadyActiveFor(podRequestsR, throttle) shouldBe true - - val podRequestsS = mkPod(List(resourceRequirements(Map("s" -> Quantity("3"))))).copy( - metadata = commonMeta, - ) - isThrottleAlreadyActiveFor(podRequestsS, throttle) shouldBe false - - val podRequestsT = mkPod(List(resourceRequirements(Map("t" -> Quantity("3"))))).copy( - metadata = commonMeta, - ) - isThrottleAlreadyActiveFor(podRequestsT, throttle) shouldBe false - - val podNoLabel = mkPod(List(resourceRequirements(Map("s" -> Quantity("3"))))).copy( - metadata = commonMeta.copy(labels = Map.empty), - ) - isThrottleAlreadyActiveFor(podNoLabel, throttle) shouldBe false - } - - "should evaluate active when the throttle's status.podCounts is active" in { - val throttle = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )) - ) - ) - ) - .withStatus( - v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(true) - )), - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - resourceRequests = Map("r" -> Quantity("3")) - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(1) - )) - ), - at)) - ) - ) - .withNamespace("default") - .withName("t1") - - val podRequestsR = mkPod(List(resourceRequirements(Map("r" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isThrottleAlreadyActiveFor(podRequestsR, throttle) shouldBe true - - val podNoLabel = mkPod(List(resourceRequirements(Map("s" -> Quantity("3"))))).copy( - metadata = commonMeta.copy(labels = Map.empty), - ) - isThrottleAlreadyActiveFor(podNoLabel, throttle) shouldBe false - } - } - "isThrottleInsufficientFor" - { - "should evaluate 'insufficient' when there are no space for some of the 'requests' resource of pods" in { - val throttle = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector( - List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - // throttle is defined on "r" and "s" - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("3"), "s" -> Quantity("2")) - ) - ) - ) - .withStatus(v1alpha1.Throttle.Status( - // only "r" is throttled - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> false, "s" -> false) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ), - calculatedThreshold = None - )) - .withNamespace("default") - - val throttleWithCalculatedThreshold = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector( - List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - // throttle is defined on "r" and "s" with very high value - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("300"), "s" -> Quantity("200")) - ) - ) - ) - .withStatus(v1alpha1.Throttle.Status( - // only "r" is throttled - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> false, "s" -> false) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ), - calculatedThreshold = Option( - v1alpha1.CalculatedThreshold( - ResourceAmount( - // throttle is overridden on "r" and "s" with normal value - resourceRequests = Map("r" -> Quantity("3"), "s" -> Quantity("2")) - ), - at - )) - )) - .withNamespace("default") - - val podRequestsR1 = mkPod(List(resourceRequirements(Map("r" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isThrottleInsufficientFor(podRequestsR1, throttle) shouldBe false - isThrottleInsufficientFor(podRequestsR1, throttleWithCalculatedThreshold) shouldBe false - - val podRequestsR2 = mkPod(List(resourceRequirements(Map("r" -> Quantity("2"))))).copy( - metadata = commonMeta, - ) - isThrottleInsufficientFor(podRequestsR2, throttle) shouldBe false - isThrottleInsufficientFor(podRequestsR2, throttleWithCalculatedThreshold) shouldBe false - - val podRequestsR3 = mkPod(List(resourceRequirements(Map("r" -> Quantity("3"))))).copy( - metadata = commonMeta, - ) - isThrottleInsufficientFor(podRequestsR3, throttle) shouldBe true - isThrottleInsufficientFor(podRequestsR3, throttleWithCalculatedThreshold) shouldBe true - - val podRequestsS1 = mkPod(List(resourceRequirements(Map("s" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isThrottleInsufficientFor(podRequestsS1, throttle) shouldBe false - isThrottleInsufficientFor(podRequestsS1, throttleWithCalculatedThreshold) shouldBe false - - val podRequestsS2 = mkPod(List(resourceRequirements(Map("s" -> Quantity("2"))))).copy( - metadata = commonMeta, - ) - isThrottleInsufficientFor(podRequestsS2, throttle) shouldBe false - isThrottleInsufficientFor(podRequestsS2, throttleWithCalculatedThreshold) shouldBe false - - val podRequestsS3 = mkPod(List(resourceRequirements(Map("s" -> Quantity("3"))))).copy( - metadata = commonMeta, - ) - isThrottleInsufficientFor(podRequestsS3, throttle) shouldBe true - isThrottleInsufficientFor(podRequestsS3, throttleWithCalculatedThreshold) shouldBe true - - val podRequestR1S1 = - mkPod(List(resourceRequirements(Map("r" -> Quantity("1"), "s" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isThrottleInsufficientFor(podRequestR1S1, throttle) shouldBe false - isThrottleInsufficientFor(podRequestR1S1, throttleWithCalculatedThreshold) shouldBe false - - val podRequestR1S3 = - mkPod(List(resourceRequirements(Map("r" -> Quantity("1"), "s" -> Quantity("3"))))).copy( - metadata = commonMeta, - ) - isThrottleInsufficientFor(podRequestR1S3, throttle) shouldBe true - isThrottleInsufficientFor(podRequestR1S3, throttleWithCalculatedThreshold) shouldBe true - - val podRequestR3S1 = - mkPod(List(resourceRequirements(Map("r" -> Quantity("3"), "s" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isThrottleInsufficientFor(podRequestR3S1, throttle) shouldBe true - isThrottleInsufficientFor(podRequestR3S1, throttleWithCalculatedThreshold) shouldBe true - - val podRequestR3S3 = - mkPod(List(resourceRequirements(Map("r" -> Quantity("3"), "s" -> Quantity("3"))))).copy( - metadata = commonMeta, - ) - isThrottleInsufficientFor(podRequestR3S3, throttle) shouldBe true - isThrottleInsufficientFor(podRequestR3S3, throttleWithCalculatedThreshold) shouldBe true - - val podNoRequests = mkPod(List.empty).copy( - metadata = commonMeta, - ) - isThrottleInsufficientFor(podNoRequests, throttle) shouldBe false - isThrottleInsufficientFor(podNoRequests, throttleWithCalculatedThreshold) shouldBe false - - val podNoLabel = mkPod(List(resourceRequirements(Map("s" -> Quantity("3"))))).copy( - metadata = commonMeta.copy(labels = Map.empty), - ) - isThrottleInsufficientFor(podNoLabel, throttle) shouldBe false - isThrottleInsufficientFor(podNoLabel, throttleWithCalculatedThreshold) shouldBe false - } - - "should evaluate 'insufficient' when there are no space for podCount" in { - val throttleNoSpace = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )) - ) - ) - ) - .withStatus( - v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(true) - )) - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )) - ), - calculatedThreshold = None - ) - ) - .withNamespace("default") - .withName("t1") - - val throttleNoSpaceOverridden = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(100) - )) - ) - ) - ) - .withStatus( - v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(true) - )) - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )) - ), - calculatedThreshold = Option( - CalculatedThreshold( - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(1) - )) - ), - at - )) - ) - ) - .withNamespace("default") - .withName("t1") - val podRequestsR = mkPod(List(resourceRequirements(Map("r" -> Quantity("1"))))).copy( - metadata = commonMeta, - ) - isThrottleInsufficientFor(podRequestsR, throttleNoSpace) shouldBe true - isThrottleInsufficientFor(podRequestsR, throttleNoSpaceOverridden) shouldBe true - - val podNoLabel = mkPod(List(resourceRequirements(Map("s" -> Quantity("3"))))).copy( - metadata = commonMeta.copy(labels = Map.empty), - ) - isThrottleInsufficientFor(podNoLabel, throttleNoSpace) shouldBe false - isThrottleInsufficientFor(podNoLabel, throttleNoSpaceOverridden) shouldBe false - - val throttleWithSpace = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(2) - ))) - ) - ) - .withStatus( - v1alpha1.Throttle.Status( - // only "r" is throttled - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(false) - )) - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ), - calculatedThreshold = None - ) - ) - .withNamespace("default") - .withName("t1") - - val throttleWithSpaceOverridden = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(1) - ))) - ) - ) - .withStatus( - v1alpha1.Throttle.Status( - // only "r" is throttled - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(false) - )) - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ), - calculatedThreshold = Option( - CalculatedThreshold( - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(2) - )) - ), - at - )) - ) - ) - .withNamespace("default") - .withName("t1") - isThrottleInsufficientFor(podRequestsR, throttleWithSpace) shouldBe false - isThrottleInsufficientFor(podRequestsR, throttleWithSpaceOverridden) shouldBe false - } - } - "calcNextThrottleStatuses" - { - "should not throttle when total `requests` of running/scheduled pods can't compare threshold" in { - val basePod = mkPod(List(resourceRequirements(Map("r" -> Quantity("2"))))) - val runningPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val scheduledPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Pending) - ) - def testOnPod(pod: Pod) = { - val throttle = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("s" -> Quantity("3")) - ) - ) - ) - .withNamespace("default") - .withName("t1") - - val throttleWithActiveOverrides = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("s" -> Quantity("1")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(1), - threshold = ResourceAmount( - resourceRequests = Map("s" -> Quantity("3")) - ) - )) - ) - ) - .withNamespace("default") - .withName("t1") - - val throttleWithoutActiveOverrides = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("s" -> Quantity("3")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.plusDays(1), - end = at.plusDays(2), - threshold = ResourceAmount( - resourceRequests = Map("s" -> Quantity("1")) - ) - )) - ) - ) - .withNamespace("default") - .withName("t1") - - val podsInNs = Set(pod) - val expectedStatus = v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("s" -> false) - ), - used = ResourceAmount(), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = Map("s" -> Quantity("3")) - ), - at)) - ) - - val actual = calcNextThrottleStatuses(Set(throttle), podsInNs, at) - actual.size shouldBe 1 - actual.head shouldBe throttle.key -> expectedStatus - - val actualWithActiveOverride = - calcNextThrottleStatuses(Set(throttleWithActiveOverrides), podsInNs, at) - actualWithActiveOverride.size shouldBe 1 - actualWithActiveOverride.head shouldBe throttleWithActiveOverrides.key -> expectedStatus - - val actualWithoutActiveOverride = - calcNextThrottleStatuses(Set(throttleWithoutActiveOverrides), podsInNs, at) - actualWithoutActiveOverride.size shouldBe 1 - actualWithoutActiveOverride.head shouldBe throttleWithoutActiveOverrides.key -> expectedStatus - } - - testOnPod(runningPod) - testOnPod(scheduledPod) - } - - "should throttle when total `requests` of running/scheduled pods equal to its threshold" in { - val basePod = mkPod(List(resourceRequirements(Map("r" -> Quantity("1"))))) - val runningPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val scheduledPod = basePod.copy( - metadata = commonMeta.copy(name = "dummy2"), - status = phase(Pod.Phase.Pending) - ) - - def testOnPods(pods: Pod*) = { - val throttle = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2"), "s" -> Quantity("3")) - ) - ) - ) - .withNamespace("default") - .withName("t1") - - val throttleWithActiveOverrides = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("200"), "s" -> Quantity("300")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.minusDays(2), - end = at.plusDays(1), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("2"), "s" -> Quantity("3")) - ) - ), - TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(2), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("20"), "s" -> Quantity("30")) - ) - ) - ) - ) - ) - .withNamespace("default") - .withName("t1") - - val throttleWithoutActiveOverrides = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2"), "s" -> Quantity("3")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.plusDays(1), - end = at.plusDays(2), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("20"), "s" -> Quantity("30")) - ) - )) - ) - ) - .withNamespace("default") - .withName("t1") - - val podsInNs = pods.toSet - val expectedStatus = v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> true, "s" -> false) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2")) - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = - Map("r" -> Quantity("2"), "s" -> Quantity("3")) - ), - at)) - ) - - val actual = calcNextThrottleStatuses(Set(throttle), podsInNs, at) - actual.size shouldBe 1 - actual.head shouldBe throttle.key -> expectedStatus - - val actualWithActiveOverrides = - calcNextThrottleStatuses(Set(throttleWithActiveOverrides), podsInNs, at) - actualWithActiveOverrides.size shouldBe 1 - actualWithActiveOverrides.head shouldBe throttleWithActiveOverrides.key -> expectedStatus - - val actualWithoutActiveOverrides = - calcNextThrottleStatuses(Set(throttleWithoutActiveOverrides), podsInNs, at) - actualWithoutActiveOverrides.size shouldBe 1 - actualWithoutActiveOverrides.head shouldBe throttleWithoutActiveOverrides.key -> expectedStatus - } - - testOnPods(runningPod, scheduledPod) - } - - "should throttle when total 'requests' of running/scheduled pods exceeds threshold" in { - val basePod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )) - val runningPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val scheduledPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Pending) - ) - - def testOnPod(pod: Pod) = { - val throttle = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ) - ) - ) - .withNamespace("default") - .withName("t1") - val throttleWithActiveOverrides = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("100")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(1), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ) - )) - ) - ) - .withNamespace("default") - .withName("t1") - val throttleWithoutActiveOverrides = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.plusDays(1), - end = at.plusDays(2), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ) - )) - ) - ) - .withNamespace("default") - .withName("t1") - - val podsInNs = Set(pod) - val expectedStatus = v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> true) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2")) - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ), - at)) - ) - - val actual = calcNextThrottleStatuses(Set(throttle), podsInNs, at) - actual.size shouldBe 1 - actual.head shouldBe throttle.key -> expectedStatus - - val actualWithActiveOverrides = - calcNextThrottleStatuses(Set(throttleWithActiveOverrides), podsInNs, at) - actualWithActiveOverrides.size shouldBe 1 - actualWithActiveOverrides.head shouldBe throttleWithActiveOverrides.key -> expectedStatus - - val actualWithougActiveOverrides = - calcNextThrottleStatuses(Set(throttleWithoutActiveOverrides), podsInNs, at) - actualWithougActiveOverrides.size shouldBe 1 - actualWithougActiveOverrides.head shouldBe throttleWithoutActiveOverrides.key -> expectedStatus - } - - testOnPod(runningPod) - testOnPod(scheduledPod) - } - - "should not throttled when total 'requests' of running/scheduled pods fall below threshold" in { - val basePod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )) - val runningPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val scheduledPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Pending) - ) - - def testOnPod(pod: Pod) = { - val throttle = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("3")) - ) - ) - ) - .withNamespace("default") - .withName("t1") - val throttleWithActiveOverrides = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(1), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("3")) - ) - )) - ) - ) - .withNamespace("default") - .withName("t1") - val throttleWithoutActiveOverrides = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("3")) - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.plusDays(1), - end = at.plusDays(2), - ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ) - )) - ) - ) - .withNamespace("default") - .withName("t1") - - val podsInNs = Set(pod) - val expectedStatus = v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> false) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2")) - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = Map("r" -> Quantity("3")) - ), - at)) - ) - - val actual = calcNextThrottleStatuses(Set(throttle), podsInNs, at) - actual.size shouldBe 1 - actual.head shouldBe throttle.key -> expectedStatus - - val actualWithActiveOverrides = - calcNextThrottleStatuses(Set(throttleWithActiveOverrides), podsInNs, at) - actualWithActiveOverrides.size shouldBe 1 - actualWithActiveOverrides.head shouldBe throttleWithActiveOverrides.key -> expectedStatus - - val actualWithougActiveOverrides = - calcNextThrottleStatuses(Set(throttleWithoutActiveOverrides), podsInNs, at) - actualWithougActiveOverrides.size shouldBe 1 - actualWithougActiveOverrides.head shouldBe throttleWithoutActiveOverrides.key -> expectedStatus - } - - testOnPod(runningPod) - testOnPod(scheduledPod) - } - - "should throttle when total number of running pods exceeds threshold" in { - val basePod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )) - - val runningPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - - val scheduledPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Pending) - ) - def testOnPod(pod: Pod) = { - val throttle = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ) - ) - ) - .withNamespace("default") - .withName("t1") - val throttleWithActiveOverrides = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(100) - )), - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(1), - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(1) - )) - ) - )) - ) - ) - .withNamespace("default") - .withName("t1") - val throttleWithoutActiveOverrides = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.plusDays(1), - end = at.plusDays(2), - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(100) - )) - ) - )) - ) - ) - .withNamespace("default") - .withName("t1") - - val podsInNs = Set(pod) - val expectedStatus = v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(true) - )), - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )), - ), - at)) - ) - - val actual = calcNextThrottleStatuses(Set(throttle), podsInNs, at) - actual.size shouldBe 1 - actual.head shouldBe throttle.key -> expectedStatus - - val actualWithActiveOverrides = - calcNextThrottleStatuses(Set(throttleWithActiveOverrides), podsInNs, at) - actualWithActiveOverrides.size shouldBe 1 - actualWithActiveOverrides.head shouldBe throttleWithActiveOverrides.key -> expectedStatus - - val actualWithoutActiveOverrides = - calcNextThrottleStatuses(Set(throttleWithoutActiveOverrides), podsInNs, at) - actualWithoutActiveOverrides.size shouldBe 1 - actualWithoutActiveOverrides.head shouldBe throttleWithoutActiveOverrides.key -> expectedStatus - } - - testOnPod(runningPod) - testOnPod(scheduledPod) - } - - "should not throttle when total number of running/scheduled pods fall below threshold" in { - val basePod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )) - val runningPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val scheduledPod = basePod.copy( - metadata = commonMeta, - status = phase(Pod.Phase.Pending) - ) - - def testOnPod(pod: Pod) = { - val throttle = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(2) - ))) - ) - ) - .withNamespace("default") - .withName("t1") - val throttleWithActiveOverrides = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(200) - ))), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(1), - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(2) - )) - ) - )) - ) - ) - .withNamespace("default") - .withName("t1") - val throttleWithoutActiveOverrides = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(2) - ))), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride(begin = at.plusDays(1), - end = at.plusDays(2), - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(200) - )) - ))) - ) - ) - .withNamespace("default") - .withName("t1") - - val podsInNs = Set(pod) - val expectedStatus = v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(false) - )) - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )) - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(2) - )) - ), - at)) - ) - - val actual = calcNextThrottleStatuses(Set(throttle), podsInNs, at) - actual.size shouldBe 1 - actual.head shouldBe throttle.key -> expectedStatus - - val actualWithActiveOverrides = - calcNextThrottleStatuses(Set(throttleWithActiveOverrides), podsInNs, at) - actualWithActiveOverrides.size shouldBe 1 - actualWithActiveOverrides.head shouldBe throttleWithActiveOverrides.key -> expectedStatus - - val actualWithoutActiveOverrides = - calcNextThrottleStatuses(Set(throttleWithoutActiveOverrides), podsInNs, at) - actualWithoutActiveOverrides.size shouldBe 1 - actualWithoutActiveOverrides.head shouldBe throttleWithoutActiveOverrides.key -> expectedStatus - } - testOnPod(runningPod) - testOnPod(scheduledPod) - } - - "should generate calculatedThreshold with messages when parse failure exists" in { - val pod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )).copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val throttle = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector( - List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(2) - ))), - temporaryThresholdOverrides = List( - TemporaryThresholdOverride( - "malformat", - "malformat", - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(3) - )) - ) - ) - ) - ) - ) - .withNamespace("default") - .withName("t1") - - val podsInNs = Set(pod) - val expectedStatus = v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option( - IsResourceCountThrottled( - pod = Option(false) - )) - ), - used = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(1) - )) - ), - calculatedThreshold = Option( - CalculatedThreshold( - ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(2) - )) - ), - at, - List("[0]: begin: Text 'malformat' could not be parsed at index 0, end: Text 'malformat' could not be parsed at index 0") - )) - ) - - val actual = calcNextThrottleStatuses(Set(throttle), podsInNs, at) - actual.size shouldBe 1 - actual.head shouldBe throttle.key -> expectedStatus - } - - "should return empty when next status is the same except for calculatedAt" in { - val pod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )).copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val throttle = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector( - List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ) - ) - ) - .withNamespace("default") - .withName("t1") - - val podsInNs = Set(pod) - val status = v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> true) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2")) - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ), - at)) - ) - - val actual = - calcNextThrottleStatuses(Set(throttle.withStatus(status)), podsInNs, at.plusMinutes(1)) - actual shouldBe empty - } - - "should return new status when next status is the same except for calculatedAt but observed is too old" in { - val pod = mkPod( - List( - resourceRequirements(Map("r" -> Quantity("2"))) - )).copy( - metadata = commonMeta, - status = phase(Pod.Phase.Running) - ) - val throttle = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector( - List( - v1alpha1.Throttle.SelectorItem(LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ) - ) - ) - .withNamespace("default") - .withName("t1") - - val podsInNs = Set(pod) - val status = v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceRequests = Map("r" -> true) - ), - used = ResourceAmount( - resourceRequests = Map("r" -> Quantity("2")) - ), - calculatedThreshold = Option( - CalculatedThreshold(ResourceAmount( - resourceRequests = Map("r" -> Quantity("1")) - ), - at)) - ) - - val actual = - calcNextThrottleStatuses(Set(throttle.withStatus(status)), podsInNs, at.plusMinutes(16)) - actual.size shouldBe 1 - actual.head shouldBe throttle.key -> status.copy(calculatedThreshold = - status.calculatedThreshold.map(_.copy(calculatedAt = at.plusMinutes(16)))) - } - } - } -} diff --git a/src/test/scala/com/github/everpeace/k8s/throttler/crd/AbstractThrottleSyntaxSpec.scala b/src/test/scala/com/github/everpeace/k8s/throttler/crd/AbstractThrottleSyntaxSpec.scala deleted file mode 100644 index 2f3d193..0000000 --- a/src/test/scala/com/github/everpeace/k8s/throttler/crd/AbstractThrottleSyntaxSpec.scala +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd - -import java.time.ZonedDateTime -import java.time.temporal.ChronoUnit - -import com.github.everpeace.k8s.throttler.crd.v1alpha1.ResourceAmount -import org.scalatest.{Matchers, WordSpec} - -class AbstractThrottleSyntaxSpec extends WordSpec with Matchers { - import v1alpha1.Implicits._ - - def st(calculatedAt: Option[skuber.Timestamp]) = - Option( - v1alpha1.Throttle.Status( - throttled = v1alpha1.IsResourceAmountThrottled(), - used = v1alpha1.ResourceAmount(), - calculatedThreshold = calculatedAt.map( - at => - v1alpha1.CalculatedThreshold( - calculatedAt = at, - threshold = ResourceAmount(), - messages = List() - )) - )) - - def ovr(end: skuber.Timestamp): List[v1alpha1.TemporaryThresholdOverride] = List( - v1alpha1.TemporaryThresholdOverride( - begin = end.minus(1, ChronoUnit.DAYS), - end = end, - threshold = ResourceAmount() - ) - ) - - def thr( - overrides: List[v1alpha1.TemporaryThresholdOverride] = List.empty, - status: Option[v1alpha1.Throttle.Status] = None - ): v1alpha1.Throttle = { - val t = v1alpha1 - .Throttle( - "t1", - v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = v1alpha1.Throttle.Selector(List()), - threshold = ResourceAmount(resourceRequests = Map()), - temporaryThresholdOverrides = overrides - ) - ) - .withNamespace("default") - .withName("t1") - - status.map(s => t.withStatus(s)).getOrElse(t) - } - - "hasTemporaryOverridesToReconcile" should { - "return false if no temporaryOverrides and no status" in { - val t = thr() - t.hasTemporaryOverridesToReconcile shouldBe false - } - - "return false if no temporaryOverrides and some status without calculatedAt" in { - val t = thr(status = st(None)) - t.hasTemporaryOverridesToReconcile shouldBe false - } - - "return false if no temporaryOverrides and some status and some calculatedAt " in { - val t = thr(status = st(Option(ZonedDateTime.now()))) - t.hasTemporaryOverridesToReconcile shouldBe false - } - - "return true if some temporaryOverrides and no status" in { - val t = thr(overrides = ovr(ZonedDateTime.now())) - t.hasTemporaryOverridesToReconcile shouldBe true - } - - "return true if some temporaryOverrides and some status without calculatedAt" in { - val t = thr(overrides = ovr(ZonedDateTime.now()), status = st(None)) - t.hasTemporaryOverridesToReconcile shouldBe true - } - - "return false if all temporaryOverrides' end is before calculatedAt" in { - val end = ZonedDateTime.now() - val t = thr( - overrides = ovr(end), - status = st(Option(end.plus(1, ChronoUnit.MINUTES))) - ) - t.hasTemporaryOverridesToReconcile shouldBe false - } - - "return true if all temporaryOverrides' end is equal calculatedAt" in { - val end = ZonedDateTime.now() - val t = thr( - overrides = ovr(end), - status = st(Option(end)) - ) - t.hasTemporaryOverridesToReconcile shouldBe true - } - - "return false if some temporaryOverrides' end is after calculatedAt" in { - val end = ZonedDateTime.now() - val t = thr( - overrides = ovr(end), - status = st(Option(end.minus(1, ChronoUnit.MINUTES))) - ) - t.hasTemporaryOverridesToReconcile shouldBe true - } - } -} diff --git a/src/test/scala/com/github/everpeace/k8s/throttler/crd/CalculateThresholdSyntaxSpec.scala b/src/test/scala/com/github/everpeace/k8s/throttler/crd/CalculateThresholdSyntaxSpec.scala deleted file mode 100644 index 3cfda92..0000000 --- a/src/test/scala/com/github/everpeace/k8s/throttler/crd/CalculateThresholdSyntaxSpec.scala +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd -import com.github.everpeace.k8s.throttler.crd.v1alpha1.{ - ResourceAmount, - ResourceCount, - TemporaryThresholdOverride -} -import org.scalatest.{FreeSpec, Matchers} -import skuber.Resource.Quantity - -class CalculateThresholdSyntaxSpec extends FreeSpec with Matchers { - import v1alpha1.Implicits._ - - val baseAmount = ResourceAmount( - resourceCounts = Option(ResourceCount(pod = Option(1))), - resourceRequests = Map( - "r1" -> Quantity("1"), - "r2" -> Quantity("2"), - "r3" -> Quantity("3") - ) - ) - - val at = java.time.ZonedDateTime.parse("2019-03-01T00:00:00+09:00") - - def activeOverride(ra: ResourceAmount) = TemporaryThresholdOverride( - begin = at.minusDays(1), - end = at.plusDays(1), - ra - ) - def inactiveOverride(ra: ResourceAmount) = TemporaryThresholdOverride( - begin = at.plusDays(1), - end = at.plusDays(2), - ra - ) - - "CalculateThresholdSyntax" - { - - "should pass through threshold when empty overrides" in { - (baseAmount, List.empty).thresholdAt(at) shouldBe baseAmount - } - "should ignore inactive overrides" in { - (baseAmount, - List( - inactiveOverride( - ResourceAmount( - resourceRequests = Map("r1" -> Quantity("10")) - )))).thresholdAt(at) shouldBe baseAmount - } - - "should merge active overrides per each resource types" in { - val overrides = List( - activeOverride( - ResourceAmount( - resourceRequests = Map("r1" -> Quantity("10")) - )), - activeOverride( - ResourceAmount( - resourceRequests = Map("r2" -> Quantity("20")) - )), - activeOverride( - ResourceAmount( - resourceRequests = Map("r3" -> Quantity("30")) - )), - activeOverride( - ResourceAmount( - resourceCounts = Option(ResourceCount(Option(2))) - )), - inactiveOverride( - ResourceAmount( - resourceRequests = Map("r1" -> Quantity("1000")) - )) - ) - - (baseAmount, overrides).thresholdAt(at) shouldBe ResourceAmount( - resourceCounts = Option(ResourceCount(pod = Option(2))), - resourceRequests = Map( - "r1" -> Quantity("10"), - "r2" -> Quantity("20"), - "r3" -> Quantity("30") - ) - ) - } - - "should choose first active overrides when multiple are active" in { - val overrides = List( - activeOverride( - ResourceAmount( - resourceRequests = Map("r1" -> Quantity("10")) - )), - activeOverride( - ResourceAmount( - resourceRequests = Map("r1" -> Quantity("100"), "r2" -> Quantity("20")) - )) - ) - - (baseAmount, overrides).thresholdAt(at) shouldBe ResourceAmount( - resourceCounts = Option(ResourceCount(pod = Option(1))), - resourceRequests = Map( - "r1" -> Quantity("10"), - "r2" -> Quantity("20"), - "r3" -> Quantity("3") - ) - ) - } - } -} diff --git a/src/test/scala/com/github/everpeace/k8s/throttler/crd/ClusterThrottleFormatSpec.scala b/src/test/scala/com/github/everpeace/k8s/throttler/crd/ClusterThrottleFormatSpec.scala deleted file mode 100644 index bbb696a..0000000 --- a/src/test/scala/com/github/everpeace/k8s/throttler/crd/ClusterThrottleFormatSpec.scala +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd - -import com.github.everpeace.k8s.throttler.crd.v1alpha1.ClusterThrottle.{Selector, SelectorItem} -import com.github.everpeace.k8s.throttler.crd.v1alpha1.Implicits._ -import com.github.everpeace.k8s.throttler.crd.v1alpha1._ -import org.scalatest.{FreeSpec, Matchers} -import play.api.libs.json._ -import skuber.LabelSelector -import skuber.LabelSelector.IsEqualRequirement -import skuber.Resource.Quantity -import java.time.ZonedDateTime - -class ClusterThrottleFormatSpec extends FreeSpec with Matchers { - - "v1alpha1.ClusterThrottle" - { - "yaml can be parsed to case class" in { - val json = Json.parse( - """|{ - | "apiVersion": "schedule.k8s.everpeace.github.com/v1alpha1", - | "kind": "ClusterThrottle", - | "metadata": { - | "name": "app-throttle" - | }, - | "spec": { - | "throttlerName": "kube-throttler", - | "selector": { - | "selectorTerms": [{ - | "podSelector": { - | "matchLabels": { - | "key": "value" - | } - | } - | }] - | }, - | "threshold": { - | "resourceCounts": { - | "pod": 10 - | }, - | "resourceRequests": { - | "cpu": "10", - | "memory": "15Gi", - | "nvidia.com/gpu": "10" - | } - | }, - | "temporaryThresholdOverrides": [{ - | "begin": "2019-02-01T00:00:00+09:00", - | "end": "2019-03-01T00:00:00+09:00", - | "threshold": { - | "resourceRequests": { - | "cpu": "20" - | } - | } - | }] - | }, - | "status": { - | "throttled": { - | "resourceCounts": { - | "pod": false - | }, - | "resourceRequests": { - | "cpu": false, - | "memory": false, - | "nvidia.com/gpu": true - | } - | }, - | "used": { - | "resourceCounts": { - | "pod": 5 - | }, - | "resourceRequests": { - | "cpu": "12", - | "memory": "12Gi", - | "nvidia.com/gpu": "12" - | } - | }, - | "calculatedThreshold":{ - | "threshold": { - | "resourceCounts": { - | "pod": 10 - | }, - | "resourceRequests": { - | "cpu": "20", - | "memory": "15Gi", - | "nvidia.com/gpu": "10" - | } - | }, - | "calculatedAt": "2019-02-01T00:00:00+09:00" - | } - | } - |} - |""".stripMargin - ) - val obj = v1alpha1 - .ClusterThrottle( - name = "app-throttle", - spec = v1alpha1.ClusterThrottle.Spec( - throttlerName = "kube-throttler", - selector = Selector( - selectorTerms = List( - SelectorItem( - podSelector = LabelSelector(IsEqualRequirement("key", "value")), - namespaceSelector = None - ) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(10) - )), - resourceRequests = Map( - "cpu" -> Quantity("10"), - "memory" -> Quantity("15Gi"), - "nvidia.com/gpu" -> Quantity("10") - ) - ), - temporaryThresholdOverrides = List(v1alpha1.TemporaryThresholdOverride( - begin = ZonedDateTime.parse("2019-02-01T00:00:00+09:00"), - end = ZonedDateTime.parse("2019-03-01T00:00:00+09:00"), - threshold = ResourceAmount( - resourceRequests = Map( - "cpu" -> Quantity("20") - ) - ) - )) - ) - ) - .withStatus(v1alpha1.ClusterThrottle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option(IsResourceCountThrottled( - pod = Option(false) - )), - resourceRequests = Map( - "cpu" -> false, - "memory" -> false, - "nvidia.com/gpu" -> true - ) - ), - used = ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(5) - )), - resourceRequests = Map( - "cpu" -> Quantity("12"), - "memory" -> Quantity("12Gi"), - "nvidia.com/gpu" -> Quantity("12") - ) - ), - calculatedThreshold = Option(CalculatedThreshold( - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(10) - )), - resourceRequests = Map( - "cpu" -> Quantity("20"), - "memory" -> Quantity("15Gi"), - "nvidia.com/gpu" -> Quantity("10") - ) - ), - java.time.ZonedDateTime.parse("2019-02-01T00:00:00+09:00") - )) - )) - - json.validate[v1alpha1.ClusterThrottle].get shouldBe obj - Json.toJson(obj) shouldBe json - } - } -} diff --git a/src/test/scala/com/github/everpeace/k8s/throttler/crd/ClusterThrottleSelectorSpec.scala b/src/test/scala/com/github/everpeace/k8s/throttler/crd/ClusterThrottleSelectorSpec.scala deleted file mode 100644 index 4628c40..0000000 --- a/src/test/scala/com/github/everpeace/k8s/throttler/crd/ClusterThrottleSelectorSpec.scala +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd -import org.scalatest.{FreeSpec, Matchers} -import skuber.{LabelSelector, Namespace, ObjectMeta, Pod} - -class ClusterThrottleSelectorSpec extends FreeSpec with Matchers with v1alpha1.Syntax { - def mkNs(name: String, labels: Map[String, String] = Map.empty) = - Namespace.from( - ObjectMeta( - name = name, - labels = labels - )) - def mkPod(namespace: String = "", name: String = "", labels: Map[String, String] = Map.empty) = - Pod - .named("") - .copy( - metadata = ObjectMeta( - namespace = namespace, - name = name, - labels = labels - ) - ) - - "SelecterItem" - { - "maches(pod, namespace) should not match anything when pod and namespace are different" in { - val selectorItem = v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(LabelSelector.InRequirement("key1", List("value1"))), - Option(LabelSelector(LabelSelector.InRequirement("nskey1", List("nsvalue1")))) - ) - val p1 = mkPod("default") - val ns1 = mkNs("different") - selectorItem.matches(p1, ns1) shouldBe false - } - - "with podSelector and namespaceSelector should be evaluated in AND-ed" in { - val selectorItem = v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(LabelSelector.InRequirement("key1", List("value1"))), - Option(LabelSelector(LabelSelector.InRequirement("nskey1", List("nsvalue1")))) - ) - - val p1 = mkPod("default", "pod", Map("key1" -> "value1")) - val p2 = mkPod("default", "pod", Map("key2" -> "value2")) - val ns1 = mkNs(p1.namespace, Map("nskey1" -> "nsvalue1")) - val ns2 = mkNs(p2.namespace, Map("nskey2" -> "nsvalue2")) - - selectorItem.matches(p1, ns1) shouldBe true - selectorItem.matches(p1, ns2) shouldBe false - selectorItem.matches(p2, ns1) shouldBe false - selectorItem.matches(p2, ns2) shouldBe false - } - - "with empty namespaceSelector should be matched to all namespaces" in { - val selectorItem = v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(LabelSelector.InRequirement("key1", List("value1"))), - None - ) - val p1 = mkPod("default", "pod", Map("key1" -> "value1")) - val p2 = mkPod("default2", "pod", Map("key1" -> "value1")) - val ns1 = mkNs(p1.namespace) - val ns2 = mkNs(p2.namespace) - selectorItem.matches(p1, ns1) shouldBe true - selectorItem.matches(p2, ns2) shouldBe true - } - } - - "Selector" - { - "matches(pod, namespace) returns false when pod and namespace is different" in { - val p = mkPod() - val ns = mkNs("ns1") - v1alpha1.ClusterThrottle.Selector(List.empty).matches(p, ns) shouldBe false - } - "with empty selecterTerms doesn't match anything" in { - val p = mkPod() - val ns = mkNs(p.namespace) - v1alpha1.ClusterThrottle.Selector(List.empty).matches(p, ns) shouldBe false - } - "nonEmpty selecterTerms are evaluated in OR-ed" in { - val selector = v1alpha1.ClusterThrottle.Selector( - List( - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(LabelSelector.InRequirement("key1", List("value1")))), - v1alpha1.ClusterThrottle.SelectorItem( - LabelSelector(LabelSelector.InRequirement("key2", List("value2")))), - )) - - val p1 = mkPod("default", "pod", Map("key1" -> "value1")) - val p2 = mkPod("default", "pod", Map("key2" -> "value2")) - val p3 = mkPod("default", "pod", Map("key3" -> "value3")) - val ns = mkNs("default") - - selector.matches(p1, ns) shouldBe true - selector.matches(p2, ns) shouldBe true - selector.matches(p3, ns) shouldBe false - } - } - -} diff --git a/src/test/scala/com/github/everpeace/k8s/throttler/crd/TemporaryThresholdOverrideSpec.scala b/src/test/scala/com/github/everpeace/k8s/throttler/crd/TemporaryThresholdOverrideSpec.scala deleted file mode 100644 index 0e84631..0000000 --- a/src/test/scala/com/github/everpeace/k8s/throttler/crd/TemporaryThresholdOverrideSpec.scala +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd -import com.github.everpeace.k8s.throttler.crd.v1alpha1.{ - ResourceAmount, - ResourceCount, - TemporaryThresholdOverride -} -import org.scalatest.{FreeSpec, Matchers} -import play.api.libs.json.Json - -import java.time.{ZonedDateTime, Instant, ZoneOffset} - -class TemporaryThresholdOverrideSpec extends FreeSpec with Matchers with v1alpha1.JsonFormat { - val amount = ResourceAmount(resourceCounts = Option(ResourceCount(Option(1)))) - val epoch = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0), ZoneOffset.UTC) - - "TemporaryThresholdOverrideFormat" - { - "should parse ISO-like (RFC3339-like) timestamp" in { - val json = Json.parse(""" - |{ - | "begin": "2019-02-01T00:00:00+09:00", - | "end": "2019-02-01T00:00:00+09:00", - | "threshold": { - | "resourceCounts": { - | "pod": 1 - | } - | } - |} - """.stripMargin) - val obj = new TemporaryThresholdOverride( - "2019-02-01T00:00:00+09:00", - ZonedDateTime.parse("2019-02-01T00:00:00+09:00"), - "2019-02-01T00:00:00+09:00", - ZonedDateTime.parse("2019-02-01T00:00:00+09:00"), - amount, - None - ) - - json.validate[TemporaryThresholdOverride].get shouldBe obj - Json.toJson(obj) shouldBe json - } - - "should fallback to epoch when parse failed" in { - val json = Json.parse(""" - |{ - | "begin": "malformed time", - | "end": "malformed time", - | "threshold": { - | "resourceCounts": { - | "pod": 1 - | } - | } - |} - """.stripMargin) - val obj = new TemporaryThresholdOverride( - "malformed time", - epoch, - "malformed time", - epoch, - amount, - Option( - "begin: Text 'malformed time' could not be parsed at index 0, end: Text 'malformed time' could not be parsed at index 0") - ) - - json.validate[TemporaryThresholdOverride].get shouldBe obj - Json.toJson(obj) shouldBe json - } - - "TemporaryThresholdOverrideSyntax" - { - import v1alpha1.Implicits._ - - "isActiveAt should not return active at anytime when parse error" in { - val ovrd = TemporaryThresholdOverride("malformed", "2019-02-01T00:00:00+09:00", amount) - - ovrd.begin shouldBe epoch - ovrd.end shouldBe java.time.ZonedDateTime.parse("2019-02-01T00:00:00+09:00") - ovrd.parseError shouldBe Option("begin: Text 'malformed' could not be parsed at index 0") - ovrd.isActiveAt(epoch) shouldBe false - } - - "isActiveAt should return active when at in [begin,end]" in { - val ovrd = - TemporaryThresholdOverride("2019-02-01T00:00:00+09:00", - "2019-02-01T00:01:00+09:00", - amount) - - ovrd.isActiveAt(java.time.ZonedDateTime.parse("2019-01-31T23:59:59+09:00")) shouldBe false - ovrd.isActiveAt(java.time.ZonedDateTime.parse("2019-02-01T00:00:00+09:00")) shouldBe true - ovrd.isActiveAt(java.time.ZonedDateTime.parse("2019-02-01T00:00:30+09:00")) shouldBe true - ovrd.isActiveAt(java.time.ZonedDateTime.parse("2019-02-01T00:01:00+09:00")) shouldBe true - ovrd.isActiveAt(java.time.ZonedDateTime.parse("2019-02-01T00:01:01+09:00")) shouldBe false - } - - "collectParseError should collect parse error string" in { - val ovrdsNg = List( - TemporaryThresholdOverride("malformed", "2019-02-01T00:00:00+09:00", amount), - TemporaryThresholdOverride("2019-02-01T00:00:00+09:00", - "2019-02-01T00:00:00+09:00", - amount), - TemporaryThresholdOverride("2019-02-01T00:00:00+09:00", "malformed", amount), - TemporaryThresholdOverride("malformed", "malformed", amount) - ) - ovrdsNg.collectParseError() shouldBe List( - "[0]: begin: Text 'malformed' could not be parsed at index 0", - "[2]: end: Text 'malformed' could not be parsed at index 0", - "[3]: begin: Text 'malformed' could not be parsed at index 0, end: Text 'malformed' could not be parsed at index 0", - ) - - val ovrdOk = List( - TemporaryThresholdOverride("2019-02-01T00:00:00+09:00", - "2019-02-01T00:00:00+09:00", - amount), - TemporaryThresholdOverride("2019-02-01T00:00:00+09:00", - "2019-02-01T00:00:00+09:00", - amount) - ) - ovrdOk.collectParseError() shouldBe List.empty - } - } - } -} diff --git a/src/test/scala/com/github/everpeace/k8s/throttler/crd/ThrottleFormatSpec.scala b/src/test/scala/com/github/everpeace/k8s/throttler/crd/ThrottleFormatSpec.scala deleted file mode 100644 index b365869..0000000 --- a/src/test/scala/com/github/everpeace/k8s/throttler/crd/ThrottleFormatSpec.scala +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd - -import java.time.ZonedDateTime - -import com.github.everpeace.k8s.throttler.crd.v1alpha1.Implicits._ -import com.github.everpeace.k8s.throttler.crd.v1alpha1.Throttle.{Selector, SelectorItem} -import com.github.everpeace.k8s.throttler.crd.v1alpha1._ -import org.scalatest.{FreeSpec, Matchers} -import play.api.libs.json._ -import skuber.LabelSelector -import skuber.LabelSelector.IsEqualRequirement -import skuber.Resource.Quantity - -class ThrottleFormatSpec extends FreeSpec with Matchers { - - "v1alpha1.Throttle" - { - "yaml can be parsed to case class" in { - val json = Json.parse( - """|{ - | "apiVersion": "schedule.k8s.everpeace.github.com/v1alpha1", - | "kind": "Throttle", - | "metadata": { - | "namespace": "default", - | "name": "app-throttle" - | }, - | "spec": { - | "throttlerName": "kube-throttler", - | "selector": { - | "selectorTerms": [{ - | "podSelector": { - | "matchLabels": { - | "key": "value" - | } - | } - | }] - | }, - | "threshold": { - | "resourceCounts": { - | "pod": 10 - | }, - | "resourceRequests": { - | "cpu": "10", - | "memory": "15Gi", - | "nvidia.com/gpu": "10" - | } - | }, - | "temporaryThresholdOverrides": [{ - | "begin": "2019-02-01T00:00:00+09:00", - | "end": "2019-03-01T00:00:00+09:00", - | "threshold": { - | "resourceRequests": { - | "cpu": "20" - | } - | } - | }] - | }, - | "status": { - | "throttled": { - | "resourceCounts": { - | "pod": true - | }, - | "resourceRequests": { - | "cpu": false, - | "memory": false, - | "nvidia.com/gpu": true - | } - | }, - | "used": { - | "resourceCounts": { - | "pod": 12 - | }, - | "resourceRequests": { - | "cpu": "12", - | "memory": "12Gi", - | "nvidia.com/gpu": "12" - | } - | }, - | "calculatedThreshold":{ - | "threshold": { - | "resourceCounts": { - | "pod": 10 - | }, - | "resourceRequests": { - | "cpu": "20", - | "memory": "15Gi", - | "nvidia.com/gpu": "10" - | } - | }, - | "calculatedAt": "2019-02-01T00:00:00+09:00" - | } - | } - |} - |""".stripMargin - ) - val obj = v1alpha1 - .Throttle( - name = "app-throttle", - spec = v1alpha1.Throttle.Spec( - throttlerName = "kube-throttler", - selector = Selector( - selectorTerms = List( - SelectorItem(podSelector = LabelSelector(IsEqualRequirement("key", "value"))) - )), - threshold = ResourceAmount( - resourceCounts = Option( - ResourceCount( - pod = Option(10) - )), - resourceRequests = Map( - "cpu" -> Quantity("10"), - "memory" -> Quantity("15Gi"), - "nvidia.com/gpu" -> Quantity("10") - ) - ), - temporaryThresholdOverrides = List(v1alpha1.TemporaryThresholdOverride( - begin = ZonedDateTime.parse("2019-02-01T00:00:00+09:00"), - end = ZonedDateTime.parse("2019-03-01T00:00:00+09:00"), - threshold = ResourceAmount( - resourceRequests = Map( - "cpu" -> Quantity("20") - ) - ) - )) - ) - ) - .withNamespace("default") - .withStatus(v1alpha1.Throttle.Status( - throttled = IsResourceAmountThrottled( - resourceCounts = Option(IsResourceCountThrottled( - pod = Option(true) - )), - resourceRequests = Map( - "cpu" -> false, - "memory" -> false, - "nvidia.com/gpu" -> true - ) - ), - used = ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(12) - )), - resourceRequests = Map( - "cpu" -> Quantity("12"), - "memory" -> Quantity("12Gi"), - "nvidia.com/gpu" -> Quantity("12") - ) - ), - calculatedThreshold = Option(CalculatedThreshold( - ResourceAmount( - resourceCounts = Option(ResourceCount( - pod = Option(10) - )), - resourceRequests = Map( - "cpu" -> Quantity("20"), - "memory" -> Quantity("15Gi"), - "nvidia.com/gpu" -> Quantity("10") - ) - ), - java.time.ZonedDateTime.parse("2019-02-01T00:00:00+09:00") - )) - )) - - json.validate[v1alpha1.Throttle].get shouldBe obj - Json.toJson(obj) shouldBe json - } - } -} diff --git a/src/test/scala/com/github/everpeace/k8s/throttler/crd/ThrottleSelectorSpec.scala b/src/test/scala/com/github/everpeace/k8s/throttler/crd/ThrottleSelectorSpec.scala deleted file mode 100644 index 05bfe6a..0000000 --- a/src/test/scala/com/github/everpeace/k8s/throttler/crd/ThrottleSelectorSpec.scala +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 com.github.everpeace.k8s.throttler.crd - -import org.scalatest.{FreeSpec, Matchers} -import skuber.{LabelSelector, Namespace, ObjectMeta, Pod} - -class ThrottleSelectorSpec extends FreeSpec with Matchers with v1alpha1.Syntax { - def mkNs(name: String, labels: Map[String, String] = Map.empty) = - Namespace.from( - ObjectMeta( - name = name, - labels = labels - )) - def mkPod(namespace: String = "", name: String = "", labels: Map[String, String] = Map.empty) = - Pod - .named("") - .copy( - metadata = ObjectMeta( - namespace = namespace, - name = name, - labels = labels - ) - ) - - "Selector" - { - "with empty selecterTerms doesn't match anything" in { - v1alpha1.Throttle.Selector(List.empty).matches(mkPod()) shouldBe false - } - - "with non empty selecterTerms are evaluated in OR-ed" in { - val selector = v1alpha1.Throttle.Selector( - List( - v1alpha1.Throttle.SelectorItem( - LabelSelector(LabelSelector.InRequirement("key1", List("value1")))), - v1alpha1.Throttle.SelectorItem( - LabelSelector(LabelSelector.InRequirement("key2", List("value2")))), - )) - - val p1 = mkPod("", "", Map("key1" -> "value1")) - val p2 = mkPod("", "", Map("key2" -> "value2")) - val p3 = mkPod("", "", Map("key3" -> "value3")) - - selector.matches(p1) shouldBe true - selector.matches(p2) shouldBe true - selector.matches(p3) shouldBe false - } - } - -} diff --git a/src/test/scala/io/k8s/pkg/scheduler/api/v1/ExtenderFormatSpec.scala b/src/test/scala/io/k8s/pkg/scheduler/api/v1/ExtenderFormatSpec.scala deleted file mode 100644 index 400462e..0000000 --- a/src/test/scala/io/k8s/pkg/scheduler/api/v1/ExtenderFormatSpec.scala +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2018 Shingo Omura - * - * 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 io.k8s.pkg.scheduler.api.v1 -import org.scalatest.{FreeSpec, Matchers} -import play.api.libs.json._ -import ExtenderFormatSpec._ -import Implicits._ - -class ExtenderFormatSpec extends FreeSpec with Matchers { - - "Format[ExtenderArgs]" - { - "can unmarshal jsons without 'apiVersion' and 'kind'" in { - argTestJson.validate[ExtenderArgs].isSuccess shouldBe true - } - "can marshal ExtenderArgs to json without 'apiVersion' and 'kind'" in { - Json.toJson(argTestJson.validate[ExtenderArgs].get) shouldBe argTestJson - } - } - - "Format[ExtenderFilterResult]" - { - "can unmarshal jsons without 'apiVersion' and 'kind'" in { - filterResultJson.validate[ExtenderFilterResult].isSuccess shouldBe true - } - "can marshal ExtenderFilterResult to json without 'apiVersion' and 'kind" in { - Json.toJson(filterResultJson.validate[ExtenderFilterResult].get) shouldBe filterResultJson - } - } - - "Format[ExtenderPreemptionArgs]" - { - "can unmarshal jsons without 'apiVersion' and 'kind'" in { - preemptArgJson.validate[ExtenderFilterResult].isSuccess shouldBe true - } - "can marshal ExtenderPreemptionArgs to json without 'apiVersion' and 'kind" in { - val v = preemptArgJson.validate[ExtenderPreemptionArgs] - Json.toJson(v.get) shouldBe preemptArgJson - } - } - - "Format[ExtenderPreemptionResult]" - { - "can unmarshal jsons without 'apiVersion' and 'kind'" in { - preemptResultJson.validate[ExtenderFilterResult].isSuccess shouldBe true - } - "can marshal ExtenderPreemptionResult to json without 'apiVersion' and 'kind" in { - Json.toJson(preemptResultJson.validate[ExtenderPreemptionResult].get) shouldBe preemptResultJson - } - } -} - -object ExtenderFormatSpec { - val argTestJson = Json.parse("""|{ - | "Pod": { - | "metadata": { - | "name": "pod-rzgq6", - | "labels": { - | "throttle": "t1" - | } - | } - | }, - | "NodeNames": [ "minikube" ], - | "Nodes": { - | "metadata": {}, - | "items": [ - | { - | "metadata": { - | "name": "minikube" - | } - | } - | ] - | } - |} - |""".stripMargin) - - val filterResultJson = Json.parse("""|{ - | "Nodes": { - | "metadata": {}, - | "items": [ - | { - | "metadata": { - | "name": "minikube" - | } - | } - | ] - | }, - | "FailedNodes": { - | "minikube": "error" - | }, - | "Error": "error" - |} - |""".stripMargin) - - val preemptArgJson = Json.parse("""|{ - | "Pod": { - | "metadata": { - | "name": "pod-rzgq6", - | "labels": { - | "throttle": "t1" - | } - | } - | }, - | "NodeNameToVictims": { - | "node1": { - | "Pods": [{ - | "metadata": { - | "name": "pod-rzgq6", - | "labels": { - | "throttle": "t1" - | } - | } - | }], - | "NumPDBViolations": 0 - | } - | }, - | "NodeNameToMetaVictims": { - | "node1": { - | "Pods": [{ "UID": "xxx" }], - | "NumPDBViolations": 0 - | } - | } - |} - """.stripMargin) - - val preemptResultJson = Json.parse("""|{ - | "NodeNameToMetaVictims":{ - | "node1": { - | "Pods": [{ "UID": "xxx" }], - | "NumPDBViolations": 0 - | } - | } - |} - """.stripMargin) -} diff --git a/version.sbt b/version.sbt deleted file mode 100644 index 509d870..0000000 --- a/version.sbt +++ /dev/null @@ -1 +0,0 @@ -version in ThisBuild := "0.7.5-SNAPSHOT" From 765161e9f6ba72622cd07361bb3b60dfa33e9743 Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Tue, 6 Jul 2021 13:59:55 +0000 Subject: [PATCH 02/16] initialize go project --- .gitignore | 120 +++------- .golangci.yml | 19 ++ .goreleaser.yaml | 27 +++ Dockerfile | 17 ++ LICENSE | 1 + Makefile | 78 +++++++ cmd/root.go | 38 ++++ cmd/scheduler.go | 53 +++++ cmd/version.go | 50 +++++ go.mod | 5 + go.sum | 566 +++++++++++++++++++++++++++++++++++++++++++++++ main.go | 24 ++ 12 files changed, 910 insertions(+), 88 deletions(-) create mode 100644 .golangci.yml create mode 100644 .goreleaser.yaml create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 cmd/root.go create mode 100644 cmd/scheduler.go create mode 100644 cmd/version.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.gitignore b/.gitignore index 62164d5..687aa0e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,99 +1,43 @@ -native/** +dist/ -# Created by https://www.gitignore.io/api/sbt,scala,intellij+all +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,go +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,go -### Intellij+all ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 +### Go ### +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf +# Test binary, built with `go test -c` +*.test -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml +# Output of the go coverage tool, specifically when used with LiteIDE +*.out -# Gradle -.idea/**/gradle.xml -.idea/**/libraries +# Dependency directories (remove the comment below to include it) +# vendor/ -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/modules.xml -# .idea/*.iml -# .idea/modules +### Go Patch ### +/vendor/ +/Godeps/ -# CMake -cmake-build-*/ +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace -# Mongo Explorer plugin -.idea/**/mongoSettings.xml +# Local History for Visual Studio Code +.history/ -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -### Intellij+all Patch ### -# Ignores the whole .idea folder and all .iml files -# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 - -.idea/ - -# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 - -*.iml -modules.xml -.idea/misc.xml -*.ipr - -### SBT ### -# Simple Build Tool -# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control - -dist/* -target/ -lib_managed/ -src_managed/ -project/boot/ -project/plugins/project/ +### VisualStudioCode Patch ### +# Ignore all local history of files .history -.cache -.lib/ - -### Scala ### -*.class -*.log - +.ionide -# End of https://www.gitignore.io/api/sbt,scala,intellij+all +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,go \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..beca021 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,19 @@ +run: + tests: false + skip-dirs: + - .dev/ +output: + format: colored-line-number + print-issued-lines: true + print-linter-name: true +linters: + enable: + - govet + - gofmt + - goimports + - unconvert + - misspell + - interfacer + - maligned + - prealloc + fast: false diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..e5e59b6 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,27 @@ +before: + hooks: + - go mod download +builds: +- env: + - CGO_ENABLED=0 + - GO111MODULE=on + main: ./ + binary: kube-throttler + goos: + - linux + - darwin + goarch: + - amd64 + - arm64 + flags: + - -a + - -tags + - netgo + - -installsuffix + - netgo + ldflags: + - -s -w -X github.com/everpeace/kube-throttler/cmd.Version={{.Version}} -X github.com/everpeace/kube-throttler/cmd.Revision={{.ShortCommit}} -extldflags "-static" +# changelog will be generated by release drafter +# see .github/release-drafter.yml +changelog: + skip: true diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..208b72a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM golang:1.16 as builder +ARG RELEASE +ARG VERSION +WORKDIR /workspace +COPY go.mod go.mod +COPY go.sum go.sum +RUN go mod download + +COPY . /workspace +RUN make build-only + +FROM ubuntu:18.04 as runtime +RUN apt-get update && apt-get install -y ca-certificates && apt-get clean && rm -rf /var/lib/apt/lists/* +WORKDIR / +COPY --from=builder /workspace/dist/kube-throttler /usr/local/bin/kube-throttler +ENTRYPOINT ["/usr/local/bin/kube-throttler"] +CMD ["scheduler"] diff --git a/LICENSE b/LICENSE index 261eeb9..d645695 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,4 @@ + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1685e63 --- /dev/null +++ b/Makefile @@ -0,0 +1,78 @@ +# env +export GO111MODULE=on +export CGO_ENABLED=0 + +# project metadta +NAME := kube-throttler +VERSION ?= $(if $(RELEASE),$(shell git semv now),$(shell git semv patch -p)) +REVISION := $(shell git rev-parse --short HEAD) +IMAGE_PREFIX ?= kube-throttler/ +IMAGE_NAME := $(if $(RELEASE),release,dev) +IMAGE_TAG ?= $(if $(RELEASE),$(VERSION),$(VERSION)-$(REVISION)) +LDFLAGS := -ldflags="-s -w -X \"github.com/everpeace/kube-throttler/cmd.Version=$(VERSION)\" -X \"github.com/everpeace/kube-throttler/cmd.Revision=$(REVISION)\" -extldflags \"-static\"" +OUTDIR ?= ./dist + +.DEFAULT_GOAL := build + +.PHONY: setup +setup: + cd $(shell go env GOPATH) && \ + go get -u golang.org/x/tools/cmd/goimports && \ + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.27.0 && \ + go get -u github.com/elastic/go-licenser && \ + go get -u github.com/linyows/git-semv/cmd/git-semv + +.PHONY: fmt +fmt: + $(shell go env GOPATH)/bin/goimports -w cmd/ + $(shell go env GOPATH)/bin/go-licenser --licensor "Shingo Omura" + +.PHONY: lint +lint: fmt + $(shell go env GOPATH)/bin/golangci-lint run --config .golangci.yml --deadline 30m + +.PHONY: build +build: fmt lint + go build -tags netgo -installsuffix netgo $(LDFLAGS) -o $(OUTDIR)/$(NAME) . + +.PHONY: build-only +build-only: + go build -tags netgo -installsuffix netgo $(LDFLAGS) -o $(OUTDIR)/$(NAME) . + +.PHONY: test +test: fmt lint + go test ./... + +.PHONY: clean +clean: + rm -rf "$(OUTDIR)" + +.PHONY: build-image +build-image: + docker build -t $(shell make -e docker-tag) --build-arg RELEASE=$(RELEASE) --build-arg VERSION=$(VERSION) --target runtime . + docker tag $(shell make -e docker-tag) $(IMAGE_PREFIX)$(IMAGE_NAME):$(VERSION) # without revision + docker tag $(shell make -e docker-tag) $(IMAGE_PREFIX)$(IMAGE_NAME):latest # latest + +.PHONY: push-image +push-image: + docker push $(shell make -e docker-tag) + docker push $(IMAGE_PREFIX)$(IMAGE_NAME):$(VERSION) # without revision + docker push $(IMAGE_PREFIX)$(IMAGE_NAME):latest # latest + +.PHONY: docker-tag +docker-tag: + @echo $(IMAGE_PREFIX)$(IMAGE_NAME):$(IMAGE_TAG) + +# +# Release +# +guard-%: + @ if [ "${${*}}" = "" ]; then \ + echo "Environment variable $* is not set"; \ + exit 1; \ + fi +.PHONY: release +release: guard-RELEASE guard-RELEASE_TAG + git diff --quiet HEAD || (echo "your current branch is dirty" && exit 1) + git tag $(RELEASE_TAG) $(REVISION) + git push origin $(RELEASE_TAG) diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..0900ac5 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,38 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 cmd + +import ( + "github.com/spf13/cobra" +) + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "kube-throttler", + Short: "kube-throttler: throttling your pods in kubernetes cluster", + Long: ``, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + cobra.CheckErr(rootCmd.Execute()) +} + +func init() { +} diff --git a/cmd/scheduler.go b/cmd/scheduler.go new file mode 100644 index 0000000..5f2dba2 --- /dev/null +++ b/cmd/scheduler.go @@ -0,0 +1,53 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// schedulerCmd represents the scheduler command +var schedulerCmd = &cobra.Command{ + Use: "scheduler", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("scheduler called") + }, +} + +func init() { + rootCmd.AddCommand(schedulerCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // schedulerCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // schedulerCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/version.go b/cmd/version.go new file mode 100644 index 0000000..b253a69 --- /dev/null +++ b/cmd/version.go @@ -0,0 +1,50 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +var ( + // Version of the program. It will be injected at build time. + Version string + // Revision of the program. It will be injected at build time. + Revision string +) + +// versionCmd represents the version command +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Print Version", + Long: `Print version.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(VersionString()) + }, +} + +func init() { + rootCmd.AddCommand(versionCmd) +} + +func VersionString() string { + return fmt.Sprintf(`{"Version": "%s", "Revision": "%s"} +`, Version, Revision) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..592dd8e --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/everpeace/kube-throttler + +go 1.16 + +require github.com/spf13/cobra v1.2.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9290f91 --- /dev/null +++ b/go.sum @@ -0,0 +1,566 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..c83cf45 --- /dev/null +++ b/main.go @@ -0,0 +1,24 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package main + +import "github.com/everpeace/kube-throttler/cmd" + +func main() { + cmd.Execute() +} From d8a035a4744cbf6a4d5dfa5c3caacb6a12f078d2 Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Tue, 6 Jul 2021 14:20:46 +0000 Subject: [PATCH 03/16] add github actions workflows --- .github/release-drafter.yml | 19 +++++++++ .github/workflows/ci.yaml | 71 +++++++++++++++++++++++++++++++ .github/workflows/release.yaml | 76 ++++++++++++++++++++++++++++++++++ Makefile | 15 +++---- 4 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/release.yaml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..c18309d --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,19 @@ +name-template: 'v$NEXT_PATCH_VERSION' +tag-template: 'v$NEXT_PATCH_VERSION' +categories: +- title: '💣 Breaking Changes' + label: 'release-note/breaking-change' +- title: '🚀 Features' + label: 'release-note/feature' +- title: '🐛 Bug Fixes' + label: 'release-note/bugfix' +- title: '📜 Documentation' + label: 'release-note/document' +- title: '🧰 Maintenance' + label: 'release-note/chore' +exclude-labels: +- 'release-note/skip' +template: | + ## What’s Changed from $PREVIOUS_TAG + $CHANGES + Thanks to $CONTRIBUTORS diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..c56a32a --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,71 @@ +name: CI + +on: + push: + branches: [ master ] + tags-ignore: [ "**" ] + paths-ignore: [ "**.md" ] + pull_request: + types: [opened, synchronize] + paths-ignore: [ "**.md" ] + +jobs: + + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Set up Go 1.x + uses: actions/setup-go@v2 + with: + go-version: ^1.16 + id: go + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Get dependencies + run: go mod download + - name: Get Devtools + run: make setup + - name: Lint + run : make lint + - name: Build + run: make + - name: Test + run: make test + - name: Validate .goreleaser.yml + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: release --snapshot --skip-publish --rm-dist --debug + + image_build: + name: Image Build + runs-on: ubuntu-latest + env: + DOCKER_BUILDKIT: 1 + IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }}/ + steps: + - name: Set up Go 1.x + uses: actions/setup-go@v2 + with: + go-version: ^1.16 + id: go + - name: Check out + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Get Devtools + run: make setup + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build Docker image + run: make build-image + - name: Push Docker image + if: github.ref == 'refs/heads/master' && github.event_name == 'push' + run: make push-image diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..e8d4d69 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,76 @@ +name: Release +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+" + +jobs: + image_build: + name: Release Docker Image + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + env: + RELEASE: "true" + DOCKER_BUILDKIT: 1 + IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }}/ + steps: + - name: Set up Go 1.x + uses: actions/setup-go@v2 + with: + go-version: ^1.16 + id: go + - name: Check out + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Get Devtools + run: make setup + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build Docker image + run: make build-image + - name: Push Docker image + run: make push-image + + goreleaser: + name: Release Binaries + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + env: + RELEASE: "true" + steps: + - name: Set up Go 1.x + uses: actions/setup-go@v2 + with: + go-version: ^1.16 + id: go + - name: Check out + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Get Devtools + run: make setup + - name: Get dependencies + run: go mod download + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: release --rm-dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + update_release_draft: + name: Draft Release Note + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + env: + RELEASE: "true" + steps: + - uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Makefile b/Makefile index 1685e63..c5d7e02 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,7 @@ export CGO_ENABLED=0 NAME := kube-throttler VERSION ?= $(if $(RELEASE),$(shell git semv now),$(shell git semv patch -p)) REVISION := $(shell git rev-parse --short HEAD) -IMAGE_PREFIX ?= kube-throttler/ -IMAGE_NAME := $(if $(RELEASE),release,dev) +IMAGE_PREFIX ?= "" IMAGE_TAG ?= $(if $(RELEASE),$(VERSION),$(VERSION)-$(REVISION)) LDFLAGS := -ldflags="-s -w -X \"github.com/everpeace/kube-throttler/cmd.Version=$(VERSION)\" -X \"github.com/everpeace/kube-throttler/cmd.Revision=$(REVISION)\" -extldflags \"-static\"" OUTDIR ?= ./dist @@ -50,18 +49,20 @@ clean: .PHONY: build-image build-image: docker build -t $(shell make -e docker-tag) --build-arg RELEASE=$(RELEASE) --build-arg VERSION=$(VERSION) --target runtime . - docker tag $(shell make -e docker-tag) $(IMAGE_PREFIX)$(IMAGE_NAME):$(VERSION) # without revision - docker tag $(shell make -e docker-tag) $(IMAGE_PREFIX)$(IMAGE_NAME):latest # latest + docker tag $(shell make -e docker-tag) $(IMAGE_PREFIX)$(NAME):$(VERSION) # without revision .PHONY: push-image push-image: docker push $(shell make -e docker-tag) - docker push $(IMAGE_PREFIX)$(IMAGE_NAME):$(VERSION) # without revision - docker push $(IMAGE_PREFIX)$(IMAGE_NAME):latest # latest + # without revision + docker push $(IMAGE_PREFIX)$(NAME):$(VERSION) + # latest (update only in release) + $(if $(RELEASE), docker tag $(shell make -e docker-tag) $(IMAGE_PREFIX)$(NAME):latest) + $(if $(RELEASE), docker push $(IMAGE_PREFIX)$(NAME):latest) .PHONY: docker-tag docker-tag: - @echo $(IMAGE_PREFIX)$(IMAGE_NAME):$(IMAGE_TAG) + @echo $(IMAGE_PREFIX)$(NAME):$(IMAGE_TAG) # # Release From b7951587235fe397af8b7b51b0922f7e61abf776 Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Tue, 6 Jul 2021 16:35:17 +0000 Subject: [PATCH 04/16] rename scheduler subcomamnd to kube-scheduler subcommand --- Dockerfile | 2 +- Makefile | 2 +- cmd/kube_scheduler.go | 48 +++ cmd/scheduler.go | 53 --- go.mod | 38 +- go.sum | 641 +++++++++++++++++++++++++++++++-- pkg/scheduler_plugin/plugin.go | 58 +++ 7 files changed, 751 insertions(+), 91 deletions(-) create mode 100644 cmd/kube_scheduler.go delete mode 100644 cmd/scheduler.go create mode 100644 pkg/scheduler_plugin/plugin.go diff --git a/Dockerfile b/Dockerfile index 208b72a..0e399e0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,4 +14,4 @@ RUN apt-get update && apt-get install -y ca-certificates && apt-get clean && rm WORKDIR / COPY --from=builder /workspace/dist/kube-throttler /usr/local/bin/kube-throttler ENTRYPOINT ["/usr/local/bin/kube-throttler"] -CMD ["scheduler"] +CMD ["kube-scheduler"] diff --git a/Makefile b/Makefile index c5d7e02..f01dc0e 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ export CGO_ENABLED=0 NAME := kube-throttler VERSION ?= $(if $(RELEASE),$(shell git semv now),$(shell git semv patch -p)) REVISION := $(shell git rev-parse --short HEAD) -IMAGE_PREFIX ?= "" +IMAGE_PREFIX ?= IMAGE_TAG ?= $(if $(RELEASE),$(VERSION),$(VERSION)-$(REVISION)) LDFLAGS := -ldflags="-s -w -X \"github.com/everpeace/kube-throttler/cmd.Version=$(VERSION)\" -X \"github.com/everpeace/kube-throttler/cmd.Revision=$(REVISION)\" -extldflags \"-static\"" OUTDIR ?= ./dist diff --git a/cmd/kube_scheduler.go b/cmd/kube_scheduler.go new file mode 100644 index 0000000..3705ad1 --- /dev/null +++ b/cmd/kube_scheduler.go @@ -0,0 +1,48 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 cmd + +import ( + "github.com/spf13/cobra" + + "math/rand" + "time" + + "github.com/everpeace/kube-throttler/pkg/scheduler_plugin" + "k8s.io/component-base/logs" + "k8s.io/kubernetes/cmd/kube-scheduler/app" +) + +func kubeSchedulerCmd() *cobra.Command { + rand.Seed(time.Now().UnixNano()) + command := app.NewSchedulerCommand( + app.WithPlugin(scheduler_plugin.Name, scheduler_plugin.New), + ) + command.Short = "run kube-scheduler with kube-throttler plugin (need to enable 'KubeThrottler' plugin in config)" + // TODO: once we switch everything over to Cobra commands, we can go back to calling + // utilflag.InitFlags() (by removing its pflag.Parse() call). For now, we have to set the + // normalize func and add the go flag set by hand. + // utilflag.InitFlags() + logs.InitLogs() + defer logs.FlushLogs() + return command +} + +func init() { + rootCmd.AddCommand(kubeSchedulerCmd()) +} diff --git a/cmd/scheduler.go b/cmd/scheduler.go deleted file mode 100644 index 5f2dba2..0000000 --- a/cmd/scheduler.go +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to Shingo Omura under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Shingo Omura licenses this file to you 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 cmd - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -// schedulerCmd represents the scheduler command -var schedulerCmd = &cobra.Command{ - Use: "scheduler", - Short: "A brief description of your command", - Long: `A longer description that spans multiple lines and likely contains examples -and usage of using your command. For example: - -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("scheduler called") - }, -} - -func init() { - rootCmd.AddCommand(schedulerCmd) - - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // schedulerCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // schedulerCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") -} diff --git a/go.mod b/go.mod index 592dd8e..4770ee3 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,40 @@ module github.com/everpeace/kube-throttler go 1.16 -require github.com/spf13/cobra v1.2.1 +require ( + github.com/spf13/cobra v1.2.1 + k8s.io/api v0.20.5 + k8s.io/apimachinery v0.20.5 + k8s.io/component-base v0.20.5 + k8s.io/kube-scheduler v0.20.5 // indirect + k8s.io/kubernetes v1.20.5 +) + +replace ( + google.golang.org/grpc => google.golang.org/grpc v1.27.1 + k8s.io/api => k8s.io/api v0.20.5 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.20.5 + k8s.io/apimachinery => k8s.io/apimachinery v0.20.5 + k8s.io/apiserver => k8s.io/apiserver v0.20.5 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.20.5 + k8s.io/client-go => k8s.io/client-go v0.20.5 + k8s.io/cloud-provider => k8s.io/cloud-provider v0.20.5 + k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.20.5 + k8s.io/code-generator => k8s.io/code-generator v0.20.5 + k8s.io/component-base => k8s.io/component-base v0.20.5 + k8s.io/component-helpers => k8s.io/component-helpers v0.20.5 + k8s.io/controller-manager => k8s.io/controller-manager v0.20.5 + k8s.io/cri-api => k8s.io/cri-api v0.20.5 + k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.20.5 + k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.20.5 + k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.20.5 + k8s.io/kube-proxy => k8s.io/kube-proxy v0.20.5 + k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.20.5 + k8s.io/kubectl => k8s.io/kubectl v0.20.5 + k8s.io/kubelet => k8s.io/kubelet v0.20.5 + k8s.io/kubernetes => k8s.io/kubernetes v1.20.5 + k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.20.5 + k8s.io/metrics => k8s.io/metrics v0.20.5 + k8s.io/mount-utils => k8s.io/mount-utils v0.20.5 + k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.20.5 +) diff --git a/go.sum b/go.sum index 9290f91..7410de3 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,4 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +bitbucket.org/bertimus9/systemstat v0.0.0-20180207000608-0eeff89b0690/go.mod h1:Ulb78X89vxKYgdL24HMTiXYHlyHEvruOj1ZPlqeNEZM= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= @@ -6,6 +6,7 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= @@ -37,45 +38,233 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v43.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= +github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20200415212048-7901bc822317/go.mod h1:DF8FZRxMHMGv/vP2lQP6h+dYzzjpuRn24VeRiYn3qjQ= +github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA= +github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/hcsshim v0.8.10-0.20200715222032-5eafd1556990/go.mod h1:ay/0dTb7NsG8QMDfsRfLHgZo/6xAJShLe1+ePPflihk= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7/go.mod h1:LWMyo4iOLWXHGdBki7NIht1kHru/0wM179h+d3g8ATM= +github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E= +github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313/go.mod h1:P1wt9Z3DP8O6W3rvwCt0REIlshg1InHImaLW0t3ObY0= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= +github.com/container-storage-interface/spec v1.2.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/coredns/corefile-migration v1.0.10/go.mod h1:RMy/mXdeDlYwzt0vdMEJvT2hGJ2I86/eO0UdXmH9XNI= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw= +github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= +github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -101,9 +290,15 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= +github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= +github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/cadvisor v0.38.8/go.mod h1:1OFB9sOOMkBdUBGCO/1SArawTnDscgMzTodacVDe8mA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -114,8 +309,11 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -131,10 +329,31 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -149,81 +368,277 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/heketi/heketi v9.0.1-0.20190917153846-c2e2a4ab7ab9+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o= +github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/ishidawataru/sctp v0.0.0-20190723014705-7c296d48a2b5/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8= +github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/lpabon/godbc v0.1.1/go.mod h1:Jo9QV0cf3U6jZABgiJ2skINAXb9j8m51r07g4KI92ZA= +github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04= +github.com/lucas-clemente/quic-clients v0.1.0/go.mod h1:y5xVIEoObKqULIKivu+gD/LU90pL73bTdtQjPBvtCBk= +github.com/lucas-clemente/quic-go v0.10.2/go.mod h1:hvaRS9IHjFLMq76puFJeWNfmn+H70QZ/CXoxqw9bzao= +github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hxQ= +github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.0.0-20200520151820-abd8a0e76976/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc92/go.mod h1:X1zlU4p7wOlX4+WRCz+hvlRv8phdL7UqbYD+vQwNMmE= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/quobyte/api v0.1.8/go.mod h1:jL7lIHrmqQ7yh05OJ+eEEdHr0u/kmT1Ff9iHd+4H6VI= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= +github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/storageos/go-api v2.2.0+incompatible/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -231,18 +646,38 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= @@ -251,9 +686,9 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -277,19 +712,30 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -311,6 +757,7 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -323,6 +770,7 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 h1:0Ja1LBD+yisY6RWM/BH7TJVXWsSjs2VwBSmvSX4HdBc= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -334,26 +782,47 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -362,9 +831,13 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -375,6 +848,7 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -383,12 +857,20 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -398,7 +880,10 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -423,8 +908,10 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -441,7 +928,14 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/gonum v0.6.2/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -449,6 +943,7 @@ google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.1-0.20200106000736-b8fc810ca6b5/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= @@ -464,12 +959,12 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -484,6 +979,7 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -503,6 +999,7 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -511,27 +1008,10 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -543,17 +1023,45 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -561,6 +1069,69 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.20.5 h1:zsMTffV0Le2EiI0aKvlTHEnXGxk1HiqGRhJcCPiI7JI= +k8s.io/api v0.20.5/go.mod h1:FQjAceXnVaWDeov2YUWhOb6Yt+5UjErkp6UO3nczO1Y= +k8s.io/apiextensions-apiserver v0.20.5/go.mod h1:1HoTwgjWNizJBIgg0Y9P4RdLtaQquilJ5ArGHv9ZpFk= +k8s.io/apimachinery v0.20.5 h1:wO/FxMVRn223rAKxnBbwCyuN96bS9MFTIvP0e/V7cps= +k8s.io/apimachinery v0.20.5/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apiserver v0.20.5 h1:J8l/MJ9pdYncrc1lhxzObDa7jgrUNfettKGNF8mFV+c= +k8s.io/apiserver v0.20.5/go.mod h1:AY3lKhcJ2Tm81XvvcBzk2VnKINSoN+qczYsdo2YEvIc= +k8s.io/cli-runtime v0.20.5/go.mod h1:ihjPeQWDk7NGVIkNEvpwxA3gJvqtU+LtkDj11TvyXn4= +k8s.io/client-go v0.20.5 h1:dJGtYUvFrFGjQ+GjXEIby0gZWdlAOc0xJBJqY3VyDxA= +k8s.io/client-go v0.20.5/go.mod h1:Ee5OOMMYvlH8FCZhDsacjMlCBwetbGZETwo1OA+e6Zw= +k8s.io/cloud-provider v0.20.5 h1:vF/8qZRIfwqNQhd9gv3apZvnvTc4qcZJvYWzzZb0K08= +k8s.io/cloud-provider v0.20.5/go.mod h1:GrzNM+VAk1cy88FJPnF9F/PUPeeD5aqfIZmp2QONG7Y= +k8s.io/cluster-bootstrap v0.20.5/go.mod h1:vr2e5AAGqdWBupioz62IRLvk+SjWqAOq2J2DtIuK6Ak= +k8s.io/code-generator v0.20.5/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= +k8s.io/component-base v0.20.5 h1:8BZQKLJGhWrxtB7kIOEejKDtAKr1HOYvB0PZNeTyLS0= +k8s.io/component-base v0.20.5/go.mod h1:l0isoBLGyQKwRoTWbPHR6jNDd3/VqQD43cNlsjddGng= +k8s.io/component-helpers v0.20.5 h1:JmmGqBM7CaJRUKL6oVFoiM7BT2hE9cxg/yjrPkMhSbk= +k8s.io/component-helpers v0.20.5/go.mod h1:AzTdoPj6YAN2SUfhBX/FUUU3ntfFuse03q/VMLovEsE= +k8s.io/controller-manager v0.20.5 h1:2OZPUfW5Y7LUePa2MckvyhguVO9Ka+iE0/CnsxdOOT0= +k8s.io/controller-manager v0.20.5/go.mod h1:r6R3hxyqNz5De1apuLEJxsZ6hvf3TQPhiH+uPWZXB38= +k8s.io/cri-api v0.20.5/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/csi-translation-lib v0.20.5 h1:H8Olsd1f24fHgY011OmUkOyFCeSy/9VdHZQSNpG665Q= +k8s.io/csi-translation-lib v0.20.5/go.mod h1:KASK4nHVw/T8YW8pyMPh/sLkCpICxXN+A+Z83BplHUk= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/kube-aggregator v0.20.5/go.mod h1:0S88kjWs/0UzOMOko6fjy4nwu1OTRrxlpa7rsx0PErA= +k8s.io/kube-controller-manager v0.20.5/go.mod h1:oC7TO9YGTI23FDtgens9eIX8ceXntHeG8xhaPSEgAV4= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kube-proxy v0.20.5/go.mod h1:DBxEvwMdK9/dJHxY6+VCONxHzBMWeebPMQM0Icr0VfY= +k8s.io/kube-scheduler v0.20.5 h1:V6WUUSawUur3okzD8+ShBPt85V32FcSLHbXhfufOEqI= +k8s.io/kube-scheduler v0.20.5/go.mod h1:oCOwGvakNU458nFM1jRC5rzp1USDOFBFoie0OAEN4I8= +k8s.io/kubectl v0.20.5/go.mod h1:mlNQgyV18D4XFt5BmfSkrxQNS+arT2pXDQxxnH5lMiw= +k8s.io/kubelet v0.20.5/go.mod h1:iM18y0xm/1VlznuHFGBd9YVT9MM15TgEWJrJHrZ4mtQ= +k8s.io/kubernetes v1.20.5 h1:oY1KI7d/2rHETR3xngvQ46vuC1cNPp7R/5vQAVd2vqs= +k8s.io/kubernetes v1.20.5/go.mod h1:aOH+RZJ0PFt6Y/G3vbR0zLeGURGW8X4aX9khigekwAo= +k8s.io/legacy-cloud-providers v0.20.5/go.mod h1:YhCukXmwAh+PLncIZMMMIUD0wSZqw4UGukAKe6ZDMbI= +k8s.io/metrics v0.20.5/go.mod h1:vsptOayjKWKWHvWR1vFQY++vxydzaEo/2+JC7kSDKPU= +k8s.io/mount-utils v0.20.5 h1:S2z/6YtfT1Nc+wRxbSwkfFbVHYDFLoLmD4Nw3Nu8qQY= +k8s.io/mount-utils v0.20.5/go.mod h1:Jv9NRZ5L2LF87A17GaGlArD+r3JAJdZFvo4XD1cG4Kc= +k8s.io/sample-apiserver v0.20.5/go.mod h1:QX9q+uZk/a9+EoRTH56rpoUlgLrsBIaRJukuck27K1o= +k8s.io/system-validators v1.2.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6gRrl0Q= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15 h1:4uqm9Mv+w2MmBYD+F4qf/v6tDFUdPOk29C095RbU5mY= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/pkg/scheduler_plugin/plugin.go b/pkg/scheduler_plugin/plugin.go new file mode 100644 index 0000000..e0cc8a8 --- /dev/null +++ b/pkg/scheduler_plugin/plugin.go @@ -0,0 +1,58 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 scheduler_plugin + +import ( + "context" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubernetes/pkg/scheduler/framework" +) + +const ( + // Name of the plugin used in the plugin registry and configurations. + Name = "KubeThrottler" +) + +type KubeThrottler struct { + fh framework.Handle +} + +var _ framework.FilterPlugin = &KubeThrottler{} + +func (p *KubeThrottler) Name() string { + return Name +} + +// New initializes a new plugin and returns it. +func New(_ runtime.Object, fh framework.Handle) (framework.Plugin, error) { + pl := KubeThrottler{ + fh: fh, + } + return &pl, nil +} + +func (p *KubeThrottler) Filter( + ctx context.Context, + state *framework.CycleState, + pod *v1.Pod, + nodeInfo *framework.NodeInfo, +) *framework.Status { + return nil +} From 1d12c40ad2c4657bbb7e8b72ad0fc12d218c343b Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Wed, 7 Jul 2021 05:36:16 +0000 Subject: [PATCH 05/16] added {Cluster}Throttle CRD --- .gitignore | 1 + .golangci.yml | 1 + Makefile | 59 +- deploy/0-crd.yaml | 546 ++++++++++++++---- go.mod | 1 + hack/boilerplate/boilerplate.generatego.txt | 16 + hack/update-codegen.sh | 17 + pkg/apis/schedule/register.go | 23 + .../schedule/v1alpha1/calculated_threshold.go | 30 + .../v1alpha1/clusterthrottle_types.go | 59 ++ pkg/apis/schedule/v1alpha1/doc.go | 22 + pkg/apis/schedule/v1alpha1/register.go | 55 ++ pkg/apis/schedule/v1alpha1/resource_amount.go | 38 ++ .../v1alpha1/temporary_threshold_override.go | 27 + pkg/apis/schedule/v1alpha1/throttle_types.go | 72 +++ .../v1alpha1/zz_generated.deepcopy.go | 413 +++++++++++++ .../clientset/versioned/clientset.go | 98 ++++ pkg/generated/clientset/versioned/doc.go | 21 + .../versioned/fake/clientset_generated.go | 83 +++ pkg/generated/clientset/versioned/fake/doc.go | 21 + .../clientset/versioned/fake/register.go | 57 ++ .../clientset/versioned/scheme/doc.go | 21 + .../clientset/versioned/scheme/register.go | 57 ++ .../schedule/v1alpha1/clusterthrottle.go | 185 ++++++ .../versioned/typed/schedule/v1alpha1/doc.go | 21 + .../typed/schedule/v1alpha1/fake/doc.go | 21 + .../v1alpha1/fake/fake_clusterthrottle.go | 134 +++++ .../v1alpha1/fake/fake_schedule_client.go | 45 ++ .../schedule/v1alpha1/fake/fake_throttle.go | 143 +++++ .../schedule/v1alpha1/generated_expansion.go | 24 + .../schedule/v1alpha1/schedule_client.go | 95 +++ .../typed/schedule/v1alpha1/throttle.go | 196 +++++++ .../informers/externalversions/factory.go | 181 ++++++ .../informers/externalversions/generic.go | 65 +++ .../internalinterfaces/factory_interfaces.go | 41 ++ .../externalversions/schedule/interface.go | 47 ++ .../schedule/v1alpha1/clusterthrottle.go | 90 +++ .../schedule/v1alpha1/interface.go | 53 ++ .../schedule/v1alpha1/throttle.go | 91 +++ .../schedule/v1alpha1/clusterthrottle.go | 69 +++ .../schedule/v1alpha1/expansion_generated.go | 32 + .../listers/schedule/v1alpha1/throttle.go | 100 ++++ 42 files changed, 3235 insertions(+), 136 deletions(-) create mode 100644 hack/boilerplate/boilerplate.generatego.txt create mode 100755 hack/update-codegen.sh create mode 100644 pkg/apis/schedule/register.go create mode 100644 pkg/apis/schedule/v1alpha1/calculated_threshold.go create mode 100644 pkg/apis/schedule/v1alpha1/clusterthrottle_types.go create mode 100644 pkg/apis/schedule/v1alpha1/doc.go create mode 100644 pkg/apis/schedule/v1alpha1/register.go create mode 100644 pkg/apis/schedule/v1alpha1/resource_amount.go create mode 100644 pkg/apis/schedule/v1alpha1/temporary_threshold_override.go create mode 100644 pkg/apis/schedule/v1alpha1/throttle_types.go create mode 100644 pkg/apis/schedule/v1alpha1/zz_generated.deepcopy.go create mode 100644 pkg/generated/clientset/versioned/clientset.go create mode 100644 pkg/generated/clientset/versioned/doc.go create mode 100644 pkg/generated/clientset/versioned/fake/clientset_generated.go create mode 100644 pkg/generated/clientset/versioned/fake/doc.go create mode 100644 pkg/generated/clientset/versioned/fake/register.go create mode 100644 pkg/generated/clientset/versioned/scheme/doc.go create mode 100644 pkg/generated/clientset/versioned/scheme/register.go create mode 100644 pkg/generated/clientset/versioned/typed/schedule/v1alpha1/clusterthrottle.go create mode 100644 pkg/generated/clientset/versioned/typed/schedule/v1alpha1/doc.go create mode 100644 pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/doc.go create mode 100644 pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_clusterthrottle.go create mode 100644 pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_schedule_client.go create mode 100644 pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_throttle.go create mode 100644 pkg/generated/clientset/versioned/typed/schedule/v1alpha1/generated_expansion.go create mode 100644 pkg/generated/clientset/versioned/typed/schedule/v1alpha1/schedule_client.go create mode 100644 pkg/generated/clientset/versioned/typed/schedule/v1alpha1/throttle.go create mode 100644 pkg/generated/informers/externalversions/factory.go create mode 100644 pkg/generated/informers/externalversions/generic.go create mode 100644 pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go create mode 100644 pkg/generated/informers/externalversions/schedule/interface.go create mode 100644 pkg/generated/informers/externalversions/schedule/v1alpha1/clusterthrottle.go create mode 100644 pkg/generated/informers/externalversions/schedule/v1alpha1/interface.go create mode 100644 pkg/generated/informers/externalversions/schedule/v1alpha1/throttle.go create mode 100644 pkg/generated/listers/schedule/v1alpha1/clusterthrottle.go create mode 100644 pkg/generated/listers/schedule/v1alpha1/expansion_generated.go create mode 100644 pkg/generated/listers/schedule/v1alpha1/throttle.go diff --git a/.gitignore b/.gitignore index 687aa0e..b0cd41a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ dist/ +.dev/ # Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,go # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,go diff --git a/.golangci.yml b/.golangci.yml index beca021..fcd6a73 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,6 +2,7 @@ run: tests: false skip-dirs: - .dev/ + skip-dirs-use-default: true output: format: colored-line-number print-issued-lines: true diff --git a/Makefile b/Makefile index f01dc0e..489c4d2 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ export CGO_ENABLED=0 # project metadta NAME := kube-throttler -VERSION ?= $(if $(RELEASE),$(shell git semv now),$(shell git semv patch -p)) +VERSION ?= $(if $(RELEASE),$(shell $(GIT_SEMV) now),$(shell $(GIT_SEMV) patch -p)) REVISION := $(shell git rev-parse --short HEAD) IMAGE_PREFIX ?= IMAGE_TAG ?= $(if $(RELEASE),$(VERSION),$(VERSION)-$(REVISION)) @@ -13,27 +13,30 @@ OUTDIR ?= ./dist .DEFAULT_GOAL := build -.PHONY: setup -setup: - cd $(shell go env GOPATH) && \ - go get -u golang.org/x/tools/cmd/goimports && \ - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.27.0 && \ - go get -u github.com/elastic/go-licenser && \ - go get -u github.com/linyows/git-semv/cmd/git-semv - .PHONY: fmt fmt: - $(shell go env GOPATH)/bin/goimports -w cmd/ - $(shell go env GOPATH)/bin/go-licenser --licensor "Shingo Omura" + $(GO_IMPORTS) -w cmd/ pkg/ + $(GO_LICENSER) --licensor "Shingo Omura" .PHONY: lint lint: fmt - $(shell go env GOPATH)/bin/golangci-lint run --config .golangci.yml --deadline 30m + $(GOLANGCI_LINT) run --config .golangci.yml --deadline 30m .PHONY: build build: fmt lint go build -tags netgo -installsuffix netgo $(LDFLAGS) -o $(OUTDIR)/$(NAME) . +.PHONY: generate +generate: codegen crd + +.PHONY: codegen +codegen: + ./hack/update-codegen.sh + +.PHONY: crd +crd: + $(CONTROLLER_GEN) crd paths=./pkg/apis/... output:stdout > ./deploy/0-crd.yaml + .PHONY: build-only build-only: go build -tags netgo -installsuffix netgo $(LDFLAGS) -o $(OUTDIR)/$(NAME) . @@ -77,3 +80,35 @@ release: guard-RELEASE guard-RELEASE_TAG git diff --quiet HEAD || (echo "your current branch is dirty" && exit 1) git tag $(RELEASE_TAG) $(REVISION) git push origin $(RELEASE_TAG) + + +# +# dev setup +# +.PHONY: setup +DEV_TOOL_PREFIX = $(shell pwd)/.dev +GIT_SEMV = $(DEV_TOOL_PREFIX)/bin/git-semv +GOLANGCI_LINT = $(DEV_TOOL_PREFIX)/bin/golangci-lint +GO_LICENSER = $(DEV_TOOL_PREFIX)/bin/go-licenser +GO_IMPORTS = $(DEV_TOOL_PREFIX)/bin/goimports +CONTROLLER_GEN = $(DEV_TOOL_PREFIX)/bin/controller-gen +setup: + $(call go-get-tool,$(GO_IMPORTS),golang.org/x/tools/cmd/goimports) + $(call go-get-tool,$(GO_LICENSER),github.com/elastic/go-licenser) + $(call go-get-tool,$(GIT_SEMV),github.com/linyows/git-semv/cmd/git-semv) + $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.6.1) + cd $(shell go env GOPATH) && \ + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(DEV_TOOL_PREFIX)/bin v1.27.0 + +# go-get-tool will 'go get' any package $2 and install it to $1. +define go-get-tool +@[ -f $(1) ] || { \ +set -e ;\ +TMP_DIR=$$(mktemp -d) ;\ +cd $$TMP_DIR ;\ +go mod init tmp ;\ +echo "Downloading $(2)" ;\ +GOBIN=$(DEV_TOOL_PREFIX)/bin go get $(2) ;\ +rm -rf $$TMP_DIR ;\ +} +endef diff --git a/deploy/0-crd.yaml b/deploy/0-crd.yaml index ef9f818..496113e 100644 --- a/deploy/0-crd.yaml +++ b/deploy/0-crd.yaml @@ -1,176 +1,474 @@ -apiVersion: apiextensions.k8s.io/v1beta1 + +--- +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: throttles.schedule.k8s.everpeace.github.com + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: throttles.schedule.k8s.everpeace.github.co spec: - group: schedule.k8s.everpeace.github.com - versions: - - name: v1alpha1 - served: true - storage: true - scope: Namespaced + group: schedule.k8s.everpeace.github.co names: - plural: throttles - singular: throttle + categories: + - kube-throttler kind: Throttle + listKind: ThrottleList + plural: throttles shortNames: - thr - thrs - subresources: - status: {} - additionalPrinterColumns: - - name: throttled - JSONPath: .status.throttled - format: byte + singular: throttle + scope: Namespaced + versions: + - additionalPrinterColumns: + - format: byte + jsonPath: .status.throttled + name: throttled type: string - - name: calculatedThreshold - JSONPath: .status.calculatedThreshold.threshold - format: byte + - format: byte + jsonPath: .status.calculatedThreshold.threshold + name: calculatedThreshold + priority: 1 type: string + - format: date + jsonPath: .status.calculatedThreshold.calculatedAt + name: calculatedAt priority: 1 - - name: calculatedAt - JSONPath: .status.calculatedThreshold.calculatedAt - format: date type: date - priority: 1 - validation: - # openAPIV3Schema is the schema for validating custom objects. - openAPIV3Schema: - properties: - spec: - type: object - required: - - selector - - threshold - properties: - selector: - type: object - required: - - selectorTerms - properties: - selectorTerms: - type: array - items: - type: object - properties: - podSelector: + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + selector: + items: + properties: + podSelector: + items: + description: A node selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: Represents a key's relationship to a set + of values. Valid operators are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. If the operator + is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. If the operator is Gt or Lt, the + values array must have a single element, which will + be interpreted as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator type: object - threshold: - type: object - properties: - resourceCounts: + type: array type: object + type: array + temporaryThresholdOverrides: + items: properties: - pod: - type: integer - resourceRequests: + begin: + format: date-time + type: string + end: + format: date-time + type: string + threshold: + properties: + resourceCounts: + properties: + pod: + type: integer + type: object + resourceRequests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: ResourceList is a set of (resource name, quantity) + pairs. + type: object + type: object + required: + - threshold type: object - temporaryThresholdOverrides: - type: array - items: + type: array + threshold: + properties: + resourceCounts: + properties: + pod: + type: integer + type: object + resourceRequests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: ResourceList is a set of (resource name, quantity) + pairs. + type: object type: object - required: - - begin - - end - - threshold + throttlerName: + type: string + required: + - selector + - threshold + - throttlerName + type: object + status: + properties: + calculatedThreshold: properties: - begin: - description: start time string in RFC3339 for this override entry - type: string - minLength: 1 - end: - description: end time string in RFC3339 for this override entry + calculatedAt: + format: date-time type: string - minLength: 1 + messages: + items: + type: string + type: array threshold: - type: object properties: resourceCounts: - type: object properties: pod: type: integer + type: object resourceRequests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: ResourceList is a set of (resource name, quantity) + pairs. type: object + type: object + required: + - calculatedAt + - threshold + type: object + throttled: + properties: + resourceCounts: + properties: + pod: + type: boolean + type: object + resourceRequests: + additionalProperties: + type: boolean + type: object + type: object + used: + properties: + resourceCounts: + properties: + pod: + type: integer + type: object + resourceRequests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: ResourceList is a set of (resource name, quantity) + pairs. + type: object + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + --- -apiVersion: apiextensions.k8s.io/v1beta1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: clusterthrottles.schedule.k8s.everpeace.github.com + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: clusterthrottles.schedule.k8s.everpeace.github.co spec: - group: schedule.k8s.everpeace.github.com - versions: - - name: v1alpha1 - served: true - storage: true - scope: Cluster + group: schedule.k8s.everpeace.github.co names: - plural: clusterthrottles - singular: clusterthrottle + categories: + - kube-throttler kind: ClusterThrottle + listKind: ClusterThrottleList + plural: clusterthrottles shortNames: - clthr - clthrs - subresources: - status: {} - validation: - # openAPIV3Schema is the schema for validating custom objects. - openAPIV3Schema: - properties: - spec: - type: object - required: - - selector - - threshold - properties: - selector: - type: object - required: - - selectorTerms - properties: - selectorTerms: - type: array - items: - type: object - properties: - namespaceSelector: + singular: clusterthrottle + scope: Cluster + versions: + - additionalPrinterColumns: + - format: byte + jsonPath: .status.throttled + name: throttled + type: string + - format: byte + jsonPath: .status.calculatedThreshold.threshold + name: calculatedThreshold + priority: 1 + type: string + - format: date + jsonPath: .status.calculatedThreshold.calculatedAt + name: calculatedAt + priority: 1 + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + selector: + items: + properties: + namespaceSelector: + items: + description: A node selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: Represents a key's relationship to a set + of values. Valid operators are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. If the operator + is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. If the operator is Gt or Lt, the + values array must have a single element, which will + be interpreted as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator type: object - podSelector: + type: array + podSelector: + items: + description: A node selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: Represents a key's relationship to a set + of values. Valid operators are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. If the operator + is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. If the operator is Gt or Lt, the + values array must have a single element, which will + be interpreted as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator type: object - threshold: - type: object - properties: - resourceCounts: + type: array type: object + type: array + temporaryThresholdOverrides: + items: properties: - pod: - type: integer - resourceRequests: + begin: + format: date-time + type: string + end: + format: date-time + type: string + threshold: + properties: + resourceCounts: + properties: + pod: + type: integer + type: object + resourceRequests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: ResourceList is a set of (resource name, quantity) + pairs. + type: object + type: object + required: + - threshold type: object - temporaryThresholdOverrides: - type: array - items: + type: array + threshold: + properties: + resourceCounts: + properties: + pod: + type: integer + type: object + resourceRequests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: ResourceList is a set of (resource name, quantity) + pairs. + type: object type: object - required: - - begin - - end - - threshold + throttlerName: + type: string + required: + - selector + - threshold + - throttlerName + type: object + status: + properties: + calculatedThreshold: properties: - begin: - description: start time string in RFC3339 for this override entry + calculatedAt: + format: date-time type: string - minLength: 1 - end: - description: end time string in RFC3339 for this override entry - type: string - minLength: 1 + messages: + items: + type: string + type: array threshold: - type: object properties: resourceCounts: - type: object properties: pod: type: integer + type: object resourceRequests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: ResourceList is a set of (resource name, quantity) + pairs. type: object + type: object + required: + - calculatedAt + - threshold + type: object + throttled: + properties: + resourceCounts: + properties: + pod: + type: boolean + type: object + resourceRequests: + additionalProperties: + type: boolean + type: object + type: object + used: + properties: + resourceCounts: + properties: + pod: + type: integer + type: object + resourceRequests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: ResourceList is a set of (resource name, quantity) + pairs. + type: object + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/go.mod b/go.mod index 4770ee3..a16da90 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/spf13/cobra v1.2.1 k8s.io/api v0.20.5 k8s.io/apimachinery v0.20.5 + k8s.io/client-go v0.20.5 k8s.io/component-base v0.20.5 k8s.io/kube-scheduler v0.20.5 // indirect k8s.io/kubernetes v1.20.5 diff --git a/hack/boilerplate/boilerplate.generatego.txt b/hack/boilerplate/boilerplate.generatego.txt new file mode 100644 index 0000000..156bf58 --- /dev/null +++ b/hack/boilerplate/boilerplate.generatego.txt @@ -0,0 +1,16 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh new file mode 100755 index 0000000..683139a --- /dev/null +++ b/hack/update-codegen.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +CODEGEN_PKG=${CODEGEN_PKG:-$(ls -d -1 $(go env GOPATH)/pkg/mod/k8s.io/code-generator* 2>/dev/null || echo ../code-generator)} + +echo ${CODEGEN_PKG} + +bash "${CODEGEN_PKG}"/generate-groups.sh \ + all \ + github.com/everpeace/kube-throttler/pkg/generated \ + github.com/everpeace/kube-throttler/pkg/apis \ + "schedule:v1alpha1" \ + --go-header-file "${SCRIPT_ROOT}"/hack/boilerplate/boilerplate.generatego.txt diff --git a/pkg/apis/schedule/register.go b/pkg/apis/schedule/register.go new file mode 100644 index 0000000..93a4170 --- /dev/null +++ b/pkg/apis/schedule/register.go @@ -0,0 +1,23 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 schedule + +// GroupName is the group name used in this package +const ( + GroupName = "schedule.k8s.everpeace.github.com" +) diff --git a/pkg/apis/schedule/v1alpha1/calculated_threshold.go b/pkg/apis/schedule/v1alpha1/calculated_threshold.go new file mode 100644 index 0000000..35a9168 --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/calculated_threshold.go @@ -0,0 +1,30 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type CalculatedThreshold struct { + // +kubebuilder:validation:Required + Threshold ResourceAmount `json:"threshold"` + // +kubebuilder:validation:Required + CalculatedAt metav1.Time `json:"calculatedAt"` + Messages []string `json:"messages,omitempty"` +} diff --git a/pkg/apis/schedule/v1alpha1/clusterthrottle_types.go b/pkg/apis/schedule/v1alpha1/clusterthrottle_types.go new file mode 100644 index 0000000..400f48f --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/clusterthrottle_types.go @@ -0,0 +1,59 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type ClusterThrottleSelectorTerm struct { + ThrottleSelectorTerm `json:",inline"` + NamespaceSelector []corev1.NodeSelectorRequirement `json:"namespaceSelector"` +} + +type ClusterThrottleSpec struct { + ThrottleSpecBase `json:",inline"` + // +kubebuilder:validation:Required + Selector []ClusterThrottleSelectorTerm `json:"selector,omitempty"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:categories=kube-throttler,scope=Cluster,shortName=clthr;clthrs +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name=throttled,JSONPath=.status.throttled,format=byte,type=string +// +kubebuilder:printcolumn:name=calculatedThreshold,JSONPath=.status.calculatedThreshold.threshold,format=byte,type=string,priority=1 +// +kubebuilder:printcolumn:name=calculatedAt,JSONPath=.status.calculatedThreshold.calculatedAt,format=date,type=date,priority=1 + +type ClusterThrottle struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ClusterThrottleSpec `json:"spec,omitempty"` + Status ThrottleStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type ClusterThrottleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterThrottle `json:"items"` +} diff --git a/pkg/apis/schedule/v1alpha1/doc.go b/pkg/apis/schedule/v1alpha1/doc.go new file mode 100644 index 0000000..55408b1 --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/doc.go @@ -0,0 +1,22 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// +k8s:deepcopy-gen=package +// +groupName=schedule.k8s.everpeace.github.co +// +kubebuilder:validation:Optional + +package v1alpha1 diff --git a/pkg/apis/schedule/v1alpha1/register.go b/pkg/apis/schedule/v1alpha1/register.go new file mode 100644 index 0000000..d52ca05 --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/register.go @@ -0,0 +1,55 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import ( + "github.com/everpeace/kube-throttler/pkg/apis/schedule" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: schedule.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + // SchemeBuilder initializes a scheme builder + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + // AddToScheme is a global function that registers this API group & version to a scheme + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &ClusterThrottle{}, &ClusterThrottleList{}, + &Throttle{}, &ThrottleList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/schedule/v1alpha1/resource_amount.go b/pkg/apis/schedule/v1alpha1/resource_amount.go new file mode 100644 index 0000000..2a2b4cc --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/resource_amount.go @@ -0,0 +1,38 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import corev1 "k8s.io/api/core/v1" + +type ResourceAmount struct { + ResourceCounts *ResourceCounts `json:"resourceCounts,omitempty"` + ResourceRequests corev1.ResourceList `json:"resourceRequests,omitempty"` +} + +type ResourceCounts struct { + Pod int `json:"pod,omitempty"` +} + +type IsResourceAmountThrottled struct { + ResourceCounts IsResourceCountThrottled `json:"resourceCounts,omitempty"` + ResourceRequests map[corev1.ResourceName]bool `json:"resourceRequests,omitempty"` +} + +type IsResourceCountThrottled struct { + Pod bool `json:"pod,omitempty"` +} diff --git a/pkg/apis/schedule/v1alpha1/temporary_threshold_override.go b/pkg/apis/schedule/v1alpha1/temporary_threshold_override.go new file mode 100644 index 0000000..4174296 --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/temporary_threshold_override.go @@ -0,0 +1,27 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +type TemporaryThresholdOverride struct { + Begin metav1.Time `json:"begin"` + EndStr metav1.Time `json:"end"` + // +kubebuilder:validation:Required + Threshold ResourceAmount `json:"threshold"` +} diff --git a/pkg/apis/schedule/v1alpha1/throttle_types.go b/pkg/apis/schedule/v1alpha1/throttle_types.go new file mode 100644 index 0000000..599a020 --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/throttle_types.go @@ -0,0 +1,72 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type ThrottleSelectorTerm struct { + PodSelector []corev1.NodeSelectorRequirement `json:"podSelector"` +} + +type ThrottleSpecBase struct { + // +kubebuilder:validation:Required + ThrottlerName string `json:"throttlerName,omitempty"` + // +kubebuilder:validation:Required + Threshold ResourceAmount `json:"threshold,omitempty"` + + TemporaryThresholdOverrides []TemporaryThresholdOverride `json:"temporaryThresholdOverrides,omitempty"` +} + +type ThrottleSpec struct { + ThrottleSpecBase `json:",inline"` + // +kubebuilder:validation:Required + Selector []ThrottleSelectorTerm `json:"selector,omitempty"` +} + +type ThrottleStatus struct { + CalculatedThreshold CalculatedThreshold `json:"calculatedThreshold,omitempty"` + Throttled IsResourceAmountThrottled `json:"throttled,omitempty"` + Used ResourceAmount `json:"used,omitempty"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:categories=kube-throttler,shortName=thr;thrs +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name=throttled,JSONPath=.status.throttled,format=byte,type=string +// +kubebuilder:printcolumn:name=calculatedThreshold,JSONPath=.status.calculatedThreshold.threshold,format=byte,type=string,priority=1 +// +kubebuilder:printcolumn:name=calculatedAt,JSONPath=.status.calculatedThreshold.calculatedAt,format=date,type=date,priority=1 + +type Throttle struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ThrottleSpec `json:"spec,omitempty"` + Status ThrottleStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type ThrottleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Throttle `json:"items"` +} diff --git a/pkg/apis/schedule/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/schedule/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000..f304d5a --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,413 @@ +// +build !ignore_autogenerated + +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CalculatedThreshold) DeepCopyInto(out *CalculatedThreshold) { + *out = *in + in.Threshold.DeepCopyInto(&out.Threshold) + in.CalculatedAt.DeepCopyInto(&out.CalculatedAt) + if in.Messages != nil { + in, out := &in.Messages, &out.Messages + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CalculatedThreshold. +func (in *CalculatedThreshold) DeepCopy() *CalculatedThreshold { + if in == nil { + return nil + } + out := new(CalculatedThreshold) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterThrottle) DeepCopyInto(out *ClusterThrottle) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterThrottle. +func (in *ClusterThrottle) DeepCopy() *ClusterThrottle { + if in == nil { + return nil + } + out := new(ClusterThrottle) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterThrottle) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterThrottleList) DeepCopyInto(out *ClusterThrottleList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterThrottle, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterThrottleList. +func (in *ClusterThrottleList) DeepCopy() *ClusterThrottleList { + if in == nil { + return nil + } + out := new(ClusterThrottleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterThrottleList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterThrottleSelectorTerm) DeepCopyInto(out *ClusterThrottleSelectorTerm) { + *out = *in + in.ThrottleSelectorTerm.DeepCopyInto(&out.ThrottleSelectorTerm) + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = make([]v1.NodeSelectorRequirement, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterThrottleSelectorTerm. +func (in *ClusterThrottleSelectorTerm) DeepCopy() *ClusterThrottleSelectorTerm { + if in == nil { + return nil + } + out := new(ClusterThrottleSelectorTerm) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterThrottleSpec) DeepCopyInto(out *ClusterThrottleSpec) { + *out = *in + in.ThrottleSpecBase.DeepCopyInto(&out.ThrottleSpecBase) + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = make([]ClusterThrottleSelectorTerm, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterThrottleSpec. +func (in *ClusterThrottleSpec) DeepCopy() *ClusterThrottleSpec { + if in == nil { + return nil + } + out := new(ClusterThrottleSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IsResourceAmountThrottled) DeepCopyInto(out *IsResourceAmountThrottled) { + *out = *in + out.ResourceCounts = in.ResourceCounts + if in.ResourceRequests != nil { + in, out := &in.ResourceRequests, &out.ResourceRequests + *out = make(map[v1.ResourceName]bool, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IsResourceAmountThrottled. +func (in *IsResourceAmountThrottled) DeepCopy() *IsResourceAmountThrottled { + if in == nil { + return nil + } + out := new(IsResourceAmountThrottled) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IsResourceCountThrottled) DeepCopyInto(out *IsResourceCountThrottled) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IsResourceCountThrottled. +func (in *IsResourceCountThrottled) DeepCopy() *IsResourceCountThrottled { + if in == nil { + return nil + } + out := new(IsResourceCountThrottled) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceAmount) DeepCopyInto(out *ResourceAmount) { + *out = *in + if in.ResourceCounts != nil { + in, out := &in.ResourceCounts, &out.ResourceCounts + *out = new(ResourceCounts) + **out = **in + } + if in.ResourceRequests != nil { + in, out := &in.ResourceRequests, &out.ResourceRequests + *out = make(v1.ResourceList, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceAmount. +func (in *ResourceAmount) DeepCopy() *ResourceAmount { + if in == nil { + return nil + } + out := new(ResourceAmount) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceCounts) DeepCopyInto(out *ResourceCounts) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceCounts. +func (in *ResourceCounts) DeepCopy() *ResourceCounts { + if in == nil { + return nil + } + out := new(ResourceCounts) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TemporaryThresholdOverride) DeepCopyInto(out *TemporaryThresholdOverride) { + *out = *in + in.Begin.DeepCopyInto(&out.Begin) + in.EndStr.DeepCopyInto(&out.EndStr) + in.Threshold.DeepCopyInto(&out.Threshold) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TemporaryThresholdOverride. +func (in *TemporaryThresholdOverride) DeepCopy() *TemporaryThresholdOverride { + if in == nil { + return nil + } + out := new(TemporaryThresholdOverride) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Throttle) DeepCopyInto(out *Throttle) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Throttle. +func (in *Throttle) DeepCopy() *Throttle { + if in == nil { + return nil + } + out := new(Throttle) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Throttle) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ThrottleList) DeepCopyInto(out *ThrottleList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Throttle, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ThrottleList. +func (in *ThrottleList) DeepCopy() *ThrottleList { + if in == nil { + return nil + } + out := new(ThrottleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ThrottleList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ThrottleSelectorTerm) DeepCopyInto(out *ThrottleSelectorTerm) { + *out = *in + if in.PodSelector != nil { + in, out := &in.PodSelector, &out.PodSelector + *out = make([]v1.NodeSelectorRequirement, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ThrottleSelectorTerm. +func (in *ThrottleSelectorTerm) DeepCopy() *ThrottleSelectorTerm { + if in == nil { + return nil + } + out := new(ThrottleSelectorTerm) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ThrottleSpec) DeepCopyInto(out *ThrottleSpec) { + *out = *in + in.ThrottleSpecBase.DeepCopyInto(&out.ThrottleSpecBase) + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = make([]ThrottleSelectorTerm, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ThrottleSpec. +func (in *ThrottleSpec) DeepCopy() *ThrottleSpec { + if in == nil { + return nil + } + out := new(ThrottleSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ThrottleSpecBase) DeepCopyInto(out *ThrottleSpecBase) { + *out = *in + in.Threshold.DeepCopyInto(&out.Threshold) + if in.TemporaryThresholdOverrides != nil { + in, out := &in.TemporaryThresholdOverrides, &out.TemporaryThresholdOverrides + *out = make([]TemporaryThresholdOverride, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ThrottleSpecBase. +func (in *ThrottleSpecBase) DeepCopy() *ThrottleSpecBase { + if in == nil { + return nil + } + out := new(ThrottleSpecBase) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ThrottleStatus) DeepCopyInto(out *ThrottleStatus) { + *out = *in + in.CalculatedThreshold.DeepCopyInto(&out.CalculatedThreshold) + in.Throttled.DeepCopyInto(&out.Throttled) + in.Used.DeepCopyInto(&out.Used) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ThrottleStatus. +func (in *ThrottleStatus) DeepCopy() *ThrottleStatus { + if in == nil { + return nil + } + out := new(ThrottleStatus) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go new file mode 100644 index 0000000..af94c10 --- /dev/null +++ b/pkg/generated/clientset/versioned/clientset.go @@ -0,0 +1,98 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + "fmt" + + schedulev1alpha1 "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned/typed/schedule/v1alpha1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + ScheduleV1alpha1() schedulev1alpha1.ScheduleV1alpha1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + scheduleV1alpha1 *schedulev1alpha1.ScheduleV1alpha1Client +} + +// ScheduleV1alpha1 retrieves the ScheduleV1alpha1Client +func (c *Clientset) ScheduleV1alpha1() schedulev1alpha1.ScheduleV1alpha1Interface { + return c.scheduleV1alpha1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.scheduleV1alpha1, err = schedulev1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.scheduleV1alpha1 = schedulev1alpha1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.scheduleV1alpha1 = schedulev1alpha1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/pkg/generated/clientset/versioned/doc.go b/pkg/generated/clientset/versioned/doc.go new file mode 100644 index 0000000..f4d8139 --- /dev/null +++ b/pkg/generated/clientset/versioned/doc.go @@ -0,0 +1,21 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/pkg/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 0000000..6f87f26 --- /dev/null +++ b/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,83 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + clientset "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned" + schedulev1alpha1 "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned/typed/schedule/v1alpha1" + fakeschedulev1alpha1 "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/discovery" + fakediscovery "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/testing" +) + +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{tracker: o} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery + tracker testing.ObjectTracker +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +func (c *Clientset) Tracker() testing.ObjectTracker { + return c.tracker +} + +var _ clientset.Interface = &Clientset{} + +// ScheduleV1alpha1 retrieves the ScheduleV1alpha1Client +func (c *Clientset) ScheduleV1alpha1() schedulev1alpha1.ScheduleV1alpha1Interface { + return &fakeschedulev1alpha1.FakeScheduleV1alpha1{Fake: &c.Fake} +} diff --git a/pkg/generated/clientset/versioned/fake/doc.go b/pkg/generated/clientset/versioned/fake/doc.go new file mode 100644 index 0000000..6c16a19 --- /dev/null +++ b/pkg/generated/clientset/versioned/fake/doc.go @@ -0,0 +1,21 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated fake clientset. +package fake diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go new file mode 100644 index 0000000..66a99b1 --- /dev/null +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -0,0 +1,57 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + schedulev1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) + +var localSchemeBuilder = runtime.SchemeBuilder{ + schedulev1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(scheme)) +} diff --git a/pkg/generated/clientset/versioned/scheme/doc.go b/pkg/generated/clientset/versioned/scheme/doc.go new file mode 100644 index 0000000..e92df1d --- /dev/null +++ b/pkg/generated/clientset/versioned/scheme/doc.go @@ -0,0 +1,21 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go new file mode 100644 index 0000000..ca667cb --- /dev/null +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -0,0 +1,57 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + schedulev1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + schedulev1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/clusterthrottle.go b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/clusterthrottle.go new file mode 100644 index 0000000..a3ee12b --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/clusterthrottle.go @@ -0,0 +1,185 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + scheme "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ClusterThrottlesGetter has a method to return a ClusterThrottleInterface. +// A group's client should implement this interface. +type ClusterThrottlesGetter interface { + ClusterThrottles() ClusterThrottleInterface +} + +// ClusterThrottleInterface has methods to work with ClusterThrottle resources. +type ClusterThrottleInterface interface { + Create(ctx context.Context, clusterThrottle *v1alpha1.ClusterThrottle, opts v1.CreateOptions) (*v1alpha1.ClusterThrottle, error) + Update(ctx context.Context, clusterThrottle *v1alpha1.ClusterThrottle, opts v1.UpdateOptions) (*v1alpha1.ClusterThrottle, error) + UpdateStatus(ctx context.Context, clusterThrottle *v1alpha1.ClusterThrottle, opts v1.UpdateOptions) (*v1alpha1.ClusterThrottle, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ClusterThrottle, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ClusterThrottleList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterThrottle, err error) + ClusterThrottleExpansion +} + +// clusterThrottles implements ClusterThrottleInterface +type clusterThrottles struct { + client rest.Interface +} + +// newClusterThrottles returns a ClusterThrottles +func newClusterThrottles(c *ScheduleV1alpha1Client) *clusterThrottles { + return &clusterThrottles{ + client: c.RESTClient(), + } +} + +// Get takes name of the clusterThrottle, and returns the corresponding clusterThrottle object, and an error if there is any. +func (c *clusterThrottles) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterThrottle, err error) { + result = &v1alpha1.ClusterThrottle{} + err = c.client.Get(). + Resource("clusterthrottles"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ClusterThrottles that match those selectors. +func (c *clusterThrottles) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterThrottleList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ClusterThrottleList{} + err = c.client.Get(). + Resource("clusterthrottles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusterThrottles. +func (c *clusterThrottles) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("clusterthrottles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a clusterThrottle and creates it. Returns the server's representation of the clusterThrottle, and an error, if there is any. +func (c *clusterThrottles) Create(ctx context.Context, clusterThrottle *v1alpha1.ClusterThrottle, opts v1.CreateOptions) (result *v1alpha1.ClusterThrottle, err error) { + result = &v1alpha1.ClusterThrottle{} + err = c.client.Post(). + Resource("clusterthrottles"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterThrottle). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a clusterThrottle and updates it. Returns the server's representation of the clusterThrottle, and an error, if there is any. +func (c *clusterThrottles) Update(ctx context.Context, clusterThrottle *v1alpha1.ClusterThrottle, opts v1.UpdateOptions) (result *v1alpha1.ClusterThrottle, err error) { + result = &v1alpha1.ClusterThrottle{} + err = c.client.Put(). + Resource("clusterthrottles"). + Name(clusterThrottle.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterThrottle). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *clusterThrottles) UpdateStatus(ctx context.Context, clusterThrottle *v1alpha1.ClusterThrottle, opts v1.UpdateOptions) (result *v1alpha1.ClusterThrottle, err error) { + result = &v1alpha1.ClusterThrottle{} + err = c.client.Put(). + Resource("clusterthrottles"). + Name(clusterThrottle.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterThrottle). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the clusterThrottle and deletes it. Returns an error if one occurs. +func (c *clusterThrottles) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Resource("clusterthrottles"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusterThrottles) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("clusterthrottles"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched clusterThrottle. +func (c *clusterThrottles) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterThrottle, err error) { + result = &v1alpha1.ClusterThrottle{} + err = c.client.Patch(pt). + Resource("clusterthrottles"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/doc.go b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/doc.go new file mode 100644 index 0000000..1352b01 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/doc.go @@ -0,0 +1,21 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/doc.go b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/doc.go new file mode 100644 index 0000000..b05bb58 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/doc.go @@ -0,0 +1,21 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_clusterthrottle.go b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_clusterthrottle.go new file mode 100644 index 0000000..cc19c3f --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_clusterthrottle.go @@ -0,0 +1,134 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeClusterThrottles implements ClusterThrottleInterface +type FakeClusterThrottles struct { + Fake *FakeScheduleV1alpha1 +} + +var clusterthrottlesResource = schema.GroupVersionResource{Group: "schedule.k8s.everpeace.github.co", Version: "v1alpha1", Resource: "clusterthrottles"} + +var clusterthrottlesKind = schema.GroupVersionKind{Group: "schedule.k8s.everpeace.github.co", Version: "v1alpha1", Kind: "ClusterThrottle"} + +// Get takes name of the clusterThrottle, and returns the corresponding clusterThrottle object, and an error if there is any. +func (c *FakeClusterThrottles) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterThrottle, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(clusterthrottlesResource, name), &v1alpha1.ClusterThrottle{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterThrottle), err +} + +// List takes label and field selectors, and returns the list of ClusterThrottles that match those selectors. +func (c *FakeClusterThrottles) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterThrottleList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(clusterthrottlesResource, clusterthrottlesKind, opts), &v1alpha1.ClusterThrottleList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.ClusterThrottleList{ListMeta: obj.(*v1alpha1.ClusterThrottleList).ListMeta} + for _, item := range obj.(*v1alpha1.ClusterThrottleList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested clusterThrottles. +func (c *FakeClusterThrottles) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(clusterthrottlesResource, opts)) +} + +// Create takes the representation of a clusterThrottle and creates it. Returns the server's representation of the clusterThrottle, and an error, if there is any. +func (c *FakeClusterThrottles) Create(ctx context.Context, clusterThrottle *v1alpha1.ClusterThrottle, opts v1.CreateOptions) (result *v1alpha1.ClusterThrottle, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(clusterthrottlesResource, clusterThrottle), &v1alpha1.ClusterThrottle{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterThrottle), err +} + +// Update takes the representation of a clusterThrottle and updates it. Returns the server's representation of the clusterThrottle, and an error, if there is any. +func (c *FakeClusterThrottles) Update(ctx context.Context, clusterThrottle *v1alpha1.ClusterThrottle, opts v1.UpdateOptions) (result *v1alpha1.ClusterThrottle, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(clusterthrottlesResource, clusterThrottle), &v1alpha1.ClusterThrottle{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterThrottle), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeClusterThrottles) UpdateStatus(ctx context.Context, clusterThrottle *v1alpha1.ClusterThrottle, opts v1.UpdateOptions) (*v1alpha1.ClusterThrottle, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(clusterthrottlesResource, "status", clusterThrottle), &v1alpha1.ClusterThrottle{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterThrottle), err +} + +// Delete takes name of the clusterThrottle and deletes it. Returns an error if one occurs. +func (c *FakeClusterThrottles) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(clusterthrottlesResource, name), &v1alpha1.ClusterThrottle{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeClusterThrottles) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(clusterthrottlesResource, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.ClusterThrottleList{}) + return err +} + +// Patch applies the patch and returns the patched clusterThrottle. +func (c *FakeClusterThrottles) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterThrottle, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(clusterthrottlesResource, name, pt, data, subresources...), &v1alpha1.ClusterThrottle{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterThrottle), err +} diff --git a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_schedule_client.go b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_schedule_client.go new file mode 100644 index 0000000..2722e90 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_schedule_client.go @@ -0,0 +1,45 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned/typed/schedule/v1alpha1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeScheduleV1alpha1 struct { + *testing.Fake +} + +func (c *FakeScheduleV1alpha1) ClusterThrottles() v1alpha1.ClusterThrottleInterface { + return &FakeClusterThrottles{c} +} + +func (c *FakeScheduleV1alpha1) Throttles(namespace string) v1alpha1.ThrottleInterface { + return &FakeThrottles{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeScheduleV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_throttle.go b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_throttle.go new file mode 100644 index 0000000..6fd650f --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_throttle.go @@ -0,0 +1,143 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeThrottles implements ThrottleInterface +type FakeThrottles struct { + Fake *FakeScheduleV1alpha1 + ns string +} + +var throttlesResource = schema.GroupVersionResource{Group: "schedule.k8s.everpeace.github.co", Version: "v1alpha1", Resource: "throttles"} + +var throttlesKind = schema.GroupVersionKind{Group: "schedule.k8s.everpeace.github.co", Version: "v1alpha1", Kind: "Throttle"} + +// Get takes name of the throttle, and returns the corresponding throttle object, and an error if there is any. +func (c *FakeThrottles) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Throttle, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(throttlesResource, c.ns, name), &v1alpha1.Throttle{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Throttle), err +} + +// List takes label and field selectors, and returns the list of Throttles that match those selectors. +func (c *FakeThrottles) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ThrottleList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(throttlesResource, throttlesKind, c.ns, opts), &v1alpha1.ThrottleList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.ThrottleList{ListMeta: obj.(*v1alpha1.ThrottleList).ListMeta} + for _, item := range obj.(*v1alpha1.ThrottleList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested throttles. +func (c *FakeThrottles) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(throttlesResource, c.ns, opts)) + +} + +// Create takes the representation of a throttle and creates it. Returns the server's representation of the throttle, and an error, if there is any. +func (c *FakeThrottles) Create(ctx context.Context, throttle *v1alpha1.Throttle, opts v1.CreateOptions) (result *v1alpha1.Throttle, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(throttlesResource, c.ns, throttle), &v1alpha1.Throttle{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Throttle), err +} + +// Update takes the representation of a throttle and updates it. Returns the server's representation of the throttle, and an error, if there is any. +func (c *FakeThrottles) Update(ctx context.Context, throttle *v1alpha1.Throttle, opts v1.UpdateOptions) (result *v1alpha1.Throttle, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(throttlesResource, c.ns, throttle), &v1alpha1.Throttle{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Throttle), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeThrottles) UpdateStatus(ctx context.Context, throttle *v1alpha1.Throttle, opts v1.UpdateOptions) (*v1alpha1.Throttle, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(throttlesResource, "status", c.ns, throttle), &v1alpha1.Throttle{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Throttle), err +} + +// Delete takes name of the throttle and deletes it. Returns an error if one occurs. +func (c *FakeThrottles) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(throttlesResource, c.ns, name), &v1alpha1.Throttle{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeThrottles) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(throttlesResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.ThrottleList{}) + return err +} + +// Patch applies the patch and returns the patched throttle. +func (c *FakeThrottles) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Throttle, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(throttlesResource, c.ns, name, pt, data, subresources...), &v1alpha1.Throttle{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Throttle), err +} diff --git a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/generated_expansion.go new file mode 100644 index 0000000..43c715c --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/generated_expansion.go @@ -0,0 +1,24 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type ClusterThrottleExpansion interface{} + +type ThrottleExpansion interface{} diff --git a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/schedule_client.go b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/schedule_client.go new file mode 100644 index 0000000..d797812 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/schedule_client.go @@ -0,0 +1,95 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type ScheduleV1alpha1Interface interface { + RESTClient() rest.Interface + ClusterThrottlesGetter + ThrottlesGetter +} + +// ScheduleV1alpha1Client is used to interact with features provided by the schedule.k8s.everpeace.github.co group. +type ScheduleV1alpha1Client struct { + restClient rest.Interface +} + +func (c *ScheduleV1alpha1Client) ClusterThrottles() ClusterThrottleInterface { + return newClusterThrottles(c) +} + +func (c *ScheduleV1alpha1Client) Throttles(namespace string) ThrottleInterface { + return newThrottles(c, namespace) +} + +// NewForConfig creates a new ScheduleV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*ScheduleV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &ScheduleV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new ScheduleV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *ScheduleV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new ScheduleV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *ScheduleV1alpha1Client { + return &ScheduleV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *ScheduleV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/throttle.go b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/throttle.go new file mode 100644 index 0000000..e51c45c --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/throttle.go @@ -0,0 +1,196 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + scheme "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ThrottlesGetter has a method to return a ThrottleInterface. +// A group's client should implement this interface. +type ThrottlesGetter interface { + Throttles(namespace string) ThrottleInterface +} + +// ThrottleInterface has methods to work with Throttle resources. +type ThrottleInterface interface { + Create(ctx context.Context, throttle *v1alpha1.Throttle, opts v1.CreateOptions) (*v1alpha1.Throttle, error) + Update(ctx context.Context, throttle *v1alpha1.Throttle, opts v1.UpdateOptions) (*v1alpha1.Throttle, error) + UpdateStatus(ctx context.Context, throttle *v1alpha1.Throttle, opts v1.UpdateOptions) (*v1alpha1.Throttle, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.Throttle, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ThrottleList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Throttle, err error) + ThrottleExpansion +} + +// throttles implements ThrottleInterface +type throttles struct { + client rest.Interface + ns string +} + +// newThrottles returns a Throttles +func newThrottles(c *ScheduleV1alpha1Client, namespace string) *throttles { + return &throttles{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the throttle, and returns the corresponding throttle object, and an error if there is any. +func (c *throttles) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Throttle, err error) { + result = &v1alpha1.Throttle{} + err = c.client.Get(). + Namespace(c.ns). + Resource("throttles"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Throttles that match those selectors. +func (c *throttles) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ThrottleList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ThrottleList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("throttles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested throttles. +func (c *throttles) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("throttles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a throttle and creates it. Returns the server's representation of the throttle, and an error, if there is any. +func (c *throttles) Create(ctx context.Context, throttle *v1alpha1.Throttle, opts v1.CreateOptions) (result *v1alpha1.Throttle, err error) { + result = &v1alpha1.Throttle{} + err = c.client.Post(). + Namespace(c.ns). + Resource("throttles"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(throttle). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a throttle and updates it. Returns the server's representation of the throttle, and an error, if there is any. +func (c *throttles) Update(ctx context.Context, throttle *v1alpha1.Throttle, opts v1.UpdateOptions) (result *v1alpha1.Throttle, err error) { + result = &v1alpha1.Throttle{} + err = c.client.Put(). + Namespace(c.ns). + Resource("throttles"). + Name(throttle.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(throttle). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *throttles) UpdateStatus(ctx context.Context, throttle *v1alpha1.Throttle, opts v1.UpdateOptions) (result *v1alpha1.Throttle, err error) { + result = &v1alpha1.Throttle{} + err = c.client.Put(). + Namespace(c.ns). + Resource("throttles"). + Name(throttle.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(throttle). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the throttle and deletes it. Returns an error if one occurs. +func (c *throttles) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("throttles"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *throttles) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("throttles"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched throttle. +func (c *throttles) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Throttle, err error) { + result = &v1alpha1.Throttle{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("throttles"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/generated/informers/externalversions/factory.go b/pkg/generated/informers/externalversions/factory.go new file mode 100644 index 0000000..d2641be --- /dev/null +++ b/pkg/generated/informers/externalversions/factory.go @@ -0,0 +1,181 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + reflect "reflect" + sync "sync" + time "time" + + versioned "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned" + internalinterfaces "github.com/everpeace/kube-throttler/pkg/generated/informers/externalversions/internalinterfaces" + schedule "github.com/everpeace/kube-throttler/pkg/generated/informers/externalversions/schedule" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client versioned.Interface + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc + lock sync.Mutex + defaultResync time.Duration + customResync map[reflect.Type]time.Duration + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + customResync: make(map[reflect.Type]time.Duration), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + Schedule() schedule.Interface +} + +func (f *sharedInformerFactory) Schedule() schedule.Interface { + return schedule.New(f, f.namespace, f.tweakListOptions) +} diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go new file mode 100644 index 0000000..cebe80b --- /dev/null +++ b/pkg/generated/informers/externalversions/generic.go @@ -0,0 +1,65 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + "fmt" + + v1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=schedule.k8s.everpeace.github.co, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("clusterthrottles"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Schedule().V1alpha1().ClusterThrottles().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("throttles"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Schedule().V1alpha1().Throttles().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go new file mode 100644 index 0000000..11642a9 --- /dev/null +++ b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -0,0 +1,41 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by informer-gen. DO NOT EDIT. + +package internalinterfaces + +import ( + time "time" + + versioned "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + cache "k8s.io/client-go/tools/cache" +) + +// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. +type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} + +// TweakListOptionsFunc is a function that transforms a v1.ListOptions. +type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/pkg/generated/informers/externalversions/schedule/interface.go b/pkg/generated/informers/externalversions/schedule/interface.go new file mode 100644 index 0000000..14e6227 --- /dev/null +++ b/pkg/generated/informers/externalversions/schedule/interface.go @@ -0,0 +1,47 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by informer-gen. DO NOT EDIT. + +package schedule + +import ( + internalinterfaces "github.com/everpeace/kube-throttler/pkg/generated/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/everpeace/kube-throttler/pkg/generated/informers/externalversions/schedule/v1alpha1" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1alpha1 provides access to shared informers for resources in V1alpha1. + V1alpha1() v1alpha1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1alpha1 returns a new v1alpha1.Interface. +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/generated/informers/externalversions/schedule/v1alpha1/clusterthrottle.go b/pkg/generated/informers/externalversions/schedule/v1alpha1/clusterthrottle.go new file mode 100644 index 0000000..68caa01 --- /dev/null +++ b/pkg/generated/informers/externalversions/schedule/v1alpha1/clusterthrottle.go @@ -0,0 +1,90 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + schedulev1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + versioned "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned" + internalinterfaces "github.com/everpeace/kube-throttler/pkg/generated/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/everpeace/kube-throttler/pkg/generated/listers/schedule/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ClusterThrottleInformer provides access to a shared informer and lister for +// ClusterThrottles. +type ClusterThrottleInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ClusterThrottleLister +} + +type clusterThrottleInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewClusterThrottleInformer constructs a new informer for ClusterThrottle type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewClusterThrottleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredClusterThrottleInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredClusterThrottleInformer constructs a new informer for ClusterThrottle type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredClusterThrottleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ScheduleV1alpha1().ClusterThrottles().List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ScheduleV1alpha1().ClusterThrottles().Watch(context.TODO(), options) + }, + }, + &schedulev1alpha1.ClusterThrottle{}, + resyncPeriod, + indexers, + ) +} + +func (f *clusterThrottleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredClusterThrottleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *clusterThrottleInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&schedulev1alpha1.ClusterThrottle{}, f.defaultInformer) +} + +func (f *clusterThrottleInformer) Lister() v1alpha1.ClusterThrottleLister { + return v1alpha1.NewClusterThrottleLister(f.Informer().GetIndexer()) +} diff --git a/pkg/generated/informers/externalversions/schedule/v1alpha1/interface.go b/pkg/generated/informers/externalversions/schedule/v1alpha1/interface.go new file mode 100644 index 0000000..f793e6c --- /dev/null +++ b/pkg/generated/informers/externalversions/schedule/v1alpha1/interface.go @@ -0,0 +1,53 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + internalinterfaces "github.com/everpeace/kube-throttler/pkg/generated/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // ClusterThrottles returns a ClusterThrottleInformer. + ClusterThrottles() ClusterThrottleInformer + // Throttles returns a ThrottleInformer. + Throttles() ThrottleInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// ClusterThrottles returns a ClusterThrottleInformer. +func (v *version) ClusterThrottles() ClusterThrottleInformer { + return &clusterThrottleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// Throttles returns a ThrottleInformer. +func (v *version) Throttles() ThrottleInformer { + return &throttleInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/generated/informers/externalversions/schedule/v1alpha1/throttle.go b/pkg/generated/informers/externalversions/schedule/v1alpha1/throttle.go new file mode 100644 index 0000000..bda78b1 --- /dev/null +++ b/pkg/generated/informers/externalversions/schedule/v1alpha1/throttle.go @@ -0,0 +1,91 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + schedulev1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + versioned "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned" + internalinterfaces "github.com/everpeace/kube-throttler/pkg/generated/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/everpeace/kube-throttler/pkg/generated/listers/schedule/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ThrottleInformer provides access to a shared informer and lister for +// Throttles. +type ThrottleInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ThrottleLister +} + +type throttleInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewThrottleInformer constructs a new informer for Throttle type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewThrottleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredThrottleInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredThrottleInformer constructs a new informer for Throttle type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredThrottleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ScheduleV1alpha1().Throttles(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ScheduleV1alpha1().Throttles(namespace).Watch(context.TODO(), options) + }, + }, + &schedulev1alpha1.Throttle{}, + resyncPeriod, + indexers, + ) +} + +func (f *throttleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredThrottleInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *throttleInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&schedulev1alpha1.Throttle{}, f.defaultInformer) +} + +func (f *throttleInformer) Lister() v1alpha1.ThrottleLister { + return v1alpha1.NewThrottleLister(f.Informer().GetIndexer()) +} diff --git a/pkg/generated/listers/schedule/v1alpha1/clusterthrottle.go b/pkg/generated/listers/schedule/v1alpha1/clusterthrottle.go new file mode 100644 index 0000000..f0a763a --- /dev/null +++ b/pkg/generated/listers/schedule/v1alpha1/clusterthrottle.go @@ -0,0 +1,69 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ClusterThrottleLister helps list ClusterThrottles. +// All objects returned here must be treated as read-only. +type ClusterThrottleLister interface { + // List lists all ClusterThrottles in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ClusterThrottle, err error) + // Get retrieves the ClusterThrottle from the index for a given name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.ClusterThrottle, error) + ClusterThrottleListerExpansion +} + +// clusterThrottleLister implements the ClusterThrottleLister interface. +type clusterThrottleLister struct { + indexer cache.Indexer +} + +// NewClusterThrottleLister returns a new ClusterThrottleLister. +func NewClusterThrottleLister(indexer cache.Indexer) ClusterThrottleLister { + return &clusterThrottleLister{indexer: indexer} +} + +// List lists all ClusterThrottles in the indexer. +func (s *clusterThrottleLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterThrottle, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ClusterThrottle)) + }) + return ret, err +} + +// Get retrieves the ClusterThrottle from the index for a given name. +func (s *clusterThrottleLister) Get(name string) (*v1alpha1.ClusterThrottle, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("clusterthrottle"), name) + } + return obj.(*v1alpha1.ClusterThrottle), nil +} diff --git a/pkg/generated/listers/schedule/v1alpha1/expansion_generated.go b/pkg/generated/listers/schedule/v1alpha1/expansion_generated.go new file mode 100644 index 0000000..e0676e9 --- /dev/null +++ b/pkg/generated/listers/schedule/v1alpha1/expansion_generated.go @@ -0,0 +1,32 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +// ClusterThrottleListerExpansion allows custom methods to be added to +// ClusterThrottleLister. +type ClusterThrottleListerExpansion interface{} + +// ThrottleListerExpansion allows custom methods to be added to +// ThrottleLister. +type ThrottleListerExpansion interface{} + +// ThrottleNamespaceListerExpansion allows custom methods to be added to +// ThrottleNamespaceLister. +type ThrottleNamespaceListerExpansion interface{} diff --git a/pkg/generated/listers/schedule/v1alpha1/throttle.go b/pkg/generated/listers/schedule/v1alpha1/throttle.go new file mode 100644 index 0000000..2830a77 --- /dev/null +++ b/pkg/generated/listers/schedule/v1alpha1/throttle.go @@ -0,0 +1,100 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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. + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ThrottleLister helps list Throttles. +// All objects returned here must be treated as read-only. +type ThrottleLister interface { + // List lists all Throttles in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Throttle, err error) + // Throttles returns an object that can list and get Throttles. + Throttles(namespace string) ThrottleNamespaceLister + ThrottleListerExpansion +} + +// throttleLister implements the ThrottleLister interface. +type throttleLister struct { + indexer cache.Indexer +} + +// NewThrottleLister returns a new ThrottleLister. +func NewThrottleLister(indexer cache.Indexer) ThrottleLister { + return &throttleLister{indexer: indexer} +} + +// List lists all Throttles in the indexer. +func (s *throttleLister) List(selector labels.Selector) (ret []*v1alpha1.Throttle, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Throttle)) + }) + return ret, err +} + +// Throttles returns an object that can list and get Throttles. +func (s *throttleLister) Throttles(namespace string) ThrottleNamespaceLister { + return throttleNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// ThrottleNamespaceLister helps list and get Throttles. +// All objects returned here must be treated as read-only. +type ThrottleNamespaceLister interface { + // List lists all Throttles in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Throttle, err error) + // Get retrieves the Throttle from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.Throttle, error) + ThrottleNamespaceListerExpansion +} + +// throttleNamespaceLister implements the ThrottleNamespaceLister +// interface. +type throttleNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Throttles in the indexer for a given namespace. +func (s throttleNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Throttle, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Throttle)) + }) + return ret, err +} + +// Get retrieves the Throttle from the indexer for a given namespace and name. +func (s throttleNamespaceLister) Get(name string) (*v1alpha1.Throttle, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("throttle"), name) + } + return obj.(*v1alpha1.Throttle), nil +} From 2b0ef7d6d3c217283b725adc38ea7330ccbe32a8 Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Wed, 1 Sep 2021 16:43:58 +0000 Subject: [PATCH 06/16] kube-throttler implementation and its unittests --- .gitignore | 4 +- Makefile | 31 +- cmd/kube_scheduler.go | 4 +- deploy/3-deployment.yaml | 117 ----- deploy/config.yaml | 20 + deploy/{0-crd.yaml => crd.yaml} | 270 ++++++---- deploy/deployment.yaml | 34 ++ deploy/kustomization.yaml | 14 + deploy/{1-namespace.yaml => namespace.yaml} | 0 deploy/{2-rbac.yaml => rbac.yaml} | 0 go.mod | 69 +-- go.sum | 261 ++++++---- hack/boilerplate/boilerplate.generatego.txt | 16 - hack/dev/.gitignore | 1 + hack/dev/scheduler-config.yaml.template | 25 + .../v1alpha1/clusterthrottle_selector.go | 65 +++ .../v1alpha1/clusterthrottle_selector_test.go | 111 ++++ .../v1alpha1/clusterthrottle_types.go | 26 +- pkg/apis/schedule/v1alpha1/doc.go | 2 +- pkg/apis/schedule/v1alpha1/resource_amount.go | 134 ++++- .../schedule/v1alpha1/resource_amount_test.go | 250 +++++++++ .../v1alpha1/temporary_threshold_override.go | 31 +- .../temporary_threshold_override_test.go | 102 ++++ .../schedule/v1alpha1/throttle_selector.go | 54 ++ .../v1alpha1/throttle_selector_test.go | 103 ++++ pkg/apis/schedule/v1alpha1/throttle_types.go | 79 ++- .../schedule/v1alpha1/throttle_types_test.go | 152 ++++++ .../schedule/v1alpha1/v1alpha1_suite_test.go | 75 +++ .../v1alpha1/zz_generated.deepcopy.go | 75 ++- pkg/controllers/clusterthrottle_controller.go | 489 ++++++++++++++++++ pkg/controllers/clusterthrottle_metrics.go | 129 +++++ pkg/controllers/metrics_recorder.go | 67 +++ pkg/controllers/pod_util.go | 28 + pkg/controllers/reserved_resource_ammounts.go | 139 +++++ pkg/controllers/throttle_controller.go | 468 +++++++++++++++++ pkg/controllers/throttle_metrics.go | 130 +++++ .../v1alpha1/fake/fake_clusterthrottle.go | 4 +- .../schedule/v1alpha1/fake/fake_throttle.go | 4 +- .../schedule/v1alpha1/schedule_client.go | 2 +- .../informers/externalversions/generic.go | 2 +- pkg/resourcelist/resourcelist.go | 137 +++++ pkg/resourcelist/resourcelist_test.go | 421 +++++++++++++++ pkg/scheduler_plugin/plugin.go | 193 ++++++- pkg/scheduler_plugin/plugin_args.go | 54 ++ 44 files changed, 3954 insertions(+), 438 deletions(-) delete mode 100644 deploy/3-deployment.yaml create mode 100644 deploy/config.yaml rename deploy/{0-crd.yaml => crd.yaml} (57%) create mode 100644 deploy/deployment.yaml create mode 100644 deploy/kustomization.yaml rename deploy/{1-namespace.yaml => namespace.yaml} (100%) rename deploy/{2-rbac.yaml => rbac.yaml} (100%) create mode 100644 hack/dev/.gitignore create mode 100644 hack/dev/scheduler-config.yaml.template create mode 100644 pkg/apis/schedule/v1alpha1/clusterthrottle_selector.go create mode 100644 pkg/apis/schedule/v1alpha1/clusterthrottle_selector_test.go create mode 100644 pkg/apis/schedule/v1alpha1/resource_amount_test.go create mode 100644 pkg/apis/schedule/v1alpha1/temporary_threshold_override_test.go create mode 100644 pkg/apis/schedule/v1alpha1/throttle_selector.go create mode 100644 pkg/apis/schedule/v1alpha1/throttle_selector_test.go create mode 100644 pkg/apis/schedule/v1alpha1/throttle_types_test.go create mode 100644 pkg/apis/schedule/v1alpha1/v1alpha1_suite_test.go create mode 100644 pkg/controllers/clusterthrottle_controller.go create mode 100644 pkg/controllers/clusterthrottle_metrics.go create mode 100644 pkg/controllers/metrics_recorder.go create mode 100644 pkg/controllers/pod_util.go create mode 100644 pkg/controllers/reserved_resource_ammounts.go create mode 100644 pkg/controllers/throttle_controller.go create mode 100644 pkg/controllers/throttle_metrics.go create mode 100644 pkg/resourcelist/resourcelist.go create mode 100644 pkg/resourcelist/resourcelist_test.go create mode 100644 pkg/scheduler_plugin/plugin_args.go diff --git a/.gitignore b/.gitignore index b0cd41a..328e678 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,6 @@ dist/ .history .ionide -# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,go \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,go + +.vscode/ diff --git a/Makefile b/Makefile index 489c4d2..9ea16b2 100644 --- a/Makefile +++ b/Makefile @@ -26,16 +26,21 @@ lint: fmt build: fmt lint go build -tags netgo -installsuffix netgo $(LDFLAGS) -o $(OUTDIR)/$(NAME) . +.PHONY: install +install: + kubectl apply -f ./deploy/crd.yaml + .PHONY: generate generate: codegen crd .PHONY: codegen codegen: ./hack/update-codegen.sh + $(GO_LICENSER) --licensor "Shingo Omura" .PHONY: crd crd: - $(CONTROLLER_GEN) crd paths=./pkg/apis/... output:stdout > ./deploy/0-crd.yaml + $(CONTROLLER_GEN) crd paths=./pkg/apis/... output:stdout > ./deploy/crd.yaml .PHONY: build-only build-only: @@ -112,3 +117,27 @@ GOBIN=$(DEV_TOOL_PREFIX)/bin go get $(2) ;\ rm -rf $$TMP_DIR ;\ } endef + +# +# local development +# TIPS: You can change loglevel dynamicaly: +# $ curl curl -XPUT --data 'N' localhost:10251/debug/flags/v +# +KUBECONFIG ?= $(HOME)/.kube/config +.PHONY: dev-scheduler-conf +dev-scheduler-conf: + mkdir -p .dev + KUBECONFIG=$(KUBECONFIG) envsubst < ./hack/dev/scheduler-config.yaml.template > ./hack/dev/scheduler-config.yaml + +.PHONY: dev-run +dev-run: dev-scheduler-conf + go run main.go kube-scheduler \ + --config=./hack/dev/scheduler-config.yaml \ + -v=3 + +.PHONY: dev-run-debug +dev-run-debug: dev-scheduler-conf + dlv debug --headless --listen=0.0.0.0:2345 --api-version=2 --log main.go -- kube-scheduler \ + --config=./hack/dev/scheduler-config.yaml \ + --kubeconfig=$(HOME)/.kube/config \ + --v=3 diff --git a/cmd/kube_scheduler.go b/cmd/kube_scheduler.go index 3705ad1..31ed426 100644 --- a/cmd/kube_scheduler.go +++ b/cmd/kube_scheduler.go @@ -23,7 +23,7 @@ import ( "math/rand" "time" - "github.com/everpeace/kube-throttler/pkg/scheduler_plugin" + kubethrottler "github.com/everpeace/kube-throttler/pkg/scheduler_plugin" "k8s.io/component-base/logs" "k8s.io/kubernetes/cmd/kube-scheduler/app" ) @@ -31,7 +31,7 @@ import ( func kubeSchedulerCmd() *cobra.Command { rand.Seed(time.Now().UnixNano()) command := app.NewSchedulerCommand( - app.WithPlugin(scheduler_plugin.Name, scheduler_plugin.New), + app.WithPlugin(kubethrottler.PluginName, kubethrottler.New), ) command.Short = "run kube-scheduler with kube-throttler plugin (need to enable 'KubeThrottler' plugin in config)" // TODO: once we switch everything over to Cobra commands, we can go back to calling diff --git a/deploy/3-deployment.yaml b/deploy/3-deployment.yaml deleted file mode 100644 index f5efde6..0000000 --- a/deploy/3-deployment.yaml +++ /dev/null @@ -1,117 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: kube-throttler - namespace: kube-throttler - labels: - app: kube-throttler -spec: - selector: - app: kube-throttler - ports: - - name: extender - protocol: TCP - port: 80 - targetPort: 4321 - - name: prometheus - protocol: TCP - port: 9095 - targetPort: 9095 ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: kube-throttler-application-ini - namespace: kube-throttler -data: - application.ini: | - # for jvm debug - -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 - application.conf: | - kube-throttler { - # throttler name, the throttler instance is responsible for 'Throttle's with spec.throttleName equals to the name - throttler-name = "kube-throttler" - # target scheduler names, the throttler instance only counts running pods which is responsible for this scheduler names. - target-scheduler-names = [ "my-scheduler" ] - - graceful-shutdown-duration = 30 s - ask-timeout = 10 s - host = "0.0.0.0" - port = 4321 - } - skuber { - akka { - # The ID of the dispatcher to use by Skuber. If undefined or empty the default Akka dispatcher is used. - dispatcher = "" - } - - watch { - # The idle timeout duration for any connections used by skuber `watch` requests - if null the timeout is infinite. - idle-timeout = null - } - - watch-continuously { - # Timeout that is passed to the kubernetes cluster for all list/watch calls. This limits the duration of the call, - # regardless of any activity or inactivity. - request-timeout = 1800s - - # The idle timeout for the connection before if closes due to inactivity. The idle-timeout must be a great value - # than that used for timeout-seconds. - idle-timeout = 3600s - - # The idle timeout for the connection pool used by the Watch Source (each source has its own connection pool). - # When the pool is no longer used by the source and the idle time has been exceeded the pool will shutdown and - # reclaim the unused resources. - pool-idle-timeout = 7200s - } - } - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: kube-throttler - namespace: kube-throttler - labels: - app: kube-throttler -spec: - replicas: 1 - selector: - matchLabels: - app: kube-throttler - template: - metadata: - labels: - app: kube-throttler - spec: - serviceAccountName: kube-throttler - volumes: - - name: application-ini - configMap: - name: kube-throttler-application-ini - containers: - - name: ctr - image: everpeace/kube-throttler:latest - imagePullPolicy: IfNotPresent - ports: - - name: extender - containerPort: 4321 - - name: jvm-debug - containerPort: 5005 - - name: prometheus - containerPort: 9095 - volumeMounts: - - name: application-ini - mountPath: /opt/docker/conf - livenessProbe: - httpGet: - path: /live - port: 4321 - initialDelaySeconds: 30 - periodSeconds: 30 - readinessProbe: - httpGet: - path: /ready - port: 4321 - initialDelaySeconds: 50 - periodSeconds: 50 diff --git a/deploy/config.yaml b/deploy/config.yaml new file mode 100644 index 0000000..0f94365 --- /dev/null +++ b/deploy/config.yaml @@ -0,0 +1,20 @@ +apiVersion: kubescheduler.config.k8s.io/v1beta1 +kind: KubeSchedulerConfiguration +leaderElection: + leaderElect: false # replicas: 1 +profiles: +- schedulerName: my-scheduler + plugins: + preFilter: + enabled: + - name: kube-throttler + weight: 1 + reserve: + enabled: + - name: kube-throttler + weight: 1 + pluginConfig: + - name: kube-throttler + args: + name: kube-throttler + targetSchedulerName: my-scheduler diff --git a/deploy/0-crd.yaml b/deploy/crd.yaml similarity index 57% rename from deploy/0-crd.yaml rename to deploy/crd.yaml index 496113e..c93f0c4 100644 --- a/deploy/0-crd.yaml +++ b/deploy/crd.yaml @@ -6,9 +6,9 @@ metadata: annotations: controller-gen.kubebuilder.io/version: v0.6.1 creationTimestamp: null - name: throttles.schedule.k8s.everpeace.github.co + name: throttles.schedule.k8s.everpeace.github.com spec: - group: schedule.k8s.everpeace.github.co + group: schedule.k8s.everpeace.github.com names: categories: - kube-throttler @@ -55,48 +55,67 @@ spec: spec: properties: selector: - items: - properties: - podSelector: - items: - description: A node selector requirement is a selector that - contains values, a key, and an operator that relates the - key and values. - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: Represents a key's relationship to a set - of values. Valid operators are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. If the operator - is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. If the operator is Gt or Lt, the - values array must have a single element, which will - be interpreted as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array + properties: + selectorTerms: + items: + properties: + podSelector: + description: A label selector is a label query over a set + of resources. The result of matchLabels and matchExpressions + are ANDed. An empty label selector matches all objects. + A null label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + type: object + type: array + type: object temporaryThresholdOverrides: items: properties: begin: - format: date-time type: string end: - format: date-time type: string threshold: properties: @@ -141,7 +160,6 @@ spec: throttlerName: type: string required: - - selector - threshold - throttlerName type: object @@ -228,9 +246,9 @@ metadata: annotations: controller-gen.kubebuilder.io/version: v0.6.1 creationTimestamp: null - name: clusterthrottles.schedule.k8s.everpeace.github.co + name: clusterthrottles.schedule.k8s.everpeace.github.com spec: - group: schedule.k8s.everpeace.github.co + group: schedule.k8s.everpeace.github.com names: categories: - kube-throttler @@ -277,78 +295,115 @@ spec: spec: properties: selector: - items: - properties: - namespaceSelector: - items: - description: A node selector requirement is a selector that - contains values, a key, and an operator that relates the - key and values. - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: Represents a key's relationship to a set - of values. Valid operators are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. If the operator - is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. If the operator is Gt or Lt, the - values array must have a single element, which will - be interpreted as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - podSelector: - items: - description: A node selector requirement is a selector that - contains values, a key, and an operator that relates the - key and values. - properties: - key: - description: The label key that the selector applies to. - type: string - operator: - description: Represents a key's relationship to a set - of values. Valid operators are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. If the operator - is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. If the operator is Gt or Lt, the - values array must have a single element, which will - be interpreted as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array + properties: + selectorTerms: + items: + properties: + namespaceSelector: + description: A label selector is a label query over a set + of resources. The result of matchLabels and matchExpressions + are ANDed. An empty label selector matches all objects. + A null label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + podSelector: + description: A label selector is a label query over a set + of resources. The result of matchLabels and matchExpressions + are ANDed. An empty label selector matches all objects. + A null label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + type: object + type: array + type: object temporaryThresholdOverrides: items: properties: begin: - format: date-time type: string end: - format: date-time type: string threshold: properties: @@ -393,7 +448,6 @@ spec: throttlerName: type: string required: - - selector - threshold - throttlerName type: object diff --git a/deploy/deployment.yaml b/deploy/deployment.yaml new file mode 100644 index 0000000..cc6e6e6 --- /dev/null +++ b/deploy/deployment.yaml @@ -0,0 +1,34 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kube-throttler + namespace: kube-throttler + labels: + app: kube-throttler +spec: + replicas: 1 + selector: + matchLabels: + app: kube-throttler + template: + metadata: + labels: + app: kube-throttler + spec: + serviceAccountName: kube-throttler + volumes: + - name: scheduler-config + configMap: + name: scheduler-config + containers: + - name: ctr + args: + - --config=/scheduler-config/config.yaml + - -v=3 + image: kube-throttler + imagePullPolicy: IfNotPresent + volumeMounts: + - name: scheduler-config + mountPath: /scheduler-config + diff --git a/deploy/kustomization.yaml b/deploy/kustomization.yaml new file mode 100644 index 0000000..67c455a --- /dev/null +++ b/deploy/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: kube-throttler +resources: +- crd.yaml +- namespace.yaml +- rbac.yaml +- deployment.yaml +configMapGenerator: +- name: scheduler-config + files: + - config.yaml +generatorOptions: + disableNameSuffixHash: true diff --git a/deploy/1-namespace.yaml b/deploy/namespace.yaml similarity index 100% rename from deploy/1-namespace.yaml rename to deploy/namespace.yaml diff --git a/deploy/2-rbac.yaml b/deploy/rbac.yaml similarity index 100% rename from deploy/2-rbac.yaml rename to deploy/rbac.yaml diff --git a/go.mod b/go.mod index a16da90..92ab1bf 100644 --- a/go.mod +++ b/go.mod @@ -3,40 +3,49 @@ module github.com/everpeace/kube-throttler go 1.16 require ( + github.com/google/go-cmp v0.5.5 + github.com/onsi/ginkgo v1.16.4 + github.com/onsi/gomega v1.14.0 + github.com/pkg/errors v0.9.1 + github.com/prometheus/client_golang v1.7.1 github.com/spf13/cobra v1.2.1 - k8s.io/api v0.20.5 - k8s.io/apimachinery v0.20.5 - k8s.io/client-go v0.20.5 - k8s.io/component-base v0.20.5 - k8s.io/kube-scheduler v0.20.5 // indirect + k8s.io/api v0.21.4 + k8s.io/apimachinery v0.21.4 + k8s.io/apiserver v0.21.4 + k8s.io/client-go v0.21.4 + k8s.io/component-base v0.21.4 + k8s.io/klog/v2 v2.8.0 + k8s.io/kube-scheduler v0.21.4 // indirect k8s.io/kubernetes v1.20.5 + k8s.io/utils v0.0.0-20210629042839-4a2b36d8d73f ) replace ( + // see https://github.com/kubernetes/kubernetes/blob/v1.21.4/go.mod#L475 google.golang.org/grpc => google.golang.org/grpc v1.27.1 - k8s.io/api => k8s.io/api v0.20.5 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.20.5 - k8s.io/apimachinery => k8s.io/apimachinery v0.20.5 - k8s.io/apiserver => k8s.io/apiserver v0.20.5 - k8s.io/cli-runtime => k8s.io/cli-runtime v0.20.5 - k8s.io/client-go => k8s.io/client-go v0.20.5 - k8s.io/cloud-provider => k8s.io/cloud-provider v0.20.5 - k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.20.5 - k8s.io/code-generator => k8s.io/code-generator v0.20.5 - k8s.io/component-base => k8s.io/component-base v0.20.5 - k8s.io/component-helpers => k8s.io/component-helpers v0.20.5 - k8s.io/controller-manager => k8s.io/controller-manager v0.20.5 - k8s.io/cri-api => k8s.io/cri-api v0.20.5 - k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.20.5 - k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.20.5 - k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.20.5 - k8s.io/kube-proxy => k8s.io/kube-proxy v0.20.5 - k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.20.5 - k8s.io/kubectl => k8s.io/kubectl v0.20.5 - k8s.io/kubelet => k8s.io/kubelet v0.20.5 - k8s.io/kubernetes => k8s.io/kubernetes v1.20.5 - k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.20.5 - k8s.io/metrics => k8s.io/metrics v0.20.5 - k8s.io/mount-utils => k8s.io/mount-utils v0.20.5 - k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.20.5 + k8s.io/api => k8s.io/api v0.21.4 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.21.4 + k8s.io/apimachinery => k8s.io/apimachinery v0.21.4 + k8s.io/apiserver => k8s.io/apiserver v0.21.4 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.21.4 + k8s.io/client-go => k8s.io/client-go v0.21.4 + k8s.io/cloud-provider => k8s.io/cloud-provider v0.21.4 + k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.21.4 + k8s.io/code-generator => k8s.io/code-generator v0.21.4 + k8s.io/component-base => k8s.io/component-base v0.21.4 + k8s.io/component-helpers => k8s.io/component-helpers v0.21.4 + k8s.io/controller-manager => k8s.io/controller-manager v0.21.4 + k8s.io/cri-api => k8s.io/cri-api v0.21.4 + k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.21.4 + k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.21.4 + k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.21.4 + k8s.io/kube-proxy => k8s.io/kube-proxy v0.21.4 + k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.21.4 + k8s.io/kubectl => k8s.io/kubectl v0.21.4 + k8s.io/kubelet => k8s.io/kubelet v0.21.4 + k8s.io/kubernetes => k8s.io/kubernetes v1.21.4 + k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.21.4 + k8s.io/metrics => k8s.io/metrics v0.21.4 + k8s.io/mount-utils => k8s.io/mount-utils v0.21.4 + k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.21.4 ) diff --git a/go.sum b/go.sum index 7410de3..440060f 100644 --- a/go.sum +++ b/go.sum @@ -38,15 +38,14 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v43.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= @@ -61,8 +60,9 @@ github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YH github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.10-0.20200715222032-5eafd1556990/go.mod h1:ay/0dTb7NsG8QMDfsRfLHgZo/6xAJShLe1+ePPflihk= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -106,22 +106,24 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313/go.mod h1:P1wt9Z3DP8O6W3rvwCt0REIlshg1InHImaLW0t3ObY0= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= -github.com/container-storage-interface/spec v1.2.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= +github.com/container-storage-interface/spec v1.3.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= @@ -130,7 +132,7 @@ github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8h github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/coredns/corefile-migration v1.0.10/go.mod h1:RMy/mXdeDlYwzt0vdMEJvT2hGJ2I86/eO0UdXmH9XNI= +github.com/coredns/corefile-migration v1.0.11/go.mod h1:RMy/mXdeDlYwzt0vdMEJvT2hGJ2I86/eO0UdXmH9XNI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -143,6 +145,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.1/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= @@ -150,6 +153,9 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -162,11 +168,10 @@ github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyG github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= @@ -178,6 +183,7 @@ github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= @@ -186,15 +192,17 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -203,8 +211,9 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -234,12 +243,14 @@ github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29g github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= -github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw= +github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= @@ -247,9 +258,11 @@ github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tF github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -292,13 +305,11 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= -github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/cadvisor v0.38.8/go.mod h1:1OFB9sOOMkBdUBGCO/1SArawTnDscgMzTodacVDe8mA= +github.com/google/cadvisor v0.39.0/go.mod h1:rjQFmK4jPCpxeUdLq9bYhNFFsjgGOtpnDmDeap0+nsw= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -329,6 +340,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= @@ -339,7 +351,6 @@ github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyyc github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -377,9 +388,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/heketi/heketi v9.0.1-0.20190917153846-c2e2a4ab7ab9+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o= +github.com/heketi/heketi v10.2.0+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o= github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -411,17 +421,16 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= @@ -440,18 +449,20 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -465,9 +476,11 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hxQ= -github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -475,8 +488,9 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.0.0-20200520151820-abd8a0e76976/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -485,26 +499,39 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI= +github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc92/go.mod h1:X1zlU4p7wOlX4+WRCz+hvlRv8phdL7UqbYD+vQwNMmE= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runc v1.0.0-rc95/go.mod h1:z+bZxa/+Tz/FmYVWkhUajJdzFeOqjc5vrqskhVyHGUM= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -558,12 +585,13 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= @@ -598,11 +626,12 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -618,9 +647,10 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -646,6 +676,7 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= @@ -670,8 +701,9 @@ golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -679,6 +711,7 @@ golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= @@ -686,6 +719,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20210220032938-85be41e4509f/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -702,12 +736,15 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -735,6 +772,7 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -746,6 +784,7 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -754,11 +793,14 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -806,10 +848,14 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -832,39 +878,48 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -894,8 +949,10 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -911,7 +968,6 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -921,6 +977,7 @@ golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82u golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= @@ -1028,11 +1085,11 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -1053,7 +1110,9 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -1062,6 +1121,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1069,55 +1130,55 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.5 h1:zsMTffV0Le2EiI0aKvlTHEnXGxk1HiqGRhJcCPiI7JI= -k8s.io/api v0.20.5/go.mod h1:FQjAceXnVaWDeov2YUWhOb6Yt+5UjErkp6UO3nczO1Y= -k8s.io/apiextensions-apiserver v0.20.5/go.mod h1:1HoTwgjWNizJBIgg0Y9P4RdLtaQquilJ5ArGHv9ZpFk= -k8s.io/apimachinery v0.20.5 h1:wO/FxMVRn223rAKxnBbwCyuN96bS9MFTIvP0e/V7cps= -k8s.io/apimachinery v0.20.5/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apiserver v0.20.5 h1:J8l/MJ9pdYncrc1lhxzObDa7jgrUNfettKGNF8mFV+c= -k8s.io/apiserver v0.20.5/go.mod h1:AY3lKhcJ2Tm81XvvcBzk2VnKINSoN+qczYsdo2YEvIc= -k8s.io/cli-runtime v0.20.5/go.mod h1:ihjPeQWDk7NGVIkNEvpwxA3gJvqtU+LtkDj11TvyXn4= -k8s.io/client-go v0.20.5 h1:dJGtYUvFrFGjQ+GjXEIby0gZWdlAOc0xJBJqY3VyDxA= -k8s.io/client-go v0.20.5/go.mod h1:Ee5OOMMYvlH8FCZhDsacjMlCBwetbGZETwo1OA+e6Zw= -k8s.io/cloud-provider v0.20.5 h1:vF/8qZRIfwqNQhd9gv3apZvnvTc4qcZJvYWzzZb0K08= -k8s.io/cloud-provider v0.20.5/go.mod h1:GrzNM+VAk1cy88FJPnF9F/PUPeeD5aqfIZmp2QONG7Y= -k8s.io/cluster-bootstrap v0.20.5/go.mod h1:vr2e5AAGqdWBupioz62IRLvk+SjWqAOq2J2DtIuK6Ak= -k8s.io/code-generator v0.20.5/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= -k8s.io/component-base v0.20.5 h1:8BZQKLJGhWrxtB7kIOEejKDtAKr1HOYvB0PZNeTyLS0= -k8s.io/component-base v0.20.5/go.mod h1:l0isoBLGyQKwRoTWbPHR6jNDd3/VqQD43cNlsjddGng= -k8s.io/component-helpers v0.20.5 h1:JmmGqBM7CaJRUKL6oVFoiM7BT2hE9cxg/yjrPkMhSbk= -k8s.io/component-helpers v0.20.5/go.mod h1:AzTdoPj6YAN2SUfhBX/FUUU3ntfFuse03q/VMLovEsE= -k8s.io/controller-manager v0.20.5 h1:2OZPUfW5Y7LUePa2MckvyhguVO9Ka+iE0/CnsxdOOT0= -k8s.io/controller-manager v0.20.5/go.mod h1:r6R3hxyqNz5De1apuLEJxsZ6hvf3TQPhiH+uPWZXB38= -k8s.io/cri-api v0.20.5/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/csi-translation-lib v0.20.5 h1:H8Olsd1f24fHgY011OmUkOyFCeSy/9VdHZQSNpG665Q= -k8s.io/csi-translation-lib v0.20.5/go.mod h1:KASK4nHVw/T8YW8pyMPh/sLkCpICxXN+A+Z83BplHUk= +k8s.io/api v0.21.4 h1:WtDkzTAuI31WZKDPeIYpEUA+WeUfXAmA7gwj6nzFfbc= +k8s.io/api v0.21.4/go.mod h1:fTVGP+M4D8+00FN2cMnJqk/eb/GH53bvmNs2SVTmpFk= +k8s.io/apiextensions-apiserver v0.21.4/go.mod h1:OoC8LhI9LnV+wKjZkXIBbLUwtnOGJiTRE33qctH5CIk= +k8s.io/apimachinery v0.21.4 h1:KDq0lWZVslHkuE5I7iGAQHwpK0aDTlar1E7IWEc4CNw= +k8s.io/apimachinery v0.21.4/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI= +k8s.io/apiserver v0.21.4 h1:egJgdhW0ueq5iJSY0c5YedPvRM2Ft/D3dcXOgwvs9jY= +k8s.io/apiserver v0.21.4/go.mod h1:SErUuFBBPZUcD2nsUU8hItxoYheqyYr2o/pCINEPW8g= +k8s.io/cli-runtime v0.21.4/go.mod h1:eRbLHYkdVWzvG87yrkgGd8CqX6/+fAG9DTdAqTXmlRY= +k8s.io/client-go v0.21.4 h1:tcwj167If+v+pIGrCjaPG7hFo6SqFPFCCgMJy+Vm8Jc= +k8s.io/client-go v0.21.4/go.mod h1:t0/eMKyUAq/DoQ7vW8NVVA00/nomlwC+eInsS8PxSew= +k8s.io/cloud-provider v0.21.4 h1:BPGDdyz49/ohnK3QMDWBtm39QnDm+bXIP5L7mj8AHUQ= +k8s.io/cloud-provider v0.21.4/go.mod h1:9ogsWpFKWcYC0sGPu0YZ3FMLZIlaGBSFDCNXxhlCF1o= +k8s.io/cluster-bootstrap v0.21.4/go.mod h1:GtXGuiEtdV4XQJcscR6qQCm/vtQWkhUi3qnl9KL9jzw= +k8s.io/code-generator v0.21.4/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo= +k8s.io/component-base v0.21.4 h1:Bc0AttSyhJFVXEIHz+VX+D11j/5z7SPPhl6whiXaRzs= +k8s.io/component-base v0.21.4/go.mod h1:ZKG0eHVX+tUDcaoIGpU3Vtk4TIjMddN9uhEWDmW6Nyg= +k8s.io/component-helpers v0.21.4 h1:Q6L3sQ+L5uaaUcsJkhlzU5UchcIYBZ56Y2Bq5k4qOtk= +k8s.io/component-helpers v0.21.4/go.mod h1:/5TBNWmxaAymZweO1JWv3Pt5rcYJV1LbWWY0x1rDdVU= +k8s.io/controller-manager v0.21.4/go.mod h1:a/iL7W19zkyirHDaupk9cyC11nejVznGwZI6I8tbyQY= +k8s.io/cri-api v0.21.4/go.mod h1:ukzeKnOkrG9/+ghKZA57WeZbQfRtqlGLF5GcF3RtHZ8= +k8s.io/csi-translation-lib v0.21.4 h1:BXmdC3qh9hRlOfXRNDHzkwGdACB0ZB9YGIR8LxdR+Lg= +k8s.io/csi-translation-lib v0.21.4/go.mod h1:WtxJW4/3XGhllbRCO4SRkL/MyLhjaRsL6Ds+q0pDHTg= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-aggregator v0.20.5/go.mod h1:0S88kjWs/0UzOMOko6fjy4nwu1OTRrxlpa7rsx0PErA= -k8s.io/kube-controller-manager v0.20.5/go.mod h1:oC7TO9YGTI23FDtgens9eIX8ceXntHeG8xhaPSEgAV4= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-proxy v0.20.5/go.mod h1:DBxEvwMdK9/dJHxY6+VCONxHzBMWeebPMQM0Icr0VfY= -k8s.io/kube-scheduler v0.20.5 h1:V6WUUSawUur3okzD8+ShBPt85V32FcSLHbXhfufOEqI= -k8s.io/kube-scheduler v0.20.5/go.mod h1:oCOwGvakNU458nFM1jRC5rzp1USDOFBFoie0OAEN4I8= -k8s.io/kubectl v0.20.5/go.mod h1:mlNQgyV18D4XFt5BmfSkrxQNS+arT2pXDQxxnH5lMiw= -k8s.io/kubelet v0.20.5/go.mod h1:iM18y0xm/1VlznuHFGBd9YVT9MM15TgEWJrJHrZ4mtQ= -k8s.io/kubernetes v1.20.5 h1:oY1KI7d/2rHETR3xngvQ46vuC1cNPp7R/5vQAVd2vqs= -k8s.io/kubernetes v1.20.5/go.mod h1:aOH+RZJ0PFt6Y/G3vbR0zLeGURGW8X4aX9khigekwAo= -k8s.io/legacy-cloud-providers v0.20.5/go.mod h1:YhCukXmwAh+PLncIZMMMIUD0wSZqw4UGukAKe6ZDMbI= -k8s.io/metrics v0.20.5/go.mod h1:vsptOayjKWKWHvWR1vFQY++vxydzaEo/2+JC7kSDKPU= -k8s.io/mount-utils v0.20.5 h1:S2z/6YtfT1Nc+wRxbSwkfFbVHYDFLoLmD4Nw3Nu8qQY= -k8s.io/mount-utils v0.20.5/go.mod h1:Jv9NRZ5L2LF87A17GaGlArD+r3JAJdZFvo4XD1cG4Kc= -k8s.io/sample-apiserver v0.20.5/go.mod h1:QX9q+uZk/a9+EoRTH56rpoUlgLrsBIaRJukuck27K1o= -k8s.io/system-validators v1.2.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6gRrl0Q= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= +k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= +k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/kube-aggregator v0.21.4/go.mod h1:SykygeaVEQfqYH5IV8ve7Ia3dEGOGpGrdfD5NBi5yYI= +k8s.io/kube-controller-manager v0.21.4/go.mod h1:/wPS1gIX++/WjsIiimESnkpMqsjiIAMOpjVwjqLo7ng= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= +k8s.io/kube-proxy v0.21.4/go.mod h1:eUxSO/0Z/0JjKYz/aCZdwGea7lazumkTFrqS+OWcVNI= +k8s.io/kube-scheduler v0.21.4 h1:oUVUCM+v6rum1i5vn5C3ZrqPNkp7exWiy7/Tfzbs9ZQ= +k8s.io/kube-scheduler v0.21.4/go.mod h1:zFiUfgeM/dJajfHYG8Bx5fSrNAcLxMHFgN7ARdSJXqQ= +k8s.io/kubectl v0.21.4/go.mod h1:rRYB5HeScoGQKxZDQmus17pTSVIuqfm0D31ApET/qSM= +k8s.io/kubelet v0.21.4/go.mod h1:kgXUz8upYNIngMSEZP1rpg2kp4gfUrsB7ir5u9Cm4HE= +k8s.io/kubernetes v1.21.4 h1:uKnn+MDBG4Bsed/iD3L6gMkq/szAnMqeHuSjkc3WOzQ= +k8s.io/kubernetes v1.21.4/go.mod h1:yNRsD2sfx76jpLKTgr0lJdVnILFWRo7b+HCo94tD48c= +k8s.io/legacy-cloud-providers v0.21.4/go.mod h1:WzvDvkWfD7lKQSaSqqaYsoY3VQeAjhXYN2telpMx8co= +k8s.io/metrics v0.21.4/go.mod h1:uhWoVuVumUMSeCa1B1p2tm4Y4XuZIg0n24QEtB54wuA= +k8s.io/mount-utils v0.21.4 h1:T24Y4FJ9IRkXgA+UkQHr+F+f/nm7sqdkdmdSxTtF+lw= +k8s.io/mount-utils v0.21.4/go.mod h1:dwXbIPxKtTjrBEaX1aK/CMEf1KZ8GzMHpe3NEBfdFXI= +k8s.io/sample-apiserver v0.21.4/go.mod h1:rpVLxky91DoN2OehmyZf/IE+sgop/BBoZl78VJrrs0I= +k8s.io/system-validators v1.4.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6gRrl0Q= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210629042839-4a2b36d8d73f h1:6Cyc8f2OS555SrragQyv4rQ5G7F2haZ6KY2oxO1wzlE= +k8s.io/utils v0.0.0-20210629042839-4a2b36d8d73f/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= @@ -1127,11 +1188,15 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15 h1:4uqm9Mv+w2MmBYD+F4qf/v6tDFUdPOk29C095RbU5mY= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22 h1:fmRfl9WJ4ApJn7LxNuED4m0t18qivVQOxP6aAYG9J6c= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/kustomize/api v0.8.8/go.mod h1:He1zoK0nk43Pc6NlV085xDXDXTNprtcyKZVm3swsdNY= +sigs.k8s.io/kustomize/cmd/config v0.9.10/go.mod h1:Mrby0WnRH7hA6OwOYnYpfpiY0WJIMgYrEDfwOeFdMK0= +sigs.k8s.io/kustomize/kustomize/v4 v4.1.2/go.mod h1:PxBvo4WGYlCLeRPL+ziT64wBXqbgfcalOS/SXa/tcyo= +sigs.k8s.io/kustomize/kyaml v0.10.17/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/hack/boilerplate/boilerplate.generatego.txt b/hack/boilerplate/boilerplate.generatego.txt index 156bf58..e69de29 100644 --- a/hack/boilerplate/boilerplate.generatego.txt +++ b/hack/boilerplate/boilerplate.generatego.txt @@ -1,16 +0,0 @@ -// Licensed to Shingo Omura under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Shingo Omura licenses this file to you 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. diff --git a/hack/dev/.gitignore b/hack/dev/.gitignore new file mode 100644 index 0000000..e03d3f5 --- /dev/null +++ b/hack/dev/.gitignore @@ -0,0 +1 @@ +scheduler-config.yaml diff --git a/hack/dev/scheduler-config.yaml.template b/hack/dev/scheduler-config.yaml.template new file mode 100644 index 0000000..a91bb36 --- /dev/null +++ b/hack/dev/scheduler-config.yaml.template @@ -0,0 +1,25 @@ +apiVersion: kubescheduler.config.k8s.io/v1beta1 +kind: KubeSchedulerConfiguration +leaderElection: + leaderElect: false # replicas: 1 +clientConnection: + kubeconfig: ${KUBECONFIG} +podMaxBackoffSeconds: 1 +percentageOfNodesToScore: 100 +profiles: +- schedulerName: my-scheduler + plugins: + preFilter: + enabled: + - name: kube-throttler + weight: 1 + reserve: + enabled: + - name: kube-throttler + weight: 1 + pluginConfig: + - name: kube-throttler + args: + name: kube-throttler + kubeconfig: ${KUBECONFIG} + targetSchedulerName: my-scheduler diff --git a/pkg/apis/schedule/v1alpha1/clusterthrottle_selector.go b/pkg/apis/schedule/v1alpha1/clusterthrottle_selector.go new file mode 100644 index 0000000..235ba01 --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/clusterthrottle_selector.go @@ -0,0 +1,65 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" +) + +type ClusterThrottleSelector struct { + SelecterTerms []ClusterThrottleSelectorTerm `json:"selectorTerms,omitempty"` +} + +func (s ClusterThrottleSelector) MatchesToPod(pod *corev1.Pod, ns *corev1.Namespace) (bool, error) { + // OR-ed + for _, sel := range s.SelecterTerms { + match, err := sel.MatchesToPod(pod, ns) + if err != nil { + return false, err + } + if match { + return true, nil + } + } + return false, nil +} + +type ClusterThrottleSelectorTerm struct { + ThrottleSelectorTerm `json:",inline"` + NamespaceSelector metav1.LabelSelector `json:"namespaceSelector"` +} + +func (t ClusterThrottleSelectorTerm) MatchesToPod(pod *corev1.Pod, ns *corev1.Namespace) (bool, error) { + // check namespace first + nss, err := metav1.LabelSelectorAsSelector(&t.NamespaceSelector) + if err != nil { + return false, nil + } + if !nss.Matches(labels.Set(ns.Labels)) { + return false, nil + } + + match, err := t.ThrottleSelectorTerm.MatchesToPod(pod) + if err != nil { + return false, err + } + + return match, nil +} diff --git a/pkg/apis/schedule/v1alpha1/clusterthrottle_selector_test.go b/pkg/apis/schedule/v1alpha1/clusterthrottle_selector_test.go new file mode 100644 index 0000000..a5c7505 --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/clusterthrottle_selector_test.go @@ -0,0 +1,111 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var clusterThrottleSelecterSpec = Describe("ClusterThrottleSelector.MatchesPod", func() { + var testee ClusterThrottleSelector + + Describe("Empty selector", func() { + testLabel := map[string]string{ + "test": "test", + } + BeforeEach(func() { + testee = ClusterThrottleSelector{} + }) + It("should match no pods", func() { + matched, err := testee.MatchesToPod(mkPod("test", "test").WithLabels(testLabel).Pod, mkNamespace("test", testLabel)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(matched).Should(BeFalse()) + }) + }) + Describe("Multiple SelectorTerms", func() { + test1Label := map[string]string{ + "test1": "test1", + } + test2Label := map[string]string{ + "test2": "test2", + } + BeforeEach(func() { + testee = ClusterThrottleSelector{ + SelecterTerms: []ClusterThrottleSelectorTerm{{ + NamespaceSelector: metav1.LabelSelector{ + MatchLabels: test1Label, + }, + ThrottleSelectorTerm: ThrottleSelectorTerm{ + PodSelector: metav1.LabelSelector{ + MatchLabels: test1Label, + }, + }, + }, { + NamespaceSelector: metav1.LabelSelector{ + MatchLabels: test2Label, + }, + ThrottleSelectorTerm: ThrottleSelectorTerm{ + PodSelector: metav1.LabelSelector{ + MatchLabels: test2Label, + }, + }, + }}, + } + }) + It("should be evaluated in OR-ed", func() { + var matched bool + var err error + matched, err = testee.MatchesToPod(mkPod("test1", "test1").WithLabels(test1Label).Pod, mkNamespace("test1", test1Label)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(matched).Should(BeTrue()) + matched, err = testee.MatchesToPod(mkPod("test2", "test2").WithLabels(test2Label).Pod, mkNamespace("test2", test2Label)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(matched).Should(BeTrue()) + + matched, err = testee.MatchesToPod(mkPod("test1", "test2").WithLabels(test2Label).Pod, mkNamespace("test1", test1Label)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(matched).Should(BeFalse()) + }) + }) +}) + +var clusterThrottleSelecterTermSpec = Describe("ClusterthrottleSelectorTerm.MatchesPod", func() { + var testee ClusterThrottleSelectorTerm + testLabel := map[string]string{ + "test": "test", + } + + Describe("Empty ClusterThrottleSelectorTerm", func() { + BeforeEach(func() { + testee = ClusterThrottleSelectorTerm{} + }) + It("should match any namespace and pods", func() { + var matched bool + var err error + matched, err = testee.MatchesToPod(mkPod("test1", "test1").WithLabels(testLabel).Pod, mkNamespace("test1", testLabel)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(matched).Should(BeTrue()) + + matched, err = testee.MatchesToPod(mkPod("test1", "test1").Pod, mkNamespace("test1", nil)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(matched).Should(BeTrue()) + }) + }) +}) diff --git a/pkg/apis/schedule/v1alpha1/clusterthrottle_types.go b/pkg/apis/schedule/v1alpha1/clusterthrottle_types.go index 400f48f..4e60ef5 100644 --- a/pkg/apis/schedule/v1alpha1/clusterthrottle_types.go +++ b/pkg/apis/schedule/v1alpha1/clusterthrottle_types.go @@ -22,15 +22,27 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -type ClusterThrottleSelectorTerm struct { - ThrottleSelectorTerm `json:",inline"` - NamespaceSelector []corev1.NodeSelectorRequirement `json:"namespaceSelector"` -} - type ClusterThrottleSpec struct { ThrottleSpecBase `json:",inline"` - // +kubebuilder:validation:Required - Selector []ClusterThrottleSelectorTerm `json:"selector,omitempty"` + Selector ClusterThrottleSelector `json:"selector,omitempty"` +} + +func (thr ClusterThrottle) CheckThrottledFor(pod *corev1.Pod, reservedResourceAmount ResourceAmount, isThrottledOnEqual bool) CheckThrottleStatus { + if thr.Status.Throttled.IsThrottledFor(pod) { + return CheckThrottleStatusActive + } + + used := ResourceAmount{}.Add(thr.Status.Used).Add(ResourceAmountOfPod(pod)).Add(reservedResourceAmount) + threshold := thr.Spec.Threshold + if !thr.Status.CalculatedThreshold.CalculatedAt.Time.IsZero() { + threshold = thr.Status.CalculatedThreshold.Threshold + } + + if threshold.IsThrottled(used, isThrottledOnEqual).IsThrottledFor(pod) { + return CheckThrottleStatusInsufficient + } + + return CheckThrottleStatusNotThrottled } // +genclient diff --git a/pkg/apis/schedule/v1alpha1/doc.go b/pkg/apis/schedule/v1alpha1/doc.go index 55408b1..bed96a2 100644 --- a/pkg/apis/schedule/v1alpha1/doc.go +++ b/pkg/apis/schedule/v1alpha1/doc.go @@ -16,7 +16,7 @@ // under the License. // +k8s:deepcopy-gen=package -// +groupName=schedule.k8s.everpeace.github.co +// +groupName=schedule.k8s.everpeace.github.com // +kubebuilder:validation:Optional package v1alpha1 diff --git a/pkg/apis/schedule/v1alpha1/resource_amount.go b/pkg/apis/schedule/v1alpha1/resource_amount.go index 2a2b4cc..706bf19 100644 --- a/pkg/apis/schedule/v1alpha1/resource_amount.go +++ b/pkg/apis/schedule/v1alpha1/resource_amount.go @@ -17,22 +17,144 @@ package v1alpha1 -import corev1 "k8s.io/api/core/v1" +import ( + "encoding/json" + + rl "github.com/everpeace/kube-throttler/pkg/resourcelist" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" +) type ResourceAmount struct { ResourceCounts *ResourceCounts `json:"resourceCounts,omitempty"` - ResourceRequests corev1.ResourceList `json:"resourceRequests,omitempty"` + ResourceRequests corev1.ResourceList `json:"resourceRequests"` } type ResourceCounts struct { - Pod int `json:"pod,omitempty"` + Pod int `json:"pod"` } type IsResourceAmountThrottled struct { - ResourceCounts IsResourceCountThrottled `json:"resourceCounts,omitempty"` - ResourceRequests map[corev1.ResourceName]bool `json:"resourceRequests,omitempty"` + ResourceCounts IsResourceCountThrottled `json:"resourceCounts"` + ResourceRequests map[corev1.ResourceName]bool `json:"resourceRequests"` +} + +func (t IsResourceAmountThrottled) IsThrottledFor(pod *corev1.Pod) bool { + if t.ResourceCounts.Pod { + return true + } + podResourceAmount := ResourceAmountOfPod(pod) + for rn, rq := range podResourceAmount.ResourceRequests { + if rq.IsZero() { + continue + } + + throttled, ok := t.ResourceRequests[rn] + if !ok { + continue + } + if throttled { + return true + } + } + return false } type IsResourceCountThrottled struct { - Pod bool `json:"pod,omitempty"` + Pod bool `json:"pod"` +} + +func ResourceAmountOfPod(pod *corev1.Pod) ResourceAmount { + return ResourceAmount{ + ResourceCounts: &ResourceCounts{Pod: 1}, + ResourceRequests: corev1.ResourceList(rl.PodRequestResourceList(pod)), + } +} + +func (a ResourceCounts) Add(b ResourceCounts) ResourceCounts { + a.Pod += b.Pod + return a +} + +func (a ResourceCounts) Sub(b ResourceCounts) ResourceCounts { + a.Pod -= b.Pod + if a.Pod < 0 { + a.Pod = 0 + } + return a +} + +func (a ResourceAmount) Add(b ResourceAmount) ResourceAmount { + if a.ResourceRequests == nil { + a.ResourceRequests = corev1.ResourceList{} + } + + if a.ResourceCounts == nil { + if b.ResourceCounts != nil { + a.ResourceCounts = b.ResourceCounts.DeepCopy() + } + } else { + if b.ResourceCounts != nil { + added := a.ResourceCounts.Add(*b.ResourceCounts) + a.ResourceCounts = &added + } + } + + rl.ResourceList(a.ResourceRequests).Add(rl.ResourceList(b.ResourceRequests)) + + return a +} + +func (a ResourceAmount) Sub(b ResourceAmount) ResourceAmount { + if a.ResourceRequests == nil { + a.ResourceRequests = corev1.ResourceList{} + } + + if a.ResourceCounts != nil && b.ResourceCounts != nil { + subed := a.ResourceCounts.Sub(*b.ResourceCounts) + a.ResourceCounts = &subed + } + + rl.ResourceList(a.ResourceRequests).Sub(rl.ResourceList(b.ResourceRequests)) + + return a +} + +func (threshold ResourceAmount) IsThrottled(used ResourceAmount, isThrottledOnEqual bool) IsResourceAmountThrottled { + isThrottledQuantityFunc := func(used, threshold resource.Quantity) bool { + if isThrottledOnEqual { + return used.Cmp(threshold) >= 0 + } + return used.Cmp(threshold) > 0 + } + + isThrottledCountsFunc := func(used, threshold int) bool { + if isThrottledOnEqual { + return used >= threshold + } + return used > threshold + } + + isThrottled := IsResourceAmountThrottled{} + if threshold.ResourceCounts != nil && used.ResourceCounts != nil { + isThrottled.ResourceCounts.Pod = isThrottledCountsFunc(used.ResourceCounts.Pod, threshold.ResourceCounts.Pod) + } + + for rn, qt := range threshold.ResourceRequests { + if isThrottled.ResourceRequests == nil { + isThrottled.ResourceRequests = map[corev1.ResourceName]bool{} + } + if qu, ok := used.ResourceRequests[rn]; ok { + isThrottled.ResourceRequests[rn] = isThrottledQuantityFunc(qu, qt) + } else { + isThrottled.ResourceRequests[rn] = false + } + } + + return isThrottled +} + +func (a ResourceAmount) String() string { + str, _ := json.Marshal(&a) + return string(str) } diff --git a/pkg/apis/schedule/v1alpha1/resource_amount_test.go b/pkg/apis/schedule/v1alpha1/resource_amount_test.go new file mode 100644 index 0000000..be3044e --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/resource_amount_test.go @@ -0,0 +1,250 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" +) + +var resourceAmountSpec = Describe("ResourceAmount.IsThrottled", func() { + var testee ResourceAmount + When("Empty", func() { + BeforeEach(func() { + testee = ResourceAmount{} + }) + It("should not be throttled at all for any used ResourceCounts", func() { + for _, b := range []bool{false, true} { + Expect(testee.IsThrottled(ResourceAmount{ + ResourceCounts: &ResourceCounts{ + Pod: 3, + }, + }, b)).Should(Equal( + IsResourceAmountThrottled{ + ResourceCounts: IsResourceCountThrottled{ + Pod: false, + }, + }, + )) + } + }) + It("should not be throttled at all for any used ResourceRequests", func() { + for _, b := range []bool{false, true} { + Expect(testee.IsThrottled(ResourceAmount{ + ResourceRequests: corev1.ResourceList{ + "r1": resource.MustParse("1000"), + }, + }, b)).Should(Equal( + IsResourceAmountThrottled{}, + )) + } + }) + }) + When("Both ResourceCount and ResourceRequests is not nil", func() { + resourceRequestsFalse := map[corev1.ResourceName]bool{ + "r1": false, + "r2": false, + } + BeforeEach(func() { + testee = ResourceAmount{ + ResourceCounts: &ResourceCounts{Pod: 3}, + ResourceRequests: corev1.ResourceList{ + "r1": resource.MustParse("10"), + "r2": resource.MustParse("20"), + }, + } + }) + It("should be throttled for used resourcecounts to its thresold", func() { + for _, b := range []bool{false, true} { + Expect(testee.IsThrottled(ResourceAmount{ + ResourceCounts: &ResourceCounts{Pod: 2}, + }, b)).Should(Equal( + IsResourceAmountThrottled{ + ResourceCounts: IsResourceCountThrottled{Pod: false}, + ResourceRequests: resourceRequestsFalse, + }, + )) + } + + Expect(testee.IsThrottled(ResourceAmount{ + ResourceCounts: &ResourceCounts{Pod: 3}, + }, false)).Should(Equal( + IsResourceAmountThrottled{ + ResourceCounts: IsResourceCountThrottled{Pod: false}, + ResourceRequests: resourceRequestsFalse, + }, + )) + Expect(testee.IsThrottled(ResourceAmount{ + ResourceCounts: &ResourceCounts{Pod: 3}, + }, true)).Should(Equal( + IsResourceAmountThrottled{ + ResourceCounts: IsResourceCountThrottled{Pod: true}, + ResourceRequests: resourceRequestsFalse, + }, + )) + + for _, b := range []bool{false, true} { + Expect(testee.IsThrottled(ResourceAmount{ + ResourceCounts: &ResourceCounts{Pod: 4}, + }, b)).Should(Equal( + IsResourceAmountThrottled{ + ResourceCounts: IsResourceCountThrottled{Pod: true}, + ResourceRequests: resourceRequestsFalse, + }, + )) + } + }) + It("should be throttled for used resourcerequests in threshold that are greater or equal to its threshold", func() { + for _, b := range []bool{false, true} { + Expect(testee.IsThrottled(ResourceAmount{ + ResourceRequests: corev1.ResourceList{ + "r1": resource.MustParse("1"), + "r2": resource.MustParse("2"), + }, + }, b)).Should(Equal( + IsResourceAmountThrottled{ + ResourceRequests: resourceRequestsFalse, + }, + )) + } + + Expect(testee.IsThrottled(ResourceAmount{ + ResourceRequests: corev1.ResourceList{ + "r1": resource.MustParse("10"), + "r2": resource.MustParse("20"), + }, + }, false)).Should(Equal( + IsResourceAmountThrottled{ + ResourceRequests: map[corev1.ResourceName]bool{ + "r1": false, + "r2": false, + }, + }, + )) + Expect(testee.IsThrottled(ResourceAmount{ + ResourceRequests: corev1.ResourceList{ + "r1": resource.MustParse("10"), + "r2": resource.MustParse("20"), + }, + }, true)).Should(Equal( + IsResourceAmountThrottled{ + ResourceRequests: map[corev1.ResourceName]bool{ + "r1": true, + "r2": true, + }, + }, + )) + for _, b := range []bool{false, true} { + Expect(testee.IsThrottled(ResourceAmount{ + ResourceRequests: corev1.ResourceList{ + "r1": resource.MustParse("11"), + "r2": resource.MustParse("22"), + }, + }, b)).Should(Equal( + IsResourceAmountThrottled{ + ResourceRequests: map[corev1.ResourceName]bool{ + "r1": true, + "r2": true, + }, + }, + )) + } + Expect(testee.IsThrottled(ResourceAmount{ + ResourceRequests: corev1.ResourceList{ + "r1": resource.MustParse("1"), + "r2": resource.MustParse("20"), + }, + }, false)).Should(Equal( + IsResourceAmountThrottled{ + ResourceRequests: map[corev1.ResourceName]bool{ + "r1": false, + "r2": false, + }, + }, + )) + Expect(testee.IsThrottled(ResourceAmount{ + ResourceRequests: corev1.ResourceList{ + "r1": resource.MustParse("1"), + "r2": resource.MustParse("20"), + }, + }, true)).Should(Equal( + IsResourceAmountThrottled{ + ResourceRequests: map[corev1.ResourceName]bool{ + "r1": false, + "r2": true, + }, + }, + )) + }) + It("should not be throttled for used resourcerequests not in its threshold", func() { + for _, b := range []bool{false, true} { + Expect(testee.IsThrottled(ResourceAmount{ + ResourceRequests: corev1.ResourceList{ + "r3": resource.MustParse("3000"), + }, + }, b)).Should(Equal( + IsResourceAmountThrottled{ + ResourceRequests: resourceRequestsFalse, + }, + )) + } + }) + }) +}) + +var isResourceAmountThrottledSpec = Describe("IsResourceAmountThrottled.IsThrottled", func() { + var testee IsResourceAmountThrottled + When("ResourceCounts is throttled", func() { + BeforeEach(func() { + testee = IsResourceAmountThrottled{ + ResourceCounts: IsResourceCountThrottled{Pod: true}, + } + }) + It("should return true for any pod", func() { + Expect(testee.IsThrottledFor(mkPod("test", "test").Pod)).Should(BeTrue()) + }) + }) + When("ResourceCount is not throttled, and ResourceRequests defines r1 and r2", func() { + BeforeEach(func() { + testee = IsResourceAmountThrottled{ + ResourceRequests: map[corev1.ResourceName]bool{ + "r1": false, + "r2": true, + }, + } + }) + It("should be true pod requesting positive amount of throttled resource", func() { + Expect(testee.IsThrottledFor(mkPod("test", "test").WithRequests(corev1.ResourceList{ + "r2": resource.MustParse("0"), + }).Pod)).Should(BeFalse()) + Expect(testee.IsThrottledFor(mkPod("test", "test").WithRequests(corev1.ResourceList{ + "r2": resource.MustParse("1"), + }).Pod)).Should(BeTrue()) + + Expect(testee.IsThrottledFor(mkPod("test", "test").WithRequests(corev1.ResourceList{ + "r1": resource.MustParse("1000"), + }).Pod)).Should(BeFalse()) + Expect(testee.IsThrottledFor(mkPod("test", "test").WithRequests(corev1.ResourceList{ + "r3": resource.MustParse("1000"), + }).Pod)).Should(BeFalse()) + }) + + }) +}) diff --git a/pkg/apis/schedule/v1alpha1/temporary_threshold_override.go b/pkg/apis/schedule/v1alpha1/temporary_threshold_override.go index 4174296..e8d45f8 100644 --- a/pkg/apis/schedule/v1alpha1/temporary_threshold_override.go +++ b/pkg/apis/schedule/v1alpha1/temporary_threshold_override.go @@ -17,11 +17,36 @@ package v1alpha1 -import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +import ( + "time" + + "github.com/pkg/errors" +) type TemporaryThresholdOverride struct { - Begin metav1.Time `json:"begin"` - EndStr metav1.Time `json:"end"` + Begin string `json:"begin"` + End string `json:"end"` // +kubebuilder:validation:Required Threshold ResourceAmount `json:"threshold"` } + +func (o TemporaryThresholdOverride) IsActive(now time.Time) (bool, error) { + var err error + var beginTime, endTime time.Time + if o.Begin != "" { + beginTime, err = time.Parse(time.RFC3339, o.Begin) + if err != nil { + return false, errors.Wrap(err, "Failed to parse Begin") + } + } + if o.End != "" { + endTime, err = time.Parse(time.RFC3339, o.End) + if err != nil { + return false, errors.Wrap(err, "Failed to parse End") + } + } + + begin := (beginTime.Before(now) || beginTime.Equal(now)) + end := (endTime.IsZero() || (now.Before(endTime) || now.Equal(endTime))) + return begin && end, nil +} diff --git a/pkg/apis/schedule/v1alpha1/temporary_threshold_override_test.go b/pkg/apis/schedule/v1alpha1/temporary_threshold_override_test.go new file mode 100644 index 0000000..3afb213 --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/temporary_threshold_override_test.go @@ -0,0 +1,102 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import ( + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("TemporaryThresholdOverride", func() { + var testee TemporaryThresholdOverride + var begin, end string + var beginTime, endTime time.Time + BeforeEach(func() { + var err error + begin = "2021-08-04T10:00:00Z" + beginTime, err = time.Parse(time.RFC3339, begin) + Expect(err).ShouldNot(HaveOccurred()) + end = "2021-08-05T10:00:00Z" + endTime, err = time.Parse(time.RFC3339, end) + Expect(err).ShouldNot(HaveOccurred()) + }) + Describe("IsActive", func() { + When("Empty Begin/End", func() { + It("should always active", func() { + testee = TemporaryThresholdOverride{} + Expect(testee.IsActive(time.Time{})).Should(BeTrue()) + Expect(testee.IsActive(beginTime)).Should(BeTrue()) + Expect(testee.IsActive(endTime)).Should(BeTrue()) + }) + }) + When("Begin only", func() { + It("should active at Begin <= t", func() { + testee = TemporaryThresholdOverride{ + Begin: begin, + } + Expect(testee.IsActive(beginTime.Add(-1 * time.Second))).Should(BeFalse()) + Expect(testee.IsActive(beginTime)).Should(BeTrue()) + Expect(testee.IsActive(beginTime.Add(1 * time.Second))).Should(BeTrue()) + Expect(testee.IsActive(beginTime.Add(65535 * time.Hour))).Should(BeTrue()) + }) + }) + When("End only", func() { + It("should active at t <= End", func() { + testee = TemporaryThresholdOverride{ + End: end, + } + Expect(testee.IsActive(endTime.Add(-65535 * time.Hour))).Should(BeTrue()) + Expect(testee.IsActive(endTime.Add(-1 * time.Second))).Should(BeTrue()) + Expect(testee.IsActive(endTime)).Should(BeTrue()) + Expect(testee.IsActive(endTime.Add(1 * time.Second))).Should(BeFalse()) + }) + }) + When("Begin and End", func() { + It("should active at Begin <= t <= End", func() { + testee = TemporaryThresholdOverride{ + Begin: begin, + End: end, + } + Expect(testee.IsActive(beginTime.Add(-1 * time.Second))).Should(BeFalse()) + Expect(testee.IsActive(beginTime)).Should(BeTrue()) + Expect(testee.IsActive(beginTime.Add(1 * time.Second))).Should(BeTrue()) + Expect(testee.IsActive(endTime.Add(-1 * time.Second))).Should(BeTrue()) + Expect(testee.IsActive(endTime)).Should(BeTrue()) + Expect(testee.IsActive(endTime.Add(1 * time.Second))).Should(BeFalse()) + }) + }) + When("Begin/End failed to parse", func() { + It("should raise error", func() { + var err error + testee = TemporaryThresholdOverride{ + Begin: "not-time", + } + _, err = testee.IsActive(time.Time{}) + Expect(err).Should(HaveOccurred()) + + testee = TemporaryThresholdOverride{ + End: "not-time", + } + _, err = testee.IsActive(time.Time{}) + Expect(err).Should(HaveOccurred()) + }) + }) + }) +}) diff --git a/pkg/apis/schedule/v1alpha1/throttle_selector.go b/pkg/apis/schedule/v1alpha1/throttle_selector.go new file mode 100644 index 0000000..ba0e1f4 --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/throttle_selector.go @@ -0,0 +1,54 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" +) + +type ThrottleSelector struct { + SelecterTerms []ThrottleSelectorTerm `json:"selectorTerms,omitempty"` +} + +func (s ThrottleSelector) MatchesToPod(pod *corev1.Pod) (bool, error) { + // OR-ed + for _, sel := range s.SelecterTerms { + match, err := sel.MatchesToPod(pod) + if err != nil { + return false, err + } + if match { + return true, nil + } + } + return false, nil +} + +type ThrottleSelectorTerm struct { + PodSelector metav1.LabelSelector `json:"podSelector"` +} + +func (t ThrottleSelectorTerm) MatchesToPod(pod *corev1.Pod) (bool, error) { + selector, err := metav1.LabelSelectorAsSelector(&t.PodSelector) + if err != nil { + return false, err + } + return selector.Matches(labels.Set(pod.Labels)), nil +} diff --git a/pkg/apis/schedule/v1alpha1/throttle_selector_test.go b/pkg/apis/schedule/v1alpha1/throttle_selector_test.go new file mode 100644 index 0000000..fc23c64 --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/throttle_selector_test.go @@ -0,0 +1,103 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var throttleSelecterSpec = Describe("ThrottleSelector.MatchesPod", func() { + var testee ThrottleSelector + + Describe("Empty selector", func() { + testLabel := map[string]string{ + "test": "test", + } + BeforeEach(func() { + testee = ThrottleSelector{} + }) + It("should match no pods", func() { + matched, err := testee.MatchesToPod(mkPod("test", "test").WithLabels(testLabel).Pod) + Expect(err).ShouldNot(HaveOccurred()) + Expect(matched).Should(BeFalse()) + }) + }) + Describe("Multiple SelectorTerms", func() { + test1Label := map[string]string{ + "test1": "test1", + } + test2Label := map[string]string{ + "test2": "test2", + } + BeforeEach(func() { + testee = ThrottleSelector{ + SelecterTerms: []ThrottleSelectorTerm{{ + PodSelector: metav1.LabelSelector{ + MatchLabels: test1Label, + }, + }, { + PodSelector: metav1.LabelSelector{ + MatchLabels: test2Label, + }, + }}, + } + }) + It("should be evaluated in OR-ed", func() { + var matched bool + var err error + matched, err = testee.MatchesToPod(mkPod("test1", "test1").WithLabels(test1Label).Pod) + Expect(err).ShouldNot(HaveOccurred()) + Expect(matched).Should(BeTrue()) + matched, err = testee.MatchesToPod(mkPod("test2", "test2").WithLabels(test2Label).Pod) + Expect(err).ShouldNot(HaveOccurred()) + Expect(matched).Should(BeTrue()) + + matched, err = testee.MatchesToPod(mkPod("test1", "test2").WithLabels(map[string]string{ + "test1": "test2", + }).Pod) + Expect(err).ShouldNot(HaveOccurred()) + Expect(matched).Should(BeFalse()) + }) + }) +}) + +var throttleSelecterTermSpec = Describe("ThrottleSelectorTerm.MatchesPod", func() { + var testee ThrottleSelectorTerm + testLabel := map[string]string{ + "test": "test", + } + + Describe("Empty ThrottleSelectorTerm", func() { + BeforeEach(func() { + testee = ThrottleSelectorTerm{} + }) + It("should match and pods", func() { + var matched bool + var err error + matched, err = testee.MatchesToPod(mkPod("test1", "test1").WithLabels(testLabel).Pod) + Expect(err).ShouldNot(HaveOccurred()) + Expect(matched).Should(BeTrue()) + + matched, err = testee.MatchesToPod(mkPod("test1", "test1").Pod) + Expect(err).ShouldNot(HaveOccurred()) + Expect(matched).Should(BeTrue()) + }) + }) +}) diff --git a/pkg/apis/schedule/v1alpha1/throttle_types.go b/pkg/apis/schedule/v1alpha1/throttle_types.go index 599a020..b4352f9 100644 --- a/pkg/apis/schedule/v1alpha1/throttle_types.go +++ b/pkg/apis/schedule/v1alpha1/throttle_types.go @@ -18,14 +18,13 @@ package v1alpha1 import ( + "time" + + "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -type ThrottleSelectorTerm struct { - PodSelector []corev1.NodeSelectorRequirement `json:"podSelector"` -} - type ThrottleSpecBase struct { // +kubebuilder:validation:Required ThrottlerName string `json:"throttlerName,omitempty"` @@ -35,10 +34,52 @@ type ThrottleSpecBase struct { TemporaryThresholdOverrides []TemporaryThresholdOverride `json:"temporaryThresholdOverrides,omitempty"` } +func (b ThrottleSpecBase) CalculateThreshold(now time.Time) CalculatedThreshold { + calculated := CalculatedThreshold{ + CalculatedAt: metav1.Time{Time: now}, + Threshold: b.Threshold, + } + + activeFound := false + overrideResult := ResourceAmount{ + ResourceRequests: corev1.ResourceList{}, + } + errMessages := []string{} + for i, o := range b.TemporaryThresholdOverrides { + isActive, err := o.IsActive(now) + if err != nil { + errMessages = append(errMessages, errors.Wrapf(err, "index %d", i).Error()) + continue + } + if isActive { + activeFound = true + // merging active overrides + // the first override will take effect for each resource count/request + if overrideResult.ResourceCounts == nil && o.Threshold.ResourceCounts != nil { + overrideResult.ResourceCounts = o.Threshold.ResourceCounts.DeepCopy() + } + for rn, rq := range o.Threshold.ResourceRequests { + if _, ok := overrideResult.ResourceRequests[rn]; !ok { + overrideResult.ResourceRequests[rn] = rq + } + } + } + } + if activeFound { + calculated.Threshold = overrideResult + } + + if len(errMessages) > 0 { + calculated.Messages = errMessages + return calculated + } + + return calculated +} + type ThrottleSpec struct { ThrottleSpecBase `json:",inline"` - // +kubebuilder:validation:Required - Selector []ThrottleSelectorTerm `json:"selector,omitempty"` + Selector ThrottleSelector `json:"selector,omitempty"` } type ThrottleStatus struct { @@ -47,6 +88,32 @@ type ThrottleStatus struct { Used ResourceAmount `json:"used,omitempty"` } +type CheckThrottleStatus string + +var ( + CheckThrottleStatusNotThrottled CheckThrottleStatus = "not-throttled" + CheckThrottleStatusActive CheckThrottleStatus = "active" + CheckThrottleStatusInsufficient CheckThrottleStatus = "insufficient" +) + +func (thr Throttle) CheckThrottledFor(pod *corev1.Pod, reservedResourceAmount ResourceAmount, isThrottledOnEqual bool) CheckThrottleStatus { + if thr.Status.Throttled.IsThrottledFor(pod) { + return CheckThrottleStatusActive + } + + used := ResourceAmount{}.Add(thr.Status.Used).Add(ResourceAmountOfPod(pod)).Add(reservedResourceAmount) + threshold := thr.Spec.Threshold + if !thr.Status.CalculatedThreshold.CalculatedAt.Time.IsZero() { + threshold = thr.Status.CalculatedThreshold.Threshold + } + + if threshold.IsThrottled(used, isThrottledOnEqual).IsThrottledFor(pod) { + return CheckThrottleStatusInsufficient + } + + return CheckThrottleStatusNotThrottled +} + // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=kube-throttler,shortName=thr;thrs diff --git a/pkg/apis/schedule/v1alpha1/throttle_types_test.go b/pkg/apis/schedule/v1alpha1/throttle_types_test.go new file mode 100644 index 0000000..6fad0c0 --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/throttle_types_test.go @@ -0,0 +1,152 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import ( + "time" + + "github.com/google/go-cmp/cmp" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var _ = Describe("ThrottleSpecBase.CalculateThreshold", func() { + const NOW = "2006-01-02T15:04:05Z" + var now time.Time + var t ThrottleSpecBase + var threshold ResourceAmount + var override1, override2, erroredOverride TemporaryThresholdOverride + equalToWithCmp := func(expected CalculatedThreshold) func(actual CalculatedThreshold) bool { + return func(actual CalculatedThreshold) bool { + return cmp.Equal(actual, expected) + } + } + BeforeEach(func() { + var err error + now, err = time.Parse(time.RFC3339, NOW) + Expect(err).NotTo(HaveOccurred()) + threshold = ResourceAmount{ + ResourceCounts: &ResourceCounts{ + Pod: 0, + }, + ResourceRequests: map[v1.ResourceName]resource.Quantity{ + v1.ResourceCPU: resource.MustParse("1"), + }, + } + override1 = TemporaryThresholdOverride{ + Begin: now.Add(time.Minute * -1).Format(time.RFC3339), + End: now.Add(time.Minute * 1).Format(time.RFC3339), + Threshold: ResourceAmount{ + ResourceCounts: &ResourceCounts{ + Pod: 2, + }, + ResourceRequests: map[v1.ResourceName]resource.Quantity{ + v1.ResourceCPU: resource.MustParse("2"), + }, + }, + } + override2 = TemporaryThresholdOverride{ + Begin: now.Add(time.Minute * -1).Format(time.RFC3339), + End: now.Add(time.Minute * 1).Format(time.RFC3339), + Threshold: ResourceAmount{ + ResourceCounts: &ResourceCounts{ + Pod: 3, + }, + ResourceRequests: map[v1.ResourceName]resource.Quantity{ + v1.ResourceCPU: resource.MustParse("3"), + v1.ResourceMemory: resource.MustParse("3"), + }, + }, + } + erroredOverride = TemporaryThresholdOverride{Begin: "error", End: "error"} + }) + When("No active Spec.TmporaryThresholdOverrides exist", func() { + BeforeEach(func() { + t = ThrottleSpecBase{ + ThrottlerName: "dummy", + Threshold: threshold, + } + }) + It("should return spec.threshold", func() { + Expect(t.CalculateThreshold(now)).Should(Satisfy(equalToWithCmp(CalculatedThreshold{ + Threshold: threshold, + CalculatedAt: metav1.Time{Time: now}, + }))) + }) + }) + When("Single active Spec.TemporaryThresholdOverrides exists", func() { + BeforeEach(func() { + t = ThrottleSpecBase{ + ThrottlerName: "dummy", + Threshold: threshold, + TemporaryThresholdOverrides: []TemporaryThresholdOverride{override1}, + } + }) + It("should return the active one's threshold", func() { + Expect(t.CalculateThreshold(now)).Should(Satisfy(equalToWithCmp(CalculatedThreshold{ + Threshold: override1.Threshold, + CalculatedAt: metav1.Time{Time: now}, + }))) + }) + }) + When("Multiple active Spec.TemporaryThresholdOverrides exists", func() { + BeforeEach(func() { + t = ThrottleSpecBase{ + ThrottlerName: "dummy", + Threshold: threshold, + TemporaryThresholdOverrides: []TemporaryThresholdOverride{override1, override2}, + } + }) + It("should return merged active ones' threshold", func() { + Expect(t.CalculateThreshold(now)).Should(Satisfy(equalToWithCmp(CalculatedThreshold{ + // merged (first match wins for eace ResourceCounts/ResourceRequests) + Threshold: ResourceAmount{ + ResourceCounts: &ResourceCounts{ + Pod: 2, + }, + ResourceRequests: map[v1.ResourceName]resource.Quantity{ + v1.ResourceCPU: resource.MustParse("2"), + v1.ResourceMemory: resource.MustParse("3"), + }, + }, + CalculatedAt: metav1.Time{Time: now}, + }))) + }) + }) + When("Including some error Spec.TemporaryThresholdOverrides exist", func() { + BeforeEach(func() { + t = ThrottleSpecBase{ + ThrottlerName: "dummy", + Threshold: threshold, + TemporaryThresholdOverrides: []TemporaryThresholdOverride{override1, erroredOverride}, + } + }) + It("should skip the errored override", func() { + Expect(t.CalculateThreshold(now)).Should(Satisfy(equalToWithCmp(CalculatedThreshold{ + Threshold: override1.Threshold, + CalculatedAt: metav1.Time{Time: now}, + Messages: []string{ + `index 1: Failed to parse Begin: parsing time "error" as "2006-01-02T15:04:05Z07:00": cannot parse "error" as "2006"`, + }, + }))) + }) + }) +}) diff --git a/pkg/apis/schedule/v1alpha1/v1alpha1_suite_test.go b/pkg/apis/schedule/v1alpha1/v1alpha1_suite_test.go new file mode 100644 index 0000000..c842592 --- /dev/null +++ b/pkg/apis/schedule/v1alpha1/v1alpha1_suite_test.go @@ -0,0 +1,75 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 v1alpha1 + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestV1alpha1(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "V1alpha1 Suite") +} + +func mkNamespace(name string, labels map[string]string) *corev1.Namespace { + return &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: labels, + }, + } +} + +type PodBuilder struct { + Pod *corev1.Pod +} + +func mkPod(name, namespace string) *PodBuilder { + return &PodBuilder{ + Pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "ctr", + Image: "dummy", + }}, + }, + }, + } +} + +func (b *PodBuilder) WithLabels(labels map[string]string) *PodBuilder { + b.Pod.Labels = labels + return b +} + +func (b *PodBuilder) WithRequests(requests corev1.ResourceList) *PodBuilder { + for i, c := range b.Pod.Spec.Containers { + c.Resources.Requests = requests + b.Pod.Spec.Containers[i] = c + } + return b +} diff --git a/pkg/apis/schedule/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/schedule/v1alpha1/zz_generated.deepcopy.go index f304d5a..153fbc7 100644 --- a/pkg/apis/schedule/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/schedule/v1alpha1/zz_generated.deepcopy.go @@ -1,5 +1,3 @@ -// +build !ignore_autogenerated - // Licensed to Shingo Omura under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright @@ -17,6 +15,9 @@ // specific language governing permissions and limitations // under the License. +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + // Code generated by deepcopy-gen. DO NOT EDIT. package v1alpha1 @@ -111,12 +112,11 @@ func (in *ClusterThrottleList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterThrottleSelectorTerm) DeepCopyInto(out *ClusterThrottleSelectorTerm) { +func (in *ClusterThrottleSelector) DeepCopyInto(out *ClusterThrottleSelector) { *out = *in - in.ThrottleSelectorTerm.DeepCopyInto(&out.ThrottleSelectorTerm) - if in.NamespaceSelector != nil { - in, out := &in.NamespaceSelector, &out.NamespaceSelector - *out = make([]v1.NodeSelectorRequirement, len(*in)) + if in.SelecterTerms != nil { + in, out := &in.SelecterTerms, &out.SelecterTerms + *out = make([]ClusterThrottleSelectorTerm, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -124,6 +124,24 @@ func (in *ClusterThrottleSelectorTerm) DeepCopyInto(out *ClusterThrottleSelector return } +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterThrottleSelector. +func (in *ClusterThrottleSelector) DeepCopy() *ClusterThrottleSelector { + if in == nil { + return nil + } + out := new(ClusterThrottleSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterThrottleSelectorTerm) DeepCopyInto(out *ClusterThrottleSelectorTerm) { + *out = *in + in.ThrottleSelectorTerm.DeepCopyInto(&out.ThrottleSelectorTerm) + in.NamespaceSelector.DeepCopyInto(&out.NamespaceSelector) + return +} + // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterThrottleSelectorTerm. func (in *ClusterThrottleSelectorTerm) DeepCopy() *ClusterThrottleSelectorTerm { if in == nil { @@ -138,13 +156,7 @@ func (in *ClusterThrottleSelectorTerm) DeepCopy() *ClusterThrottleSelectorTerm { func (in *ClusterThrottleSpec) DeepCopyInto(out *ClusterThrottleSpec) { *out = *in in.ThrottleSpecBase.DeepCopyInto(&out.ThrottleSpecBase) - if in.Selector != nil { - in, out := &in.Selector, &out.Selector - *out = make([]ClusterThrottleSelectorTerm, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } + in.Selector.DeepCopyInto(&out.Selector) return } @@ -245,8 +257,6 @@ func (in *ResourceCounts) DeepCopy() *ResourceCounts { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TemporaryThresholdOverride) DeepCopyInto(out *TemporaryThresholdOverride) { *out = *in - in.Begin.DeepCopyInto(&out.Begin) - in.EndStr.DeepCopyInto(&out.EndStr) in.Threshold.DeepCopyInto(&out.Threshold) return } @@ -323,11 +333,11 @@ func (in *ThrottleList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ThrottleSelectorTerm) DeepCopyInto(out *ThrottleSelectorTerm) { +func (in *ThrottleSelector) DeepCopyInto(out *ThrottleSelector) { *out = *in - if in.PodSelector != nil { - in, out := &in.PodSelector, &out.PodSelector - *out = make([]v1.NodeSelectorRequirement, len(*in)) + if in.SelecterTerms != nil { + in, out := &in.SelecterTerms, &out.SelecterTerms + *out = make([]ThrottleSelectorTerm, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -335,6 +345,23 @@ func (in *ThrottleSelectorTerm) DeepCopyInto(out *ThrottleSelectorTerm) { return } +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ThrottleSelector. +func (in *ThrottleSelector) DeepCopy() *ThrottleSelector { + if in == nil { + return nil + } + out := new(ThrottleSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ThrottleSelectorTerm) DeepCopyInto(out *ThrottleSelectorTerm) { + *out = *in + in.PodSelector.DeepCopyInto(&out.PodSelector) + return +} + // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ThrottleSelectorTerm. func (in *ThrottleSelectorTerm) DeepCopy() *ThrottleSelectorTerm { if in == nil { @@ -349,13 +376,7 @@ func (in *ThrottleSelectorTerm) DeepCopy() *ThrottleSelectorTerm { func (in *ThrottleSpec) DeepCopyInto(out *ThrottleSpec) { *out = *in in.ThrottleSpecBase.DeepCopyInto(&out.ThrottleSpecBase) - if in.Selector != nil { - in, out := &in.Selector, &out.Selector - *out = make([]ThrottleSelectorTerm, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } + in.Selector.DeepCopyInto(&out.Selector) return } diff --git a/pkg/controllers/clusterthrottle_controller.go b/pkg/controllers/clusterthrottle_controller.go new file mode 100644 index 0000000..e3a9cab --- /dev/null +++ b/pkg/controllers/clusterthrottle_controller.go @@ -0,0 +1,489 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package controllers + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + schedulev1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + scheduleclientset "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned" + scheduleinformer "github.com/everpeace/kube-throttler/pkg/generated/informers/externalversions/schedule/v1alpha1" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/clock" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/wait" + corev1informer "k8s.io/client-go/informers/core/v1" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog/v2" + + apiequality "k8s.io/apimachinery/pkg/api/equality" +) + +type ClusterThrottleController struct { + throttlerName string + targetSchedulerName string + reconcileTemporaryThresholdInterval time.Duration + + metricsRecorder *ClusterThrottleMetricsRecorder + + scheduleClientset scheduleclientset.Clientset + podInformer corev1informer.PodInformer + namespaceInformer corev1informer.NamespaceInformer + clusterthrottleInformer scheduleinformer.ClusterThrottleInformer + cache *reservedResourceAmounts + + clock clock.Clock + workqueue workqueue.RateLimitingInterface +} + +func NewClusterThrottleController( + throttlerName, targetSchedulerName string, + reconcileTemporaryThresholdInterval time.Duration, + scheduleClient scheduleclientset.Clientset, + clusterthrottleInformer scheduleinformer.ClusterThrottleInformer, + podInformer corev1informer.PodInformer, + namespaceInformer corev1informer.NamespaceInformer, + clock clock.Clock, +) *ClusterThrottleController { + c := &ClusterThrottleController{ + throttlerName: throttlerName, + targetSchedulerName: targetSchedulerName, + reconcileTemporaryThresholdInterval: reconcileTemporaryThresholdInterval, + metricsRecorder: NewClusterThrottleMetricsRecorder(), + scheduleClientset: scheduleClient, + podInformer: podInformer, + namespaceInformer: namespaceInformer, + clusterthrottleInformer: clusterthrottleInformer, + cache: newReservedResourceAmounts(), + clock: clock, + workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "ClusterThrottleController"), + } + c.setupEventHandler() + return c +} + +func (c *ClusterThrottleController) reconcile(key string) error { + klog.V(2).InfoS("Reconciling ClusterThrottle", "ClusterThrottle", key) + ctx := context.Background() + _, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key)) + return err + } + + thr, err := c.scheduleClientset.ScheduleV1alpha1().ClusterThrottles().Get(ctx, name, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + return nil + } + return err + } + + affectedPods, err := c.affectedPods(thr) + klog.V(2).Infof("ClusterThrottle %s/%s affects to %d pods", thr.Namespace, thr.Name, len(affectedPods)) + if err != nil { + return err + } + + used := schedulev1alpha1.ResourceAmount{} + for _, p := range affectedPods { + used = used.Add(schedulev1alpha1.ResourceAmountOfPod(p)) + } + + newStatus := thr.Status.DeepCopy() + newStatus.Used = used + calculatedThreshold := thr.Spec.CalculateThreshold(c.clock.Now()) + if !apiequality.Semantic.DeepEqual(thr.Status.CalculatedThreshold.Threshold, calculatedThreshold.Threshold) || + !apiequality.Semantic.DeepEqual(thr.Status.CalculatedThreshold.Messages, calculatedThreshold.Messages) { + klog.V(2).InfoS("New calculatedThreshold will take effect", + "ClusterThrottle", thr.Namespace+"/"+thr.Name, + "CalculatedAt", calculatedThreshold.CalculatedAt, + "Threshold", calculatedThreshold.Threshold, + "Message", strings.Join(calculatedThreshold.Messages, ","), + ) + newStatus.CalculatedThreshold = calculatedThreshold + } + newStatus.Throttled = newStatus.CalculatedThreshold.Threshold.IsThrottled(newStatus.Used, true) + + if !apiequality.Semantic.DeepEqual(thr.Status, *newStatus) { + klog.V(2).InfoS("Updating status", "ClusterThrottle", thr.Namespace+"/"+thr.Name) + thr.Status = *newStatus + c.metricsRecorder.recordClusterThrottleMetrics(thr) + if thr, err = c.scheduleClientset.ScheduleV1alpha1().ClusterThrottles().UpdateStatus(ctx, thr, metav1.UpdateOptions{}); err != nil { + utilruntime.HandleError(errors.Wrapf(err, "failed to update ClusterThrottle '%s' status", key)) + return err + } + } else { + c.metricsRecorder.recordClusterThrottleMetrics(thr) + klog.V(2).InfoS("No need to update status", "ClusterThrottle", thr.Namespace+"/"+thr.Name) + } + + // Once status is updated, affected pods is safe to un-reserve from reserved resoruce amount cache + for _, p := range affectedPods { + c.UnReserveOnClusterThrottle(p, thr) + } + + if len(thr.Spec.TemporaryThresholdOverrides) > 0 { + go func(_thr *v1alpha1.ClusterThrottle) { + klog.V(3).Infof("Reconciling after duration", "ClusterThrottle", thr.Namespace+"/"+thr.Name, "After", c.reconcileTemporaryThresholdInterval) + <-c.clock.After(c.reconcileTemporaryThresholdInterval) + c.enqueueClusterThrottle(_thr) + }(thr) + } + + return nil +} + +func (c *ClusterThrottleController) isResponsibleFor(thr *schedulev1alpha1.ClusterThrottle) bool { + return c.throttlerName == thr.Spec.ThrottlerName +} + +func (c *ClusterThrottleController) shouldCountIn(pod *corev1.Pod) bool { + return pod.Spec.SchedulerName == c.targetSchedulerName && isScheduled(pod) && isNotFinished(pod) +} + +func (c *ClusterThrottleController) affectedPods(thr *schedulev1alpha1.ClusterThrottle) ([]*v1.Pod, error) { + pods := []*corev1.Pod{} + nss, err := c.namespaceInformer.Lister().List(labels.Everything()) + if err != nil { + return nil, err + } + for _, ns := range nss { + podsInNs, err := c.podInformer.Lister().Pods(ns.Name).List(labels.Everything()) + if err != nil { + return nil, err + } + pods = append(pods, podsInNs...) + } + + affectedPods := []*v1.Pod{} + for _, pod := range pods { + if !(c.shouldCountIn(pod)) { + continue + } + ns, err := c.namespaceInformer.Lister().Get(pod.Namespace) + if err != nil { + return nil, err + } + match, err := thr.Spec.Selector.MatchesToPod(pod, ns) + if err != nil { + return nil, err + } + if match { + affectedPods = append(affectedPods, pod) + } + } + return affectedPods, nil +} + +func (c *ClusterThrottleController) affectedClusterThrottles(pod *v1.Pod) ([]*schedulev1alpha1.ClusterThrottle, error) { + ns, err := c.namespaceInformer.Lister().Get(pod.Namespace) + if err != nil { + return nil, err + } + + throttles, err := c.clusterthrottleInformer.Lister().List(labels.Everything()) + if err != nil { + return nil, err + } + + affectedClusterThrottles := []*schedulev1alpha1.ClusterThrottle{} + for _, throttle := range throttles { + if !c.isResponsibleFor(throttle) { + continue + } + match, err := throttle.Spec.Selector.MatchesToPod(pod, ns) + if err != nil { + return nil, err + } + if match { + affectedClusterThrottles = append(affectedClusterThrottles, throttle) + } + } + + return affectedClusterThrottles, nil +} + +func (c *ClusterThrottleController) Reserve(pod *v1.Pod) error { + throttles, err := c.affectedClusterThrottles(pod) + if err != nil { + return err + } + for _, thr := range throttles { + nn := types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name} + c.cache.addPod(nn, pod) + reserved := c.cache.reservedResourceAmount(nn) + klog.V(3).InfoS("Pod is reserved for affected clusterthrottle", "Pod", pod.Namespace+"/"+pod.Name, "ClusterThrottle", thr.Name, "CurrentReservedAmount", reserved) + } + if len(throttles) > 0 { + klog.V(2).InfoS("Pod is reserved for affected clusterthrottles", "Pod", pod.Namespace+"/"+pod.Name, "#ClusterThrottles", len(throttles)) + } + return nil +} + +func (c *ClusterThrottleController) moveThrottleAssignmentForPodsInReservation( + fromPod *corev1.Pod, + fromThrs []*schedulev1alpha1.ClusterThrottle, + toPod *corev1.Pod, + toThrs []*schedulev1alpha1.ClusterThrottle, +) { + fromThrNNs := []types.NamespacedName{} + for _, thr := range fromThrs { + fromThrNNs = append(fromThrNNs, types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name}) + } + toThrNNs := []types.NamespacedName{} + for _, thr := range toThrs { + toThrNNs = append(toThrNNs, types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name}) + } + c.cache.moveThrottleAssignmentForPods(fromPod, fromThrNNs, toPod, toThrNNs) +} + +func (c *ClusterThrottleController) UnReserveOnClusterThrottle(pod *v1.Pod, thr *schedulev1alpha1.ClusterThrottle) { + nn := types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name} + removed := c.cache.removePod(nn, pod) + reserved := c.cache.reservedResourceAmount(nn) + if removed { + klog.V(3).InfoS("Pod is un-reserved for affected clusterthrottle", "Pod", pod.Namespace+"/"+pod.Name, "ClusterThrottle", thr.Name, "CurrentReservedAmount", reserved) + } +} + +func (c *ClusterThrottleController) UnReserve(pod *v1.Pod) error { + throttles, err := c.affectedClusterThrottles(pod) + if err != nil { + return err + } + for _, thr := range throttles { + c.UnReserveOnClusterThrottle(pod, thr) + } + if len(throttles) > 0 { + klog.V(2).InfoS("Pod is un-reserved for affected clusterthrottles", "Pod", pod.Namespace+"/"+pod.Name, "#ClusterThrottles", len(throttles)) + } + return nil +} + +func (c *ClusterThrottleController) CheckThrottled(pod *v1.Pod, isThrottledOnEqual bool) ([]schedulev1alpha1.ClusterThrottle, []schedulev1alpha1.ClusterThrottle, []schedulev1alpha1.ClusterThrottle, error) { + throttles, err := c.affectedClusterThrottles(pod) + if err != nil { + return nil, nil, nil, err + } + affected := []schedulev1alpha1.ClusterThrottle{} + alreadyThrottled := []schedulev1alpha1.ClusterThrottle{} + insufficient := []schedulev1alpha1.ClusterThrottle{} + for _, thr := range throttles { + affected = append(affected, *thr) + reserved := c.cache.reservedResourceAmount(types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name}) + checkStatus := thr.CheckThrottledFor( + pod, + reserved, + isThrottledOnEqual, + ) + klog.V(3).InfoS("CheckThrottled result", + "ClusterThrottle", thr.Name, + "Pod", pod.Namespace+"/"+pod.Name, + "Result", checkStatus, + "Threashold", thr.Status.CalculatedThreshold.Threshold, + "RequestedByPod", schedulev1alpha1.ResourceAmountOfPod(pod), + "UsedInClusterThrottle", thr.Status.Used, + "ReservedInScheduler", reserved, + "AmountForCheck", schedulev1alpha1.ResourceAmount{}.Add(thr.Status.Used).Add(schedulev1alpha1.ResourceAmountOfPod(pod)).Add(reserved), + ) + switch checkStatus { + case schedulev1alpha1.CheckThrottleStatusActive: + alreadyThrottled = append(alreadyThrottled, *thr) + case schedulev1alpha1.CheckThrottleStatusInsufficient: + insufficient = append(insufficient, *thr) + } + } + return alreadyThrottled, insufficient, affected, nil +} + +func (c *ClusterThrottleController) setupEventHandler() { + c.namespaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{}) + c.clusterthrottleInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + thr := obj.(*v1alpha1.ClusterThrottle) + klog.V(4).InfoS("Add event", "ClusterThrottle", thr.Name) + c.enqueueClusterThrottle(thr) + }, + UpdateFunc: func(oldObj, newObj interface{}) { + thr := newObj.(*v1alpha1.ClusterThrottle) + klog.V(4).InfoS("Update event", "ClusterThrottle", thr.Name) + c.enqueueClusterThrottle(thr) + }, + DeleteFunc: func(obj interface{}) { + thr := obj.(*v1alpha1.ClusterThrottle) + klog.V(4).InfoS("Add event", "ClusterThrottle", thr.Name) + c.enqueueClusterThrottle(thr) + }, + }) + + c.podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + pod := obj.(*corev1.Pod) + klog.V(4).InfoS("Add event", "Pod", pod.Namespace+"/"+pod.Name) + + throttles, err := c.affectedClusterThrottles(pod) + if err != nil { + utilruntime.HandleError(errors.Wrapf(err, "Failed to get affected clusterthrottles for pod '%s'", pod.Namespace+"/"+pod.Name)) + return + } + + klog.V(4).InfoS("Reconciling ClusterThrottles", "Pod", pod.Namespace+"/"+pod.Name, "#ClusterThrottles", len(throttles)) + for _, thr := range throttles { + c.enqueueClusterThrottle(thr) + } + }, + UpdateFunc: func(oldObj, newObj interface{}) { + oldPod := oldObj.(*corev1.Pod) + newPod := newObj.(*corev1.Pod) + klog.V(4).InfoS("Update event", "Pod", newPod.Namespace+"/"+newPod.Name) + + throttleNames := sets.NewString() + throttlesForOld, err := c.affectedClusterThrottles(oldPod) + if err != nil { + utilruntime.HandleError(errors.Wrapf(err, "fail to get affected clusterthrottles for pod '%s'", oldPod.Namespace+"/"+oldPod.Name)) + return + } + throttlesForNew, err := c.affectedClusterThrottles(newPod) + if err != nil { + utilruntime.HandleError(errors.Wrapf(err, "fail to get affected clusterthrottles for pod '%s'", newPod.Namespace+"/"+newPod.Name)) + return + } + + if isScheduled(oldPod) && isScheduled(newPod) { + c.moveThrottleAssignmentForPodsInReservation(oldPod, throttlesForOld, newPod, throttlesForNew) + } + + for _, thr := range throttlesForOld { + throttleNames.Insert(types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name}.String()) + } + for _, thr := range throttlesForNew { + throttleNames.Insert(types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name}.String()) + } + + klog.V(4).InfoS("Reconciling ClusterThrottles", "Pod", newPod.Namespace+"/"+newPod.Name, "#ClusterThrottles", throttleNames.Len()) + for _, key := range throttleNames.List() { + c.enqueueClusterThrottle(cache.ExplicitKey(key)) + } + }, + DeleteFunc: func(obj interface{}) { + pod := obj.(*corev1.Pod) + klog.V(4).InfoS("Delete event", "Pod", pod.Namespace+"/"+pod.Name) + // observe the deleted pod is now scheduled. controller should unreserve it. + if isScheduled(pod) { + if err := c.UnReserve(pod); err != nil { + utilruntime.HandleError(errors.Wrapf(err, "Failed to unreserve pod '%s'", pod.Namespace+"/"+pod.Name)) + } + } + + throttles, err := c.affectedClusterThrottles(pod) + if err != nil { + utilruntime.HandleError(errors.Wrapf(err, "Failed to get affected clusterthrottles for pod '%s'", pod.Namespace+"/"+pod.Name)) + return + } + + klog.V(4).InfoS("Reconciling ClusterThrottles", "Pod", pod.Namespace+"/"+pod.Name, "#ClusterThrottles", len(throttles)) + for _, thr := range throttles { + c.enqueueClusterThrottle(thr) + } + }, + }) +} + +func (c *ClusterThrottleController) Start(threadiness int, stopCh <-chan struct{}) error { + klog.InfoS("Starting ClusterThrottleController", "name", c.throttlerName) + if ok := cache.WaitForCacheSync( + stopCh, + c.clusterthrottleInformer.Informer().HasSynced, + c.podInformer.Informer().HasSynced, + c.namespaceInformer.Informer().HasSynced, + ); !ok { + return errors.Errorf("failed to wait for caches to sync") + } + klog.InfoS("Informer caches are synced") + + // Launch workers to process Foo resources + for i := 0; i < threadiness; i++ { + go wait.Until(c.runWorker, time.Second, stopCh) + } + + klog.InfoS("Started ClusterThrottleController workers", "threadiness", threadiness) + return nil +} + +func (c *ClusterThrottleController) enqueueClusterThrottle(obj interface{}) { + var key string + var err error + if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { + utilruntime.HandleError(err) + return + } + c.workqueue.Add(key) +} + +func (c *ClusterThrottleController) runWorker() { + for c.processNextWorkItem() { + } +} + +func (c *ClusterThrottleController) processNextWorkItem() bool { + obj, shutdown := c.workqueue.Get() + + if shutdown { + return false + } + + err := func(obj interface{}) error { + defer c.workqueue.Done(obj) + var key string + var ok bool + + if key, ok = obj.(string); !ok { + c.workqueue.Forget(obj) + utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) + return nil + } + if err := c.reconcile(key); err != nil { + c.workqueue.AddRateLimited(key) + return fmt.Errorf("error reconciling '%s': %s, requeuing", key, err.Error()) + } + // get queued again until another change happens. + c.workqueue.Forget(obj) + klog.InfoS("Successfully reconciled", "ClusterThrottle", key) + return nil + }(obj) + + if err != nil { + utilruntime.HandleError(err) + return true + } + + return true +} diff --git a/pkg/controllers/clusterthrottle_metrics.go b/pkg/controllers/clusterthrottle_metrics.go new file mode 100644 index 0000000..1e8a3b4 --- /dev/null +++ b/pkg/controllers/clusterthrottle_metrics.go @@ -0,0 +1,129 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package controllers + +import ( + "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + "github.com/prometheus/client_golang/prometheus" + "k8s.io/component-base/metrics" + "k8s.io/component-base/metrics/legacyregistry" +) + +type ClusterThrottleMetricsRecorder struct { + MetricsRecorder + specThresholdResourceCountsGauge *metrics.GaugeVec + specThresholdResourceRequestsGauge *metrics.GaugeVec + statusThrottledResourceCountsGauge *metrics.GaugeVec + statusThrottledResourceRequstsGauge *metrics.GaugeVec + statusUsedResourceCountsGauge *metrics.GaugeVec + statusUsedResourceRequestsGauge *metrics.GaugeVec + statusCalculatedThresholdResourceCountsGauge *metrics.GaugeVec + statusCalculatedThresholdResourceRequstsGauge *metrics.GaugeVec +} + +func NewClusterThrottleMetricsRecorder() *ClusterThrottleMetricsRecorder { + r := &ClusterThrottleMetricsRecorder{ + MetricsRecorder: MetricsRecorder{}, + specThresholdResourceCountsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "clusterthrottle_spec_threshold_resourceCounts", + Help: "threshold on specific resourceCounts of the throttle", + }, + []string{"name", "uid", "resource"}, + ), + specThresholdResourceRequestsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "clusterthrottle_spec_threshold_resourceRequests", + Help: "threshold on specific resourceRequests of the throttle", + }, + []string{"name", "uid", "resource"}, + ), + statusThrottledResourceCountsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "clusterthrottle_status_throttled_resourceCounts", + Help: "resourceCounts of the throttle is throttled or not on specific resource (1=throttled, 0=not throttled)", + }, + []string{"name", "uid", "resource"}, + ), + statusThrottledResourceRequstsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "clusterthrottle_status_throttled_resourceRequests", + Help: "resourceRequests of the throttle is throttled or not on specific resource (1=throttled, 0=not throttled)", + }, + []string{"name", "uid", "resource"}, + ), + statusUsedResourceCountsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "clusterthrottle_status_used_resourceCounts", + Help: "used resource counts of the throttle", + }, + []string{"name", "uid", "resource"}, + ), + statusUsedResourceRequestsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "clusterthrottle_status_used_resourceRequests", + Help: "used amount of resource requests of the throttle", + }, + []string{"name", "uid", "resource"}, + ), + statusCalculatedThresholdResourceCountsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "clusterthrottle_status_calculated_threshold_resourceCounts", + Help: "calculated threshold on specific resourceCounts of the throttle", + }, + []string{"name", "uid", "resource"}, + ), + statusCalculatedThresholdResourceRequstsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "clusterthrottle_status_calculated_threshold_resourceRequests", + Help: "calculated threshold on specific resourceRequests of the throttle", + }, + []string{"name", "uid", "resource"}, + ), + } + legacyregistry.MustRegister( + r.specThresholdResourceCountsGauge, + r.specThresholdResourceRequestsGauge, + r.statusThrottledResourceCountsGauge, + r.statusThrottledResourceRequstsGauge, + r.statusUsedResourceCountsGauge, + r.statusUsedResourceRequestsGauge, + r.statusCalculatedThresholdResourceCountsGauge, + r.statusCalculatedThresholdResourceRequstsGauge, + ) + return r +} + +func (r *ClusterThrottleMetricsRecorder) recordClusterThrottleMetrics(thr *v1alpha1.ClusterThrottle) { + labels := prometheus.Labels{ + "name": thr.Name, + "uid": string(thr.UID), + } + + r.recordResourceCounts(r.specThresholdResourceCountsGauge.MustCurryWith(labels), thr.Spec.Threshold.ResourceCounts) + r.recordResourceRequests(r.specThresholdResourceRequestsGauge.MustCurryWith(labels), thr.Spec.Threshold.ResourceRequests) + + r.recordIsResourceCountThrottled(r.statusThrottledResourceCountsGauge.MustCurryWith(labels), thr.Status.Throttled.ResourceCounts) + r.recordIsResourceRequestsThrottled(r.specThresholdResourceRequestsGauge.MustCurryWith(labels), thr.Status.Throttled.ResourceRequests) + + r.recordResourceCounts(r.statusUsedResourceCountsGauge.MustCurryWith(labels), thr.Status.Used.ResourceCounts) + r.recordResourceRequests(r.statusUsedResourceRequestsGauge.MustCurryWith(labels), thr.Status.Used.ResourceRequests) + + r.recordResourceCounts(r.statusCalculatedThresholdResourceCountsGauge.MustCurryWith(labels), thr.Status.CalculatedThreshold.Threshold.ResourceCounts) + r.recordResourceRequests(r.statusCalculatedThresholdResourceRequstsGauge.MustCurryWith(labels), thr.Status.CalculatedThreshold.Threshold.ResourceRequests) +} diff --git a/pkg/controllers/metrics_recorder.go b/pkg/controllers/metrics_recorder.go new file mode 100644 index 0000000..9722737 --- /dev/null +++ b/pkg/controllers/metrics_recorder.go @@ -0,0 +1,67 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package controllers + +import ( + "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + "github.com/prometheus/client_golang/prometheus" + corev1 "k8s.io/api/core/v1" +) + +type MetricsRecorder struct { +} + +func (m *MetricsRecorder) recordResourceCounts(g *prometheus.GaugeVec, rc *v1alpha1.ResourceCounts) { + if rc == nil { + g.WithLabelValues("pod").Set(0.0) + } else { + g.WithLabelValues("pod").Set(float64(rc.Pod)) + } +} + +func (m *MetricsRecorder) recordResourceRequests(g *prometheus.GaugeVec, rr corev1.ResourceList) { + for r, q := range rr { + switch r { + case corev1.ResourceCPU: + g.WithLabelValues(string(r)).Set(float64(q.MilliValue())) + default: + g.WithLabelValues(string(r)).Set(float64(q.Value())) + } + } +} + +func (m *MetricsRecorder) recordIsResourceCountThrottled(g *prometheus.GaugeVec, rc v1alpha1.IsResourceCountThrottled) { + if rc.Pod { + g.WithLabelValues("pod").Set(float64(1)) + } else { + g.WithLabelValues("pod").Set(float64(0)) + } +} + +func (m *MetricsRecorder) recordIsResourceRequestsThrottled(g *prometheus.GaugeVec, rr map[corev1.ResourceName]bool) { + if rr == nil { + return + } + for r, throttled := range rr { + if throttled { + g.WithLabelValues(string(r)).Set(float64(1)) + } else { + g.WithLabelValues(string(r)).Set(float64(0)) + } + } +} diff --git a/pkg/controllers/pod_util.go b/pkg/controllers/pod_util.go new file mode 100644 index 0000000..fa96ee4 --- /dev/null +++ b/pkg/controllers/pod_util.go @@ -0,0 +1,28 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package controllers + +import corev1 "k8s.io/api/core/v1" + +func isScheduled(pod *corev1.Pod) bool { + return pod.Spec.NodeName != "" +} + +func isNotFinished(pod *corev1.Pod) bool { + return pod.Status.Phase != corev1.PodSucceeded && pod.Status.Phase != corev1.PodFailed +} diff --git a/pkg/controllers/reserved_resource_ammounts.go b/pkg/controllers/reserved_resource_ammounts.go new file mode 100644 index 0000000..d19cd01 --- /dev/null +++ b/pkg/controllers/reserved_resource_ammounts.go @@ -0,0 +1,139 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package controllers + +import ( + "fmt" + "reflect" + + schedulev1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" + "k8s.io/utils/keymutex" +) + +type reservedResourceAmounts struct { + // [Cluster]Throttle Name -> podResourceAmountMap + cache map[types.NamespacedName]podResourceAmountMap + keyMutex keymutex.KeyMutex +} + +func newReservedResourceAmounts() *reservedResourceAmounts { + return &reservedResourceAmounts{ + cache: map[types.NamespacedName]podResourceAmountMap{}, + keyMutex: keymutex.NewHashed(0), + } +} + +func (c *reservedResourceAmounts) addPod(nn types.NamespacedName, pod *corev1.Pod) { + c.keyMutex.LockKey(nn.String()) + defer func() { + _ = c.keyMutex.UnlockKey(nn.String()) + }() + + if _, ok := c.cache[nn]; !ok { + c.cache[nn] = podResourceAmountMap{} + } + + c.cache[nn].add(pod) + klog.V(5).InfoS("reservedResourceAmounts.addPod", "Pod", pod.Namespace+"/"+pod.Name, "NamespacedName", nn.String(), "Cache", c.cache) +} + +func (c *reservedResourceAmounts) removePod(nn types.NamespacedName, pod *corev1.Pod) bool { + c.keyMutex.LockKey(nn.String()) + defer func() { + _ = c.keyMutex.UnlockKey(nn.String()) + }() + + if _, ok := c.cache[nn]; !ok { + c.cache[nn] = podResourceAmountMap{} + } + + removed := c.cache[nn].remove(pod) + klog.V(5).InfoS("reservedResourceAmounts.removePod", "Pod", pod.Namespace+"/"+pod.Name, "NamespacedName", nn.String(), "Removed", removed, "Cache", c.cache) + return removed +} + +func (c *reservedResourceAmounts) moveThrottleAssignmentForPods(fromPod *corev1.Pod, fromThrs []types.NamespacedName, toPod *corev1.Pod, toThrs []types.NamespacedName) { + fromThrSet := map[types.NamespacedName]struct{}{} + toThrSet := map[types.NamespacedName]struct{}{} + for _, nn := range fromThrs { + fromThrSet[nn] = struct{}{} + } + for _, nn := range toThrs { + toThrSet[nn] = struct{}{} + delete(fromThrSet, nn) + } + for _, nn := range fromThrs { + delete(toThrSet, nn) + } + if v5 := klog.V(5); v5.Enabled() { + v5.InfoS( + "reservedResourceAmounts.moveThrottleAssignmentForPods", + "FromPod", fromPod.Namespace+"/"+fromPod.Name, + "FromThrNNs", fmt.Sprint(reflect.ValueOf(fromThrSet).MapKeys()), + "TromPod", toPod.Namespace+"/"+toPod.Name, + "ToThrNNs", fmt.Sprint(reflect.ValueOf(toThrSet).MapKeys()), + ) + } + for nn := range toThrSet { + c.addPod(nn, toPod) + } + for nn := range fromThrSet { + c.removePod(nn, toPod) + } +} + +func (c *reservedResourceAmounts) reservedResourceAmount(nn types.NamespacedName) schedulev1alpha1.ResourceAmount { + c.keyMutex.LockKey(nn.String()) + defer func() { + _ = c.keyMutex.UnlockKey(nn.String()) + }() + podResourceAmountMap, ok := c.cache[nn] + if !ok { + return schedulev1alpha1.ResourceAmount{} + } + return podResourceAmountMap.totalResoruceAmount() +} + +// pod's namespacedname --> ResourceAmount of the pod +type podResourceAmountMap map[types.NamespacedName]schedulev1alpha1.ResourceAmount + +func (c podResourceAmountMap) add(pod *corev1.Pod) { + nn := types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name} + c[nn] = schedulev1alpha1.ResourceAmountOfPod(pod) +} + +func (c podResourceAmountMap) remove(pod *corev1.Pod) bool { + return c.removeByNN(types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name}) +} + +func (c podResourceAmountMap) removeByNN(nn types.NamespacedName) bool { + _, ok := c[nn] + delete(c, nn) + return ok +} + +func (c podResourceAmountMap) totalResoruceAmount() schedulev1alpha1.ResourceAmount { + result := schedulev1alpha1.ResourceAmount{} + for _, ra := range c { + result = result.Add(ra) + } + return result +} diff --git a/pkg/controllers/throttle_controller.go b/pkg/controllers/throttle_controller.go new file mode 100644 index 0000000..fbf427f --- /dev/null +++ b/pkg/controllers/throttle_controller.go @@ -0,0 +1,468 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package controllers + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + schedulev1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + scheduleclientset "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned" + scheduleinformer "github.com/everpeace/kube-throttler/pkg/generated/informers/externalversions/schedule/v1alpha1" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/clock" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/wait" + corev1informer "k8s.io/client-go/informers/core/v1" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog/v2" + + apiequality "k8s.io/apimachinery/pkg/api/equality" +) + +type ThrottleController struct { + throttlerName string + targetSchedulerName string + reconcileTemporaryThresholdInterval time.Duration + + metricsRecorder *ThrottleMetricsRecorder + + scheduleClientset scheduleclientset.Clientset + podInformer corev1informer.PodInformer + throttleInformer scheduleinformer.ThrottleInformer + cache *reservedResourceAmounts + + clock clock.Clock + workqueue workqueue.RateLimitingInterface +} + +func NewThrottleController( + throttlerName, targetSchedulerName string, + reconcileTemporaryThresholdInterval time.Duration, + scheduleClient scheduleclientset.Clientset, + throttleInformer scheduleinformer.ThrottleInformer, + podInformer corev1informer.PodInformer, + clock clock.Clock, +) *ThrottleController { + c := &ThrottleController{ + throttlerName: throttlerName, + targetSchedulerName: targetSchedulerName, + reconcileTemporaryThresholdInterval: reconcileTemporaryThresholdInterval, + metricsRecorder: NewThrottleMetricsRecorder(), + scheduleClientset: scheduleClient, + podInformer: podInformer, + throttleInformer: throttleInformer, + cache: newReservedResourceAmounts(), + clock: clock, + workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "ThrottleController"), + } + c.setupEventHandler() + return c +} + +func (c *ThrottleController) reconcile(key string) error { + klog.V(2).InfoS("Reconciling Throttle", "Throttle", key) + ctx := context.Background() + namespace, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key)) + return err + } + + thr, err := c.scheduleClientset.ScheduleV1alpha1().Throttles(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + return nil + } + return err + } + + affectedPods, err := c.affectedPods(thr) + klog.V(2).Infof("Throttle %s/%s affects to %d pods", thr.Namespace, thr.Name, len(affectedPods)) + if err != nil { + return err + } + + used := schedulev1alpha1.ResourceAmount{} + for _, p := range affectedPods { + used = used.Add(schedulev1alpha1.ResourceAmountOfPod(p)) + } + + newStatus := thr.Status.DeepCopy() + newStatus.Used = used + calculatedThreshold := thr.Spec.CalculateThreshold(c.clock.Now()) + if !apiequality.Semantic.DeepEqual(thr.Status.CalculatedThreshold.Threshold, calculatedThreshold.Threshold) || + !apiequality.Semantic.DeepEqual(thr.Status.CalculatedThreshold.Messages, calculatedThreshold.Messages) { + klog.V(2).InfoS("New calculatedThreshold will take effect", + "Throttle", thr.Namespace+"/"+thr.Name, + "CalculatedAt", calculatedThreshold.CalculatedAt, + "Threshold", calculatedThreshold.Threshold, + "Message", strings.Join(calculatedThreshold.Messages, ","), + ) + newStatus.CalculatedThreshold = calculatedThreshold + } + newStatus.Throttled = newStatus.CalculatedThreshold.Threshold.IsThrottled(newStatus.Used, true) + + if !apiequality.Semantic.DeepEqual(thr.Status, *newStatus) { + klog.V(2).InfoS("Updating status", "Throttle", thr.Namespace+"/"+thr.Name) + thr.Status = *newStatus + c.metricsRecorder.recordThrottleMetrics(thr) + if thr, err = c.scheduleClientset.ScheduleV1alpha1().Throttles(namespace).UpdateStatus(ctx, thr, metav1.UpdateOptions{}); err != nil { + utilruntime.HandleError(errors.Wrapf(err, "failed to update Throttle '%s' status", key)) + return err + } + } else { + c.metricsRecorder.recordThrottleMetrics(thr) + klog.V(2).InfoS("No need to update status", "Throttle", thr.Namespace+"/"+thr.Name) + } + + // Once status is updated, affected pods is safe to un-reserve from reserved resoruce amount cache + for _, p := range affectedPods { + c.UnReserveOnThrottle(p, thr) + } + + if len(thr.Spec.TemporaryThresholdOverrides) > 0 { + go func(_thr *v1alpha1.Throttle) { + klog.V(3).Infof("Reconciling after duration", "Throttle", thr.Namespace+"/"+thr.Name, "After", c.reconcileTemporaryThresholdInterval) + <-c.clock.After(c.reconcileTemporaryThresholdInterval) + c.enqueueThrottle(_thr) + }(thr) + } + + return nil +} + +func (c *ThrottleController) isResponsibleFor(thr *schedulev1alpha1.Throttle) bool { + return c.throttlerName == thr.Spec.ThrottlerName +} + +func (c *ThrottleController) shouldCountIn(pod *corev1.Pod) bool { + return pod.Spec.SchedulerName == c.targetSchedulerName && isScheduled(pod) && isNotFinished(pod) +} + +func (c *ThrottleController) affectedPods(thr *schedulev1alpha1.Throttle) ([]*v1.Pod, error) { + pods, err := c.podInformer.Lister().Pods(thr.Namespace).List(labels.Everything()) + if err != nil { + return nil, err + } + + affectedPods := []*v1.Pod{} + for _, pod := range pods { + if !(c.shouldCountIn(pod)) { + continue + } + match, err := thr.Spec.Selector.MatchesToPod(pod) + if err != nil { + return nil, err + } + if match { + affectedPods = append(affectedPods, pod) + } + } + return affectedPods, nil +} + +func (c *ThrottleController) affectedThrottles(pod *v1.Pod) ([]*schedulev1alpha1.Throttle, error) { + throttles, err := c.throttleInformer.Lister().Throttles(pod.Namespace).List(labels.Everything()) + if err != nil { + return nil, err + } + + affectedThrottles := []*schedulev1alpha1.Throttle{} + for _, throttle := range throttles { + if !c.isResponsibleFor(throttle) { + continue + } + match, err := throttle.Spec.Selector.MatchesToPod(pod) + if err != nil { + return nil, err + } + if match { + affectedThrottles = append(affectedThrottles, throttle) + } + } + + return affectedThrottles, nil +} + +func (c *ThrottleController) Reserve(pod *v1.Pod) error { + throttles, err := c.affectedThrottles(pod) + if err != nil { + return err + } + for _, thr := range throttles { + nn := types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name} + c.cache.addPod(nn, pod) + reserved := c.cache.reservedResourceAmount(nn) + klog.V(3).InfoS("Pod is reserved for affected throttle", "Pod", pod.Namespace+"/"+pod.Name, "Throttle", thr.Name, "CurrentReservedAmount", reserved) + } + if len(throttles) > 0 { + klog.V(2).InfoS("Pod is reserved for affected throttles", "Pod", pod.Namespace+"/"+pod.Name, "#Throttles", len(throttles)) + } + return nil +} + +func (c *ThrottleController) moveThrottleAssignmentForPodsInReservation( + fromPod *corev1.Pod, + fromThrs []*schedulev1alpha1.Throttle, + toPod *corev1.Pod, + toThrs []*schedulev1alpha1.Throttle, +) { + fromThrNNs := []types.NamespacedName{} + for _, thr := range fromThrs { + fromThrNNs = append(fromThrNNs, types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name}) + } + toThrNNs := []types.NamespacedName{} + for _, thr := range toThrs { + toThrNNs = append(toThrNNs, types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name}) + } + c.cache.moveThrottleAssignmentForPods(fromPod, fromThrNNs, toPod, toThrNNs) +} + +func (c *ThrottleController) UnReserveOnThrottle(pod *v1.Pod, thr *schedulev1alpha1.Throttle) { + nn := types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name} + removed := c.cache.removePod(nn, pod) + reserved := c.cache.reservedResourceAmount(nn) + if removed { + klog.V(3).InfoS("Pod is un-reserved for affected throttle", "Pod", pod.Namespace+"/"+pod.Name, "Throttle", thr.Name, "CurrentReservedAmount", reserved) + } +} + +func (c *ThrottleController) UnReserve(pod *v1.Pod) error { + throttles, err := c.affectedThrottles(pod) + if err != nil { + return err + } + for _, thr := range throttles { + c.UnReserveOnThrottle(pod, thr) + } + if len(throttles) > 0 { + klog.V(2).InfoS("Pod is un-reserved for affected throttles", "Pod", pod.Namespace+"/"+pod.Name, "#Throttles", len(throttles)) + } + return nil +} + +func (c *ThrottleController) CheckThrottled(pod *v1.Pod, isThrottledOnEqual bool) ([]schedulev1alpha1.Throttle, []schedulev1alpha1.Throttle, []schedulev1alpha1.Throttle, error) { + throttles, err := c.affectedThrottles(pod) + if err != nil { + return nil, nil, nil, err + } + affected := []schedulev1alpha1.Throttle{} + alreadyThrottled := []schedulev1alpha1.Throttle{} + insufficient := []schedulev1alpha1.Throttle{} + for _, thr := range throttles { + affected = append(affected, *thr) + reserved := c.cache.reservedResourceAmount(types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name}) + checkStatus := thr.CheckThrottledFor( + pod, + reserved, + isThrottledOnEqual, + ) + klog.V(3).InfoS("CheckThrottled result", + "Throttle", thr.Name, + "Pod", pod.Namespace+"/"+pod.Name, + "Result", checkStatus, + "RequestedByPod", schedulev1alpha1.ResourceAmountOfPod(pod), + "UsedInThrottle", thr.Status.Used, + "ReservedInScheduler", reserved, + "AmountForCheck", schedulev1alpha1.ResourceAmount{}.Add(thr.Status.Used).Add(schedulev1alpha1.ResourceAmountOfPod(pod)).Add(reserved), + "Threashold", thr.Status.CalculatedThreshold.Threshold, + ) + switch checkStatus { + case schedulev1alpha1.CheckThrottleStatusActive: + alreadyThrottled = append(alreadyThrottled, *thr) + case schedulev1alpha1.CheckThrottleStatusInsufficient: + insufficient = append(insufficient, *thr) + } + } + return alreadyThrottled, insufficient, affected, nil +} + +func (c *ThrottleController) setupEventHandler() { + c.throttleInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + thr := obj.(*v1alpha1.Throttle) + klog.V(4).InfoS("Add event", "Throttle", thr.Namespace+"/"+thr.Name) + c.enqueueThrottle(thr) + }, + UpdateFunc: func(oldObj, newObj interface{}) { + thr := newObj.(*v1alpha1.Throttle) + klog.V(4).InfoS("Update event", "Throttle", thr.Namespace+"/"+thr.Name) + c.enqueueThrottle(thr) + }, + DeleteFunc: func(obj interface{}) { + thr := obj.(*v1alpha1.Throttle) + klog.V(4).InfoS("Add event", "Throttle", thr.Namespace+"/"+thr.Name) + c.enqueueThrottle(thr) + }, + }) + + c.podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + pod := obj.(*corev1.Pod) + + klog.V(4).InfoS("Add event", "Pod", pod.Namespace+"/"+pod.Name) + + throttles, err := c.affectedThrottles(pod) + if err != nil { + utilruntime.HandleError(errors.Wrapf(err, "Failed to get affected throttles for pod '%s'", pod.Namespace+"/"+pod.Name)) + return + } + + klog.V(4).InfoS("Reconciling Throttles", "Pod", pod.Namespace+"/"+pod.Name, "#Throttles", len(throttles)) + for _, thr := range throttles { + c.enqueueThrottle(thr) + } + }, + UpdateFunc: func(oldObj, newObj interface{}) { + oldPod := oldObj.(*corev1.Pod) + newPod := newObj.(*corev1.Pod) + klog.V(4).InfoS("Update event", "Pod", newPod.Namespace+"/"+newPod.Name) + + throttleNames := sets.NewString() + throttlesForOld, err := c.affectedThrottles(oldPod) + if err != nil { + utilruntime.HandleError(errors.Wrapf(err, "fail to get affected throttles for pod '%s'", oldPod.Namespace+"/"+oldPod.Name)) + return + } + throttlesForNew, err := c.affectedThrottles(newPod) + if err != nil { + utilruntime.HandleError(errors.Wrapf(err, "fail to get affected throttles for pod '%s'", newPod.Namespace+"/"+newPod.Name)) + return + } + + if isScheduled(oldPod) && isScheduled(newPod) { + c.moveThrottleAssignmentForPodsInReservation(oldPod, throttlesForOld, newPod, throttlesForNew) + } + + for _, thr := range throttlesForOld { + throttleNames.Insert(types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name}.String()) + } + for _, thr := range throttlesForNew { + throttleNames.Insert(types.NamespacedName{Namespace: thr.Namespace, Name: thr.Name}.String()) + } + + klog.V(4).InfoS("Reconciling Throttles", "Pod", newPod.Namespace+"/"+newPod.Name, "Throttles", strings.Join(throttleNames.List(), ",")) + for _, key := range throttleNames.List() { + c.enqueueThrottle(cache.ExplicitKey(key)) + } + }, + DeleteFunc: func(obj interface{}) { + pod := obj.(*corev1.Pod) + klog.V(4).InfoS("Delete event", "Pod", pod.Namespace+"/"+pod.Name) + // observe the deleted pod is now scheduled. controller should unreserve it. + if isScheduled(pod) { + if err := c.UnReserve(pod); err != nil { + utilruntime.HandleError(errors.Wrapf(err, "Failed to unreserve pod '%s'", pod.Namespace+"/"+pod.Name)) + } + } + + throttles, err := c.affectedThrottles(pod) + if err != nil { + utilruntime.HandleError(errors.Wrapf(err, "Failed to get affected throttles for pod '%s'", pod.Namespace+"/"+pod.Name)) + return + } + + klog.V(4).InfoS("Reconciling Throttles", "Pod", pod.Namespace+"/"+pod.Name, "#Throttles", len(throttles)) + for _, thr := range throttles { + c.enqueueThrottle(thr) + } + }, + }) +} + +func (c *ThrottleController) Start(threadiness int, stopCh <-chan struct{}) error { + klog.InfoS("Starting ThrottleController", "name", c.throttlerName) + if ok := cache.WaitForCacheSync( + stopCh, + c.throttleInformer.Informer().HasSynced, + c.podInformer.Informer().HasSynced, + ); !ok { + return errors.Errorf("failed to wait for caches to sync") + } + klog.InfoS("Informer caches are synced") + + // Launch workers to process Foo resources + for i := 0; i < threadiness; i++ { + go wait.Until(c.runWorker, time.Second, stopCh) + } + + klog.InfoS("Started ThrottleController workers", "threadiness", threadiness) + return nil +} + +func (c *ThrottleController) enqueueThrottle(obj interface{}) { + var key string + var err error + if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { + utilruntime.HandleError(err) + return + } + c.workqueue.Add(key) +} + +func (c *ThrottleController) runWorker() { + for c.processNextWorkItem() { + } +} + +func (c *ThrottleController) processNextWorkItem() bool { + obj, shutdown := c.workqueue.Get() + + if shutdown { + return false + } + + err := func(obj interface{}) error { + defer c.workqueue.Done(obj) + var key string + var ok bool + + if key, ok = obj.(string); !ok { + c.workqueue.Forget(obj) + utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) + return nil + } + if err := c.reconcile(key); err != nil { + c.workqueue.AddRateLimited(key) + return fmt.Errorf("error reconciling '%s': %s, requeuing", key, err.Error()) + } + // get queued again until another change happens. + c.workqueue.Forget(obj) + klog.InfoS("Successfully reconciled", "Throttle", key) + return nil + }(obj) + + if err != nil { + utilruntime.HandleError(err) + return true + } + + return true +} diff --git a/pkg/controllers/throttle_metrics.go b/pkg/controllers/throttle_metrics.go new file mode 100644 index 0000000..3ad4d78 --- /dev/null +++ b/pkg/controllers/throttle_metrics.go @@ -0,0 +1,130 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package controllers + +import ( + "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + "github.com/prometheus/client_golang/prometheus" + "k8s.io/component-base/metrics" + "k8s.io/component-base/metrics/legacyregistry" +) + +type ThrottleMetricsRecorder struct { + MetricsRecorder + specThresholdResourceCountsGauge *metrics.GaugeVec + specThresholdResourceRequestsGauge *metrics.GaugeVec + statusThrottledResourceCountsGauge *metrics.GaugeVec + statusThrottledResourceRequstsGauge *metrics.GaugeVec + statusUsedResourceCountsGauge *metrics.GaugeVec + statusUsedResourceRequestsGauge *metrics.GaugeVec + statusCalculatedThresholdResourceCountsGauge *metrics.GaugeVec + statusCalculatedThresholdResourceRequstsGauge *metrics.GaugeVec +} + +func NewThrottleMetricsRecorder() *ThrottleMetricsRecorder { + r := &ThrottleMetricsRecorder{ + MetricsRecorder: MetricsRecorder{}, + specThresholdResourceCountsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "throttle_spec_threshold_resourceCounts", + Help: "threshold on specific resourceCounts of the throttle", + }, + []string{"namespace", "name", "uid", "resource"}, + ), + specThresholdResourceRequestsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "throttle_spec_threshold_resourceRequests", + Help: "threshold on specific resourceRequests of the throttle", + }, + []string{"namespace", "name", "uid", "resource"}, + ), + statusThrottledResourceCountsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "throttle_status_throttled_resourceCounts", + Help: "resourceCounts of the throttle is throttled or not on specific resource (1=throttled, 0=not throttled)", + }, + []string{"namespace", "name", "uid", "resource"}, + ), + statusThrottledResourceRequstsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "throttle_status_throttled_resourceRequests", + Help: "resourceRequests of the throttle is throttled or not on specific resource (1=throttled, 0=not throttled)", + }, + []string{"namespace", "name", "uid", "resource"}, + ), + statusUsedResourceCountsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "throttle_status_used_resourceCounts", + Help: "used resource counts of the throttle", + }, + []string{"namespace", "name", "uid", "resource"}, + ), + statusUsedResourceRequestsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "throttle_status_used_resourceRequests", + Help: "used amount of resource requests of the throttle", + }, + []string{"namespace", "name", "uid", "resource"}, + ), + statusCalculatedThresholdResourceCountsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "throttle_status_calculated_threshold_resourceCounts", + Help: "calculated threshold on specific resourceCounts of the throttle", + }, + []string{"namespace", "name", "uid", "resource"}, + ), + statusCalculatedThresholdResourceRequstsGauge: metrics.NewGaugeVec( + &metrics.GaugeOpts{ + Name: "throttle_status_calculated_threshold_resourceRequests", + Help: "calculated threshold on specific resourceRequests of the throttle", + }, + []string{"namespace", "name", "uid", "resource"}, + ), + } + legacyregistry.MustRegister( + r.specThresholdResourceCountsGauge, + r.specThresholdResourceRequestsGauge, + r.statusThrottledResourceCountsGauge, + r.statusThrottledResourceRequstsGauge, + r.statusUsedResourceCountsGauge, + r.statusUsedResourceRequestsGauge, + r.statusCalculatedThresholdResourceCountsGauge, + r.statusCalculatedThresholdResourceRequstsGauge, + ) + return r +} + +func (r *ThrottleMetricsRecorder) recordThrottleMetrics(thr *v1alpha1.Throttle) { + labels := prometheus.Labels{ + "namespace": thr.Namespace, + "name": thr.Name, + "uid": string(thr.UID), + } + + r.recordResourceCounts(r.specThresholdResourceCountsGauge.MustCurryWith(labels), thr.Spec.Threshold.ResourceCounts) + r.recordResourceRequests(r.specThresholdResourceRequestsGauge.MustCurryWith(labels), thr.Spec.Threshold.ResourceRequests) + + r.recordIsResourceCountThrottled(r.statusThrottledResourceCountsGauge.MustCurryWith(labels), thr.Status.Throttled.ResourceCounts) + r.recordIsResourceRequestsThrottled(r.specThresholdResourceRequestsGauge.MustCurryWith(labels), thr.Status.Throttled.ResourceRequests) + + r.recordResourceCounts(r.statusUsedResourceCountsGauge.MustCurryWith(labels), thr.Status.Used.ResourceCounts) + r.recordResourceRequests(r.statusUsedResourceRequestsGauge.MustCurryWith(labels), thr.Status.Used.ResourceRequests) + + r.recordResourceCounts(r.statusCalculatedThresholdResourceCountsGauge.MustCurryWith(labels), thr.Status.CalculatedThreshold.Threshold.ResourceCounts) + r.recordResourceRequests(r.statusCalculatedThresholdResourceRequstsGauge.MustCurryWith(labels), thr.Status.CalculatedThreshold.Threshold.ResourceRequests) +} diff --git a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_clusterthrottle.go b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_clusterthrottle.go index cc19c3f..9ed48ce 100644 --- a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_clusterthrottle.go +++ b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_clusterthrottle.go @@ -36,9 +36,9 @@ type FakeClusterThrottles struct { Fake *FakeScheduleV1alpha1 } -var clusterthrottlesResource = schema.GroupVersionResource{Group: "schedule.k8s.everpeace.github.co", Version: "v1alpha1", Resource: "clusterthrottles"} +var clusterthrottlesResource = schema.GroupVersionResource{Group: "schedule.k8s.everpeace.github.com", Version: "v1alpha1", Resource: "clusterthrottles"} -var clusterthrottlesKind = schema.GroupVersionKind{Group: "schedule.k8s.everpeace.github.co", Version: "v1alpha1", Kind: "ClusterThrottle"} +var clusterthrottlesKind = schema.GroupVersionKind{Group: "schedule.k8s.everpeace.github.com", Version: "v1alpha1", Kind: "ClusterThrottle"} // Get takes name of the clusterThrottle, and returns the corresponding clusterThrottle object, and an error if there is any. func (c *FakeClusterThrottles) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterThrottle, err error) { diff --git a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_throttle.go b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_throttle.go index 6fd650f..e2e57e0 100644 --- a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_throttle.go +++ b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/fake/fake_throttle.go @@ -37,9 +37,9 @@ type FakeThrottles struct { ns string } -var throttlesResource = schema.GroupVersionResource{Group: "schedule.k8s.everpeace.github.co", Version: "v1alpha1", Resource: "throttles"} +var throttlesResource = schema.GroupVersionResource{Group: "schedule.k8s.everpeace.github.com", Version: "v1alpha1", Resource: "throttles"} -var throttlesKind = schema.GroupVersionKind{Group: "schedule.k8s.everpeace.github.co", Version: "v1alpha1", Kind: "Throttle"} +var throttlesKind = schema.GroupVersionKind{Group: "schedule.k8s.everpeace.github.com", Version: "v1alpha1", Kind: "Throttle"} // Get takes name of the throttle, and returns the corresponding throttle object, and an error if there is any. func (c *FakeThrottles) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Throttle, err error) { diff --git a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/schedule_client.go b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/schedule_client.go index d797812..0e4d985 100644 --- a/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/schedule_client.go +++ b/pkg/generated/clientset/versioned/typed/schedule/v1alpha1/schedule_client.go @@ -31,7 +31,7 @@ type ScheduleV1alpha1Interface interface { ThrottlesGetter } -// ScheduleV1alpha1Client is used to interact with features provided by the schedule.k8s.everpeace.github.co group. +// ScheduleV1alpha1Client is used to interact with features provided by the schedule.k8s.everpeace.github.com group. type ScheduleV1alpha1Client struct { restClient rest.Interface } diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index cebe80b..a5c8146 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -53,7 +53,7 @@ func (f *genericInformer) Lister() cache.GenericLister { // TODO extend this to unknown resources with a client pool func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { - // Group=schedule.k8s.everpeace.github.co, Version=v1alpha1 + // Group=schedule.k8s.everpeace.github.com, Version=v1alpha1 case v1alpha1.SchemeGroupVersion.WithResource("clusterthrottles"): return &genericInformer{resource: resource.GroupResource(), informer: f.Schedule().V1alpha1().ClusterThrottles().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("throttles"): diff --git a/pkg/resourcelist/resourcelist.go b/pkg/resourcelist/resourcelist.go new file mode 100644 index 0000000..b779d5a --- /dev/null +++ b/pkg/resourcelist/resourcelist.go @@ -0,0 +1,137 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 resourcelist + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/kubernetes/pkg/features" +) + +type ResourceList corev1.ResourceList + +func PodRequestResourceList(pod *corev1.Pod) ResourceList { + icRes := ResourceList{} + for _, c := range pod.Spec.InitContainers { + icRes.SetMax(ResourceList(c.Resources.Requests)) + } + + cRes := ResourceList{} + for _, c := range pod.Spec.Containers { + cRes.Add(ResourceList(c.Resources.Requests)) + } + + cRes.SetMax(icRes) + + // If Overhead is being utilized, add to the total requests for the pod + if pod.Spec.Overhead != nil && utilfeature.DefaultFeatureGate.Enabled(features.PodOverhead) { + cRes.Add(ResourceList(pod.Spec.Overhead)) + } + + return cRes +} + +func (lhs ResourceList) Add(rhs ResourceList) { + for name, qrhs := range rhs { + qlhs := lhs[name] + qlhs.Add(qrhs) + lhs[name] = qlhs + } +} + +func (lhs ResourceList) Sub(rhs ResourceList) { + for name, qrhs := range rhs { + qlhs := lhs[name] + qlhs.Sub(qrhs) + lhs[name] = qlhs + } +} + +func (lhs ResourceList) GreaterOrEqual(rhs ResourceList) bool { + for name, qrhs := range rhs { + if qlhs, ok := lhs[name]; !ok { + return false + } else if qlhs.Cmp(qrhs) < 0 { + return false + } + } + + return true +} + +func (lhs ResourceList) SetMax(rhs ResourceList) { + for rName, rQuant := range rhs { + if _, ok := lhs[rName]; ok { + lhs[rName] = quantityMax(lhs[rName], rQuant) + continue + } + lhs[rName] = rhs[rName] + } +} + +func (lhs ResourceList) SetMin(rhs ResourceList) { + for rName, rQuant := range rhs { + if _, ok := lhs[rName]; ok { + lhs[rName] = quantityMin(lhs[rName], rQuant) + } + } + + for lName := range lhs { + if _, ok := rhs[lName]; !ok { + delete(lhs, lName) + } + } +} + +func (lhs ResourceList) EqualTo(rhs ResourceList) bool { + equalTo := func(r1, r2 ResourceList) bool { + for n, q := range r1 { + if q.Cmp(r2[n]) != 0 { + return false + } + } + return true + } + + return equalTo(lhs, rhs) && equalTo(rhs, lhs) +} + +func quantityMax(qx, qy resource.Quantity) resource.Quantity { + cmp := qx.Cmp(qy) + switch { + case cmp > 0: + return qx + case cmp < 0: + return qy + default: + return qx + } +} + +func quantityMin(qx, qy resource.Quantity) resource.Quantity { + cmp := qx.Cmp(qy) + switch { + case cmp < 0: + return qx + case cmp > 0: + return qy + default: + return qx + } +} diff --git a/pkg/resourcelist/resourcelist_test.go b/pkg/resourcelist/resourcelist_test.go new file mode 100644 index 0000000..8b9e8a8 --- /dev/null +++ b/pkg/resourcelist/resourcelist_test.go @@ -0,0 +1,421 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 resourcelist + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" +) + +func TestResourceList(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "ResourceList Suite") +} + +func q(str string) resource.Quantity { + return resource.MustParse(str) +} + +var ( + zero = q("0") + one = q("1") + two = q("2") + minus_one = q("-1") + minus_two = q("-2") +) + +var _ = Describe("PodResourceRequestList", func() { + Context("only with containers", func() { + It("should sum up resource requests", func() { + p := &corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "n1": one, + }, + }, + }, { + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "n1": one, + }, + }, + }}, + }, + } + + r := PodRequestResourceList(p) + + Expect(r.EqualTo(ResourceList(corev1.ResourceList{ + "n1": two, + }))).Should(Equal(true)) + }) + }) + + Context("with init containers", func() { + It("sets max(max(resources of initContainers), total resources of containers)", func() { + p := &corev1.Pod{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{{ + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "n1": one, + }, + }, + }, { + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "n2": two, + }, + }, + }}, + Containers: []corev1.Container{{ + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "n1": one, + }, + }, + }, { + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "n1": one, + }, + }, + }}, + }, + } + + r := PodRequestResourceList(p) + + Expect(r.EqualTo(ResourceList(corev1.ResourceList{ + "n1": two, + "n2": two, + }))).Should(Equal(true)) + }) + }) +}) + +var _ = Describe("ResourceList", func() { + Describe("Add", func() { + When("resource sets of lhs and rhs are not equal", func() { + It("resultant resource sets are merged", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": zero, + "n2": one, + "n3": one, + }) + rhs := ResourceList(corev1.ResourceList{ + "n2": zero, + "n3": one, + "n4": two, + }) + + lhs.Add(rhs) + + Expect(lhs.EqualTo(ResourceList(corev1.ResourceList{ + "n1": zero, + "n2": one, + "n3": two, + "n4": two, + }))).Should(Equal(true)) + }) + }) + + When("resource sets of lhs and rhs are equal", func() { + It("just adds resource quantities", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": zero, + "n2": zero, + "n3": one, + "n4": one, + }) + rhs := ResourceList(corev1.ResourceList{ + "n1": zero, + "n2": one, + "n3": zero, + "n4": one, + }) + + lhs.Add(rhs) + + Expect(lhs.EqualTo(ResourceList(corev1.ResourceList{ + "n1": zero, + "n2": one, + "n3": one, + "n4": two, + }))) + }) + }) + }) + + Describe("Sub", func() { + When("resource sets of lhs and rhs are not equal", func() { + It("resultant resource sets are merged", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": one, + "n3": one, + }) + rhs := ResourceList(corev1.ResourceList{ + "n2": zero, + "n3": one, + "n4": two, + }) + + lhs.Sub(rhs) + + Expect(lhs.EqualTo(ResourceList(corev1.ResourceList{ + "n1": one, + "n2": one, + "n3": zero, + "n4": minus_two, + }))).Should(Equal(true)) + }) + }) + + When("resource sets of lhs and rhs are equal", func() { + It("just subtracts resource quantities", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": zero, + "n2": zero, + "n3": one, + "n4": one, + }) + rhs := ResourceList(corev1.ResourceList{ + "n1": zero, + "n2": one, + "n3": zero, + "n4": one, + }) + + lhs.Sub(rhs) + + Expect(lhs.EqualTo(ResourceList(corev1.ResourceList{ + "n1": zero, + "n2": minus_one, + "n3": one, + "n4": zero, + }))).Should(Equal(true)) + }) + }) + }) + + Describe("GreaterOrEqual", func() { + When("resource kinds are equal and lhs is greater than rhs", func() { + It("should return true", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": two, + }) + rhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": one, + "n3": two, + }) + + Expect(lhs.GreaterOrEqual(rhs)).Should(BeTrue()) + }) + }) + When("resource kinds are not equal and lhs is greater than rhs", func() { + It("should return true", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": two, + }) + rhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n3": two, + }) + + Expect(lhs.GreaterOrEqual(rhs)).Should(BeTrue()) + }) + }) + When("resource kinds are equal and lhs is not greater than rhs", func() { + It("should return false", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": one, + "n3": two, + }) + rhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": one, + }) + + Expect(lhs.GreaterOrEqual(rhs)).Should(BeFalse()) + }) + }) + When("resource kinds are not equal and lhs is not greater than rhs", func() { + It("should return false", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": one, + }) + rhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n3": two, + }) + + Expect(lhs.GreaterOrEqual(rhs)).Should(BeFalse()) + }) + }) + When("resource kinds are equal and lhs is equal to rhs", func() { + It("should return true", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": one, + }) + rhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": one, + }) + + Expect(lhs.GreaterOrEqual(rhs)).Should(BeTrue()) + }) + }) + When("resource kinds are not equal and lhs is equal to rhs", func() { + It("should return true", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": one, + }) + rhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n3": one, + }) + + Expect(lhs.GreaterOrEqual(rhs)).Should(BeTrue()) + }) + }) + }) + + Describe("setMax", func() { + When("resource sets of lhs and rhs are not equal", func() { + It("resultant resource sets are merged even if its quantity is zero", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": two, + }) + rhs := ResourceList(corev1.ResourceList{ + "n2": one, + "n3": two, + "n4": zero, + }) + + lhs.SetMax(rhs) + + Expect(lhs.EqualTo(ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": two, + "n4": zero, + }))).Should(Equal(true)) + }) + }) + + When("resource sets of lhs and rhs are equal", func() { + It("just takes max resource quantities", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": one, + "n3": two, + "n4": two, + }) + rhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": one, + "n4": two, + }) + + lhs.SetMax(rhs) + + Expect(lhs.EqualTo(ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": two, + "n4": two, + }))).Should(Equal(true)) + }) + }) + }) + + Describe("setMin", func() { + When("resource sets of lhs and rhs are not equal", func() { + It("resultant resource sets are minimum of intersection", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": one, + }) + rhs := ResourceList(corev1.ResourceList{ + "n2": one, + "n3": two, + "n4": one, + }) + + lhs.SetMin(rhs) + + Expect(lhs.EqualTo(ResourceList(corev1.ResourceList{ + "n2": one, + "n3": one, + }))).Should(Equal(true)) + }) + }) + + When("resource sets of lhs and rhs are equal", func() { + It("takes minimum resource quantities even if its quantity is zero", func() { + lhs := ResourceList(corev1.ResourceList{ + "n1": one, + "n2": two, + "n3": zero, + "n4": two, + }) + rhs := ResourceList(corev1.ResourceList{ + "n1": two, + "n2": zero, + "n3": two, + "n4": one, + }) + + lhs.SetMin(rhs) + + Expect(rhs.EqualTo(ResourceList(corev1.ResourceList{ + "n1": one, + "n2": zero, + "n3": zero, + "n4": one, + }))) + }) + }) + }) +}) diff --git a/pkg/scheduler_plugin/plugin.go b/pkg/scheduler_plugin/plugin.go index e0cc8a8..abf03c7 100644 --- a/pkg/scheduler_plugin/plugin.go +++ b/pkg/scheduler_plugin/plugin.go @@ -19,40 +19,215 @@ package scheduler_plugin import ( "context" + "fmt" + "strings" + "time" + goruntime "runtime" + + schedulev1alpha1 "github.com/everpeace/kube-throttler/pkg/apis/schedule/v1alpha1" + "github.com/everpeace/kube-throttler/pkg/controllers" + scheduleclient "github.com/everpeace/kube-throttler/pkg/generated/clientset/versioned" + scheduleinformers "github.com/everpeace/kube-throttler/pkg/generated/informers/externalversions" + "github.com/pkg/errors" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/clock" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/klog/v2" "k8s.io/kubernetes/pkg/scheduler/framework" ) const ( - // Name of the plugin used in the plugin registry and configurations. - Name = "KubeThrottler" + // PluginName of the plugin used in the plugin registry and configurations. + PluginName = "kube-throttler" ) type KubeThrottler struct { - fh framework.Handle + fh framework.Handle + throttleCtr *controllers.ThrottleController + clusterThrottleCtr *controllers.ClusterThrottleController } -var _ framework.FilterPlugin = &KubeThrottler{} +var _ framework.PreFilterPlugin = &KubeThrottler{} +var _ framework.ReservePlugin = &KubeThrottler{} func (p *KubeThrottler) Name() string { - return Name + return PluginName } // New initializes a new plugin and returns it. -func New(_ runtime.Object, fh framework.Handle) (framework.Plugin, error) { +func New(configuration runtime.Object, fh framework.Handle) (framework.Plugin, error) { + ctx := context.TODO() + + kubeThrottlerArgs, err := DecodePluginArgs(configuration) + if err != nil { + return nil, err + } + + restConfig, err := clientcmd.BuildConfigFromFlags("", kubeThrottlerArgs.KubeConifg) + if err != nil { + return nil, err + } + + scheduleClientset := scheduleclient.NewForConfigOrDie(restConfig) + scheduleInformerFactory := scheduleinformers.NewSharedInformerFactory(scheduleClientset, 5*time.Minute) + throttleInformer := scheduleInformerFactory.Schedule().V1alpha1().Throttles() + clusterthrottleInformer := scheduleInformerFactory.Schedule().V1alpha1().ClusterThrottles() + + // we don't use informerFactory in frameworkHandle + // because podInformer in the frameworkHandle does not have indexer + // that is too inefficient to list pod in some namespace. + // see: https://github.com/kubernetes/kubernetes/blob/v1.20.5/pkg/scheduler/scheduler.go#L671 + clientset := kubernetes.NewForConfigOrDie(restConfig) + informerFactory := informers.NewSharedInformerFactory(clientset, 5*time.Minute) + podInformer := informerFactory.Core().V1().Pods() + namespaceInformer := informerFactory.Core().V1().Namespaces() + + throttleController := controllers.NewThrottleController( + kubeThrottlerArgs.Name, + kubeThrottlerArgs.TargetSchedulerName, + kubeThrottlerArgs.ReconcileTemporaryThresholdInterval, + *scheduleClientset, + throttleInformer, + podInformer, + clock.RealClock{}, + ) + clusterthrottleController := controllers.NewClusterThrottleController( + kubeThrottlerArgs.Name, + kubeThrottlerArgs.TargetSchedulerName, + kubeThrottlerArgs.ReconcileTemporaryThresholdInterval, + *scheduleClientset, + clusterthrottleInformer, + podInformer, + namespaceInformer, + clock.RealClock{}, + ) + + scheduleInformerFactory.Start(ctx.Done()) + informerFactory.Start(ctx.Done()) + if err := throttleController.Start(goruntime.NumCPU(), context.Background().Done()); err != nil { + return nil, err + } + if err := clusterthrottleController.Start(goruntime.NumCPU(), context.Background().Done()); err != nil { + return nil, err + } + pl := KubeThrottler{ - fh: fh, + fh: fh, + throttleCtr: throttleController, + clusterThrottleCtr: clusterthrottleController, } + return &pl, nil } -func (p *KubeThrottler) Filter( +func (pl *KubeThrottler) PreFilter( ctx context.Context, state *framework.CycleState, pod *v1.Pod, - nodeInfo *framework.NodeInfo, ) *framework.Status { + thrActive, thrInsufficient, thrAffected, err := pl.throttleCtr.CheckThrottled(pod, false) + if err != nil { + return framework.NewStatus(framework.Error, err.Error()) + } + klog.V(2).InfoS("PreFilter: throttle check result", + "Pod", pod.Namespace+"/"+pod.Name, + "#ActiveThrottles", len(thrActive), "#InsufficientThrottles", len(thrInsufficient), "#AffectedThrottles", len(thrAffected), + ) + + clthrActive, clthrInsufficient, clThrAffected, err := pl.clusterThrottleCtr.CheckThrottled(pod, false) + if err != nil { + return framework.NewStatus(framework.Error, err.Error()) + } + klog.V(2).InfoS("PreFilter: clusterthrottle check result", + "Pod", pod.Namespace+"/"+pod.Name, + "#ActiveClusterThrottles", len(clthrActive), "#InsufficientClusterThrottles", len(clthrInsufficient), "#AffectedClusterThrottles", len(clThrAffected), + ) + + if len(thrActive)+len(thrInsufficient)+len(clthrActive)+len(clthrInsufficient) == 0 { + return framework.NewStatus(framework.Success) + } + + reasons := []string{} + if len(clthrActive) > 0 { + reasons = append(reasons, fmt.Sprintf("clusterthrottle[%s]=%s", schedulev1alpha1.CheckThrottleStatusActive, strings.Join(clusterThrottleNames(clthrActive), ","))) + } + if len(thrActive) > 0 { + reasons = append(reasons, fmt.Sprintf("throttle[%s]=%s", schedulev1alpha1.CheckThrottleStatusActive, strings.Join(throttleNames(thrActive), ","))) + } + if len(clthrInsufficient) != 0 { + reasons = append(reasons, fmt.Sprintf("clusterthrottle[%s]=%s", schedulev1alpha1.CheckThrottleStatusInsufficient, strings.Join(clusterThrottleNames(clthrInsufficient), ","))) + } + if len(thrInsufficient) != 0 { + reasons = append(reasons, fmt.Sprintf("throttle[%s]=%s", schedulev1alpha1.CheckThrottleStatusInsufficient, strings.Join(throttleNames(thrInsufficient), ","))) + } + return framework.NewStatus(framework.UnschedulableAndUnresolvable, reasons...) +} + +func (pl *KubeThrottler) Reserve( + ctx context.Context, + state *framework.CycleState, + pod *v1.Pod, + node string, +) *framework.Status { + errs := []string{} + err := pl.throttleCtr.Reserve(pod) + if err != nil { + errs = append(errs, errors.Wrapf(err, "Failed to reserve pod=%s/%s in ThrottleController", pod.Namespace, pod.Name).Error()) + } + err = pl.clusterThrottleCtr.Reserve(pod) + if err != nil { + errs = append(errs, errors.Wrapf(err, "Failed to reserve pod=%s/%s in ClusterThrottleController", pod.Namespace, pod.Name).Error()) + } + + if len(errs) != 0 { + return framework.NewStatus(framework.Error, errs...) + } + klog.V(2).InfoS("Reserve: pod is reserved", "pod", pod.Namespace+"/"+pod.Name) + return framework.NewStatus(framework.Success) +} + +func (pl *KubeThrottler) Unreserve( + ctx context.Context, + state *framework.CycleState, + pod *v1.Pod, + node string, +) { + // FIXME: How to handle error if it happened?? + err := pl.throttleCtr.UnReserve(pod) + if err != nil { + utilruntime.HandleError(errors.Wrapf(err, "Failed to unreserve pod %s/%s in ThrottleController", pod.Namespace, pod.Name)) + } + pod.GetName() + err = pl.clusterThrottleCtr.UnReserve(pod) + if err != nil { + utilruntime.HandleError(errors.Wrapf(err, "Failed to unreserve pod %s/%s in ClusterThrottleController", pod.Namespace, pod.Name)) + } + + klog.V(2).InfoS("Unreserve: pod is unreserved", "pod", pod.Namespace+"/"+pod.Name) +} + +func (p *KubeThrottler) PreFilterExtensions() framework.PreFilterExtensions { return nil } + +func throttleNames(objs []schedulev1alpha1.Throttle) []string { + names := make([]string, len(objs)) + for i, obj := range objs { + names[i] = types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}.String() + } + return names +} + +func clusterThrottleNames(objs []schedulev1alpha1.ClusterThrottle) []string { + names := make([]string, len(objs)) + for i, obj := range objs { + names[i] = types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}.String() + } + return names +} diff --git a/pkg/scheduler_plugin/plugin_args.go b/pkg/scheduler_plugin/plugin_args.go new file mode 100644 index 0000000..4754a49 --- /dev/null +++ b/pkg/scheduler_plugin/plugin_args.go @@ -0,0 +1,54 @@ +// Licensed to Shingo Omura under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Shingo Omura licenses this file to you 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 scheduler_plugin + +import ( + "fmt" + "time" + + "k8s.io/apimachinery/pkg/runtime" + fwkruntime "k8s.io/kubernetes/pkg/scheduler/framework/runtime" +) + +var ( + DefaultReconcileTemporaryThresholdInterval = 15 * time.Second +) + +type KubeThrottlerPluginArgs struct { + Name string `json:"name"` + KubeConifg string `json:"kubeconfig"` + ReconcileTemporaryThresholdInterval time.Duration `json:"reconcileTemporaryThresholdInterval"` + TargetSchedulerName string `json:"targetSchedulerName"` +} + +func DecodePluginArgs(configuration runtime.Object) (*KubeThrottlerPluginArgs, error) { + args := &KubeThrottlerPluginArgs{} + if err := fwkruntime.DecodeInto(configuration, &args); err != nil { + return nil, fmt.Errorf("Failed to decode into %s PluginConfig", PluginName) + } + if args.Name == "" { + return nil, fmt.Errorf("Name must not be empty") + } + if args.TargetSchedulerName == "" { + return nil, fmt.Errorf("TargetSchedulerName must not be empty") + } + if args.ReconcileTemporaryThresholdInterval == 0 { + args.ReconcileTemporaryThresholdInterval = DefaultReconcileTemporaryThresholdInterval + } + return args, nil +} From e1b79704c1399a75eca9fe6e5fb4365c3892afe8 Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Wed, 6 Oct 2021 14:19:48 +0900 Subject: [PATCH 07/16] enable specify scheduler-name and throttler-name for dev --- Makefile | 11 ++++++++--- hack/dev/scheduler-config.yaml.template | 6 +++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 9ea16b2..25ee647 100644 --- a/Makefile +++ b/Makefile @@ -124,20 +124,25 @@ endef # $ curl curl -XPUT --data 'N' localhost:10251/debug/flags/v # KUBECONFIG ?= $(HOME)/.kube/config +THROTTLER_NAME ?= kube-throttler +SCHEDULER_NAME ?= my-scheduler .PHONY: dev-scheduler-conf dev-scheduler-conf: mkdir -p .dev - KUBECONFIG=$(KUBECONFIG) envsubst < ./hack/dev/scheduler-config.yaml.template > ./hack/dev/scheduler-config.yaml + KUBECONFIG=$(KUBECONFIG) \ + THROTTLER_NAME=$(THROTTLER_NAME) \ + SCHEDULER_NAME=$(SCHEDULER_NAME) \ + envsubst < ./hack/dev/scheduler-config.yaml.template > ./hack/dev/scheduler-config.yaml .PHONY: dev-run dev-run: dev-scheduler-conf go run main.go kube-scheduler \ --config=./hack/dev/scheduler-config.yaml \ - -v=3 + -v=4 .PHONY: dev-run-debug dev-run-debug: dev-scheduler-conf dlv debug --headless --listen=0.0.0.0:2345 --api-version=2 --log main.go -- kube-scheduler \ --config=./hack/dev/scheduler-config.yaml \ --kubeconfig=$(HOME)/.kube/config \ - --v=3 + --v=4 diff --git a/hack/dev/scheduler-config.yaml.template b/hack/dev/scheduler-config.yaml.template index a91bb36..19dd91c 100644 --- a/hack/dev/scheduler-config.yaml.template +++ b/hack/dev/scheduler-config.yaml.template @@ -7,7 +7,7 @@ clientConnection: podMaxBackoffSeconds: 1 percentageOfNodesToScore: 100 profiles: -- schedulerName: my-scheduler +- schedulerName: ${SCHEDULER_NAME} plugins: preFilter: enabled: @@ -20,6 +20,6 @@ profiles: pluginConfig: - name: kube-throttler args: - name: kube-throttler + name: ${THROTTLER_NAME} kubeconfig: ${KUBECONFIG} - targetSchedulerName: my-scheduler + targetSchedulerName: ${SCHEDULER_NAME} From 2e2325c75066eccff727493ad5c1b613d7d88392 Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Wed, 6 Oct 2021 14:20:21 +0900 Subject: [PATCH 08/16] no reconcile throttles which are not responsible for --- pkg/controllers/clusterthrottle_controller.go | 19 +++++++++++++++++++ pkg/controllers/throttle_controller.go | 19 ++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/pkg/controllers/clusterthrottle_controller.go b/pkg/controllers/clusterthrottle_controller.go index e3a9cab..e203907 100644 --- a/pkg/controllers/clusterthrottle_controller.go +++ b/pkg/controllers/clusterthrottle_controller.go @@ -329,16 +329,26 @@ func (c *ClusterThrottleController) setupEventHandler() { c.clusterthrottleInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { thr := obj.(*v1alpha1.ClusterThrottle) + if !c.isResponsibleFor(thr) { + return + } + klog.V(4).InfoS("Add event", "ClusterThrottle", thr.Name) c.enqueueClusterThrottle(thr) }, UpdateFunc: func(oldObj, newObj interface{}) { thr := newObj.(*v1alpha1.ClusterThrottle) + if !c.isResponsibleFor(thr) { + return + } klog.V(4).InfoS("Update event", "ClusterThrottle", thr.Name) c.enqueueClusterThrottle(thr) }, DeleteFunc: func(obj interface{}) { thr := obj.(*v1alpha1.ClusterThrottle) + if !c.isResponsibleFor(thr) { + return + } klog.V(4).InfoS("Add event", "ClusterThrottle", thr.Name) c.enqueueClusterThrottle(thr) }, @@ -347,6 +357,9 @@ func (c *ClusterThrottleController) setupEventHandler() { c.podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { pod := obj.(*corev1.Pod) + if !c.shouldCountIn(pod) { + return + } klog.V(4).InfoS("Add event", "Pod", pod.Namespace+"/"+pod.Name) throttles, err := c.affectedClusterThrottles(pod) @@ -363,6 +376,9 @@ func (c *ClusterThrottleController) setupEventHandler() { UpdateFunc: func(oldObj, newObj interface{}) { oldPod := oldObj.(*corev1.Pod) newPod := newObj.(*corev1.Pod) + if !c.shouldCountIn(oldPod) && !c.shouldCountIn(newPod) { + return + } klog.V(4).InfoS("Update event", "Pod", newPod.Namespace+"/"+newPod.Name) throttleNames := sets.NewString() @@ -395,6 +411,9 @@ func (c *ClusterThrottleController) setupEventHandler() { }, DeleteFunc: func(obj interface{}) { pod := obj.(*corev1.Pod) + if !c.shouldCountIn(pod) { + return + } klog.V(4).InfoS("Delete event", "Pod", pod.Namespace+"/"+pod.Name) // observe the deleted pod is now scheduled. controller should unreserve it. if isScheduled(pod) { diff --git a/pkg/controllers/throttle_controller.go b/pkg/controllers/throttle_controller.go index fbf427f..eff2ac9 100644 --- a/pkg/controllers/throttle_controller.go +++ b/pkg/controllers/throttle_controller.go @@ -308,16 +308,25 @@ func (c *ThrottleController) setupEventHandler() { c.throttleInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { thr := obj.(*v1alpha1.Throttle) + if !c.isResponsibleFor(thr) { + return + } klog.V(4).InfoS("Add event", "Throttle", thr.Namespace+"/"+thr.Name) c.enqueueThrottle(thr) }, UpdateFunc: func(oldObj, newObj interface{}) { thr := newObj.(*v1alpha1.Throttle) + if !c.isResponsibleFor(thr) { + return + } klog.V(4).InfoS("Update event", "Throttle", thr.Namespace+"/"+thr.Name) c.enqueueThrottle(thr) }, DeleteFunc: func(obj interface{}) { thr := obj.(*v1alpha1.Throttle) + if !c.isResponsibleFor(thr) { + return + } klog.V(4).InfoS("Add event", "Throttle", thr.Namespace+"/"+thr.Name) c.enqueueThrottle(thr) }, @@ -326,7 +335,9 @@ func (c *ThrottleController) setupEventHandler() { c.podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { pod := obj.(*corev1.Pod) - + if !c.shouldCountIn(pod) { + return + } klog.V(4).InfoS("Add event", "Pod", pod.Namespace+"/"+pod.Name) throttles, err := c.affectedThrottles(pod) @@ -343,6 +354,9 @@ func (c *ThrottleController) setupEventHandler() { UpdateFunc: func(oldObj, newObj interface{}) { oldPod := oldObj.(*corev1.Pod) newPod := newObj.(*corev1.Pod) + if !c.shouldCountIn(oldPod) && !c.shouldCountIn(newPod) { + return + } klog.V(4).InfoS("Update event", "Pod", newPod.Namespace+"/"+newPod.Name) throttleNames := sets.NewString() @@ -375,6 +389,9 @@ func (c *ThrottleController) setupEventHandler() { }, DeleteFunc: func(obj interface{}) { pod := obj.(*corev1.Pod) + if !c.shouldCountIn(pod) { + return + } klog.V(4).InfoS("Delete event", "Pod", pod.Namespace+"/"+pod.Name) // observe the deleted pod is now scheduled. controller should unreserve it. if isScheduled(pod) { From 6f04bf42a85c5de74b84032ba18eccdb5213739e Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Wed, 6 Oct 2021 14:41:35 +0900 Subject: [PATCH 09/16] more structured logging --- pkg/controllers/clusterthrottle_controller.go | 6 ++++-- pkg/controllers/throttle_controller.go | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/controllers/clusterthrottle_controller.go b/pkg/controllers/clusterthrottle_controller.go index e203907..a30b68f 100644 --- a/pkg/controllers/clusterthrottle_controller.go +++ b/pkg/controllers/clusterthrottle_controller.go @@ -107,10 +107,12 @@ func (c *ClusterThrottleController) reconcile(key string) error { } affectedPods, err := c.affectedPods(thr) - klog.V(2).Infof("ClusterThrottle %s/%s affects to %d pods", thr.Namespace, thr.Name, len(affectedPods)) if err != nil { return err } + if len(affectedPods) > 0 { + klog.V(2).InfoS("Affected pods detected", "ClusterThrottle", thr.Namespace+"/"+thr.Name, "#AffectedPods", len(affectedPods)) + } used := schedulev1alpha1.ResourceAmount{} for _, p := range affectedPods { @@ -152,7 +154,7 @@ func (c *ClusterThrottleController) reconcile(key string) error { if len(thr.Spec.TemporaryThresholdOverrides) > 0 { go func(_thr *v1alpha1.ClusterThrottle) { - klog.V(3).Infof("Reconciling after duration", "ClusterThrottle", thr.Namespace+"/"+thr.Name, "After", c.reconcileTemporaryThresholdInterval) + klog.V(3).InfoS("Reconciling after duration", "ClusterThrottle", thr.Namespace+"/"+thr.Name, "After", c.reconcileTemporaryThresholdInterval) <-c.clock.After(c.reconcileTemporaryThresholdInterval) c.enqueueClusterThrottle(_thr) }(thr) diff --git a/pkg/controllers/throttle_controller.go b/pkg/controllers/throttle_controller.go index eff2ac9..bb99705 100644 --- a/pkg/controllers/throttle_controller.go +++ b/pkg/controllers/throttle_controller.go @@ -104,10 +104,12 @@ func (c *ThrottleController) reconcile(key string) error { } affectedPods, err := c.affectedPods(thr) - klog.V(2).Infof("Throttle %s/%s affects to %d pods", thr.Namespace, thr.Name, len(affectedPods)) if err != nil { return err } + if len(affectedPods) > 0 { + klog.V(2).InfoS("Affected pods detected", "Throttle", thr.Namespace+"/"+thr.Name, "#AffectedPods", len(affectedPods)) + } used := schedulev1alpha1.ResourceAmount{} for _, p := range affectedPods { @@ -149,7 +151,7 @@ func (c *ThrottleController) reconcile(key string) error { if len(thr.Spec.TemporaryThresholdOverrides) > 0 { go func(_thr *v1alpha1.Throttle) { - klog.V(3).Infof("Reconciling after duration", "Throttle", thr.Namespace+"/"+thr.Name, "After", c.reconcileTemporaryThresholdInterval) + klog.V(3).InfoS("Reconciling after duration", "Throttle", thr.Namespace+"/"+thr.Name, "After", c.reconcileTemporaryThresholdInterval) <-c.clock.After(c.reconcileTemporaryThresholdInterval) c.enqueueThrottle(_thr) }(thr) From 3b52cb1a2affcbdc8799eeef5fc68c85ca4c5d34 Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Wed, 6 Oct 2021 15:50:55 +0900 Subject: [PATCH 10/16] reconcile overrides in more clever way --- .../v1alpha1/temporary_threshold_override.go | 26 ++++++++++++++--- pkg/apis/schedule/v1alpha1/throttle_types.go | 28 +++++++++++++++++++ pkg/controllers/clusterthrottle_controller.go | 27 +++++++++++++----- pkg/controllers/throttle_controller.go | 28 +++++++++++++------ 4 files changed, 90 insertions(+), 19 deletions(-) diff --git a/pkg/apis/schedule/v1alpha1/temporary_threshold_override.go b/pkg/apis/schedule/v1alpha1/temporary_threshold_override.go index e8d45f8..75c7d05 100644 --- a/pkg/apis/schedule/v1alpha1/temporary_threshold_override.go +++ b/pkg/apis/schedule/v1alpha1/temporary_threshold_override.go @@ -30,21 +30,39 @@ type TemporaryThresholdOverride struct { Threshold ResourceAmount `json:"threshold"` } -func (o TemporaryThresholdOverride) IsActive(now time.Time) (bool, error) { +func (o TemporaryThresholdOverride) BeginTime() (time.Time, error) { var err error - var beginTime, endTime time.Time + var beginTime time.Time if o.Begin != "" { beginTime, err = time.Parse(time.RFC3339, o.Begin) if err != nil { - return false, errors.Wrap(err, "Failed to parse Begin") + return beginTime, errors.Wrap(err, "Failed to parse Begin") } } + return beginTime, nil +} + +func (o TemporaryThresholdOverride) EndTime() (time.Time, error) { + var endTime time.Time if o.End != "" { + var err error endTime, err = time.Parse(time.RFC3339, o.End) if err != nil { - return false, errors.Wrap(err, "Failed to parse End") + return endTime, errors.Wrap(err, "Failed to parse End") } } + return endTime, nil +} + +func (o TemporaryThresholdOverride) IsActive(now time.Time) (bool, error) { + beginTime, err := o.BeginTime() + if err != nil { + return false, err + } + endTime, err := o.EndTime() + if err != nil { + return false, err + } begin := (beginTime.Before(now) || beginTime.Equal(now)) end := (endTime.IsZero() || (now.Before(endTime) || now.Equal(endTime))) diff --git a/pkg/apis/schedule/v1alpha1/throttle_types.go b/pkg/apis/schedule/v1alpha1/throttle_types.go index b4352f9..8d68ec8 100644 --- a/pkg/apis/schedule/v1alpha1/throttle_types.go +++ b/pkg/apis/schedule/v1alpha1/throttle_types.go @@ -34,6 +34,34 @@ type ThrottleSpecBase struct { TemporaryThresholdOverrides []TemporaryThresholdOverride `json:"temporaryThresholdOverrides,omitempty"` } +func (b ThrottleSpecBase) NextOverrideHappensIn(now time.Time) (*time.Duration, error) { + var nextHappenAfter *time.Duration + updateIfNeeded := func(d time.Duration) { + if nextHappenAfter == nil || *nextHappenAfter > d { + nextHappenAfter = &d + } + } + for _, o := range b.TemporaryThresholdOverrides { + beginTime, err := o.BeginTime() + if err != nil { + continue + } + if beginTime.After(now) { + updateIfNeeded(beginTime.Sub(now)) + } + + endTime, err := o.EndTime() + if err != nil { + continue + } + if endTime.After(now) { + updateIfNeeded(endTime.Sub(now)) + } + } + + return nextHappenAfter, nil +} + func (b ThrottleSpecBase) CalculateThreshold(now time.Time) CalculatedThreshold { calculated := CalculatedThreshold{ CalculatedAt: metav1.Time{Time: now}, diff --git a/pkg/controllers/clusterthrottle_controller.go b/pkg/controllers/clusterthrottle_controller.go index a30b68f..f9b0e59 100644 --- a/pkg/controllers/clusterthrottle_controller.go +++ b/pkg/controllers/clusterthrottle_controller.go @@ -92,6 +92,8 @@ func NewClusterThrottleController( func (c *ClusterThrottleController) reconcile(key string) error { klog.V(2).InfoS("Reconciling ClusterThrottle", "ClusterThrottle", key) ctx := context.Background() + now := c.clock.Now() + _, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key)) @@ -121,7 +123,7 @@ func (c *ClusterThrottleController) reconcile(key string) error { newStatus := thr.Status.DeepCopy() newStatus.Used = used - calculatedThreshold := thr.Spec.CalculateThreshold(c.clock.Now()) + calculatedThreshold := thr.Spec.CalculateThreshold(now) if !apiequality.Semantic.DeepEqual(thr.Status.CalculatedThreshold.Threshold, calculatedThreshold.Threshold) || !apiequality.Semantic.DeepEqual(thr.Status.CalculatedThreshold.Messages, calculatedThreshold.Messages) { klog.V(2).InfoS("New calculatedThreshold will take effect", @@ -152,12 +154,13 @@ func (c *ClusterThrottleController) reconcile(key string) error { c.UnReserveOnClusterThrottle(p, thr) } - if len(thr.Spec.TemporaryThresholdOverrides) > 0 { - go func(_thr *v1alpha1.ClusterThrottle) { - klog.V(3).InfoS("Reconciling after duration", "ClusterThrottle", thr.Namespace+"/"+thr.Name, "After", c.reconcileTemporaryThresholdInterval) - <-c.clock.After(c.reconcileTemporaryThresholdInterval) - c.enqueueClusterThrottle(_thr) - }(thr) + nextOverrideHappensIn, err := thr.Spec.NextOverrideHappensIn(now) + if err != nil { + return err + } + if nextOverrideHappensIn != nil { + klog.V(3).InfoS("Reconciling after duration", "ClusterThrottle", thr.Namespace+"/"+thr.Name, "After", nextOverrideHappensIn) + c.enqueueClusterThrottleAfter(thr, *nextOverrideHappensIn) } return nil @@ -459,6 +462,16 @@ func (c *ClusterThrottleController) Start(threadiness int, stopCh <-chan struct{ return nil } +func (c *ClusterThrottleController) enqueueClusterThrottleAfter(obj interface{}, duration time.Duration) { + var key string + var err error + if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { + utilruntime.HandleError(err) + return + } + c.workqueue.AddAfter(key, duration) +} + func (c *ClusterThrottleController) enqueueClusterThrottle(obj interface{}) { var key string var err error diff --git a/pkg/controllers/throttle_controller.go b/pkg/controllers/throttle_controller.go index bb99705..1db878d 100644 --- a/pkg/controllers/throttle_controller.go +++ b/pkg/controllers/throttle_controller.go @@ -89,6 +89,8 @@ func NewThrottleController( func (c *ThrottleController) reconcile(key string) error { klog.V(2).InfoS("Reconciling Throttle", "Throttle", key) ctx := context.Background() + now := c.clock.Now() + namespace, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key)) @@ -118,7 +120,7 @@ func (c *ThrottleController) reconcile(key string) error { newStatus := thr.Status.DeepCopy() newStatus.Used = used - calculatedThreshold := thr.Spec.CalculateThreshold(c.clock.Now()) + calculatedThreshold := thr.Spec.CalculateThreshold(now) if !apiequality.Semantic.DeepEqual(thr.Status.CalculatedThreshold.Threshold, calculatedThreshold.Threshold) || !apiequality.Semantic.DeepEqual(thr.Status.CalculatedThreshold.Messages, calculatedThreshold.Messages) { klog.V(2).InfoS("New calculatedThreshold will take effect", @@ -149,12 +151,13 @@ func (c *ThrottleController) reconcile(key string) error { c.UnReserveOnThrottle(p, thr) } - if len(thr.Spec.TemporaryThresholdOverrides) > 0 { - go func(_thr *v1alpha1.Throttle) { - klog.V(3).InfoS("Reconciling after duration", "Throttle", thr.Namespace+"/"+thr.Name, "After", c.reconcileTemporaryThresholdInterval) - <-c.clock.After(c.reconcileTemporaryThresholdInterval) - c.enqueueThrottle(_thr) - }(thr) + nextOverrideHappensIn, err := thr.Spec.NextOverrideHappensIn(now) + if err != nil { + return err + } + if nextOverrideHappensIn != nil { + klog.V(3).InfoS("Reconciling after duration", "Throttle", thr.Namespace+"/"+thr.Name, "After", nextOverrideHappensIn) + c.enqueueThrottleAfter(thr, *nextOverrideHappensIn) } return nil @@ -436,6 +439,16 @@ func (c *ThrottleController) Start(threadiness int, stopCh <-chan struct{}) erro return nil } +func (c *ThrottleController) enqueueThrottleAfter(obj interface{}, duration time.Duration) { + var key string + var err error + if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { + utilruntime.HandleError(err) + return + } + c.workqueue.AddAfter(key, duration) +} + func (c *ThrottleController) enqueueThrottle(obj interface{}) { var key string var err error @@ -445,7 +458,6 @@ func (c *ThrottleController) enqueueThrottle(obj interface{}) { } c.workqueue.Add(key) } - func (c *ThrottleController) runWorker() { for c.processNextWorkItem() { } From e0f4aa7e2e5bbee2212823fc96763ce84d5b9471 Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Wed, 6 Oct 2021 16:05:52 +0900 Subject: [PATCH 11/16] update kubernetes version to 1.20.10 --- go.mod | 64 ++++++++--------- go.sum | 215 +++++++++++++++++++++++---------------------------------- 2 files changed, 117 insertions(+), 162 deletions(-) diff --git a/go.mod b/go.mod index 92ab1bf..26eb97e 100644 --- a/go.mod +++ b/go.mod @@ -9,43 +9,43 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.7.1 github.com/spf13/cobra v1.2.1 - k8s.io/api v0.21.4 - k8s.io/apimachinery v0.21.4 - k8s.io/apiserver v0.21.4 - k8s.io/client-go v0.21.4 - k8s.io/component-base v0.21.4 + k8s.io/api v0.20.10 + k8s.io/apimachinery v0.20.10 + k8s.io/apiserver v0.20.10 + k8s.io/client-go v0.20.10 + k8s.io/component-base v0.20.10 k8s.io/klog/v2 v2.8.0 - k8s.io/kube-scheduler v0.21.4 // indirect + k8s.io/kube-scheduler v0.20.10 // indirect k8s.io/kubernetes v1.20.5 k8s.io/utils v0.0.0-20210629042839-4a2b36d8d73f ) replace ( - // see https://github.com/kubernetes/kubernetes/blob/v1.21.4/go.mod#L475 + // see https://github.com/kubernetes/kubernetes/blob/v1.20.10/go.mod#L475 google.golang.org/grpc => google.golang.org/grpc v1.27.1 - k8s.io/api => k8s.io/api v0.21.4 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.21.4 - k8s.io/apimachinery => k8s.io/apimachinery v0.21.4 - k8s.io/apiserver => k8s.io/apiserver v0.21.4 - k8s.io/cli-runtime => k8s.io/cli-runtime v0.21.4 - k8s.io/client-go => k8s.io/client-go v0.21.4 - k8s.io/cloud-provider => k8s.io/cloud-provider v0.21.4 - k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.21.4 - k8s.io/code-generator => k8s.io/code-generator v0.21.4 - k8s.io/component-base => k8s.io/component-base v0.21.4 - k8s.io/component-helpers => k8s.io/component-helpers v0.21.4 - k8s.io/controller-manager => k8s.io/controller-manager v0.21.4 - k8s.io/cri-api => k8s.io/cri-api v0.21.4 - k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.21.4 - k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.21.4 - k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.21.4 - k8s.io/kube-proxy => k8s.io/kube-proxy v0.21.4 - k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.21.4 - k8s.io/kubectl => k8s.io/kubectl v0.21.4 - k8s.io/kubelet => k8s.io/kubelet v0.21.4 - k8s.io/kubernetes => k8s.io/kubernetes v1.21.4 - k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.21.4 - k8s.io/metrics => k8s.io/metrics v0.21.4 - k8s.io/mount-utils => k8s.io/mount-utils v0.21.4 - k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.21.4 + k8s.io/api => k8s.io/api v0.20.10 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.20.10 + k8s.io/apimachinery => k8s.io/apimachinery v0.20.10 + k8s.io/apiserver => k8s.io/apiserver v0.20.10 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.20.10 + k8s.io/client-go => k8s.io/client-go v0.20.10 + k8s.io/cloud-provider => k8s.io/cloud-provider v0.20.10 + k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.20.10 + k8s.io/code-generator => k8s.io/code-generator v0.20.10 + k8s.io/component-base => k8s.io/component-base v0.20.10 + k8s.io/component-helpers => k8s.io/component-helpers v0.20.10 + k8s.io/controller-manager => k8s.io/controller-manager v0.20.10 + k8s.io/cri-api => k8s.io/cri-api v0.20.10 + k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.20.10 + k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.20.10 + k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.20.10 + k8s.io/kube-proxy => k8s.io/kube-proxy v0.20.10 + k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.20.10 + k8s.io/kubectl => k8s.io/kubectl v0.20.10 + k8s.io/kubelet => k8s.io/kubelet v0.20.10 + k8s.io/kubernetes => k8s.io/kubernetes v1.20.10 + k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.20.10 + k8s.io/metrics => k8s.io/metrics v0.20.10 + k8s.io/mount-utils => k8s.io/mount-utils v0.20.10 + k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.20.10 ) diff --git a/go.sum b/go.sum index 440060f..9142f39 100644 --- a/go.sum +++ b/go.sum @@ -38,14 +38,15 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v43.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= @@ -60,9 +61,8 @@ github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YH github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.10-0.20200715222032-5eafd1556990/go.mod h1:ay/0dTb7NsG8QMDfsRfLHgZo/6xAJShLe1+ePPflihk= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -106,24 +106,22 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313/go.mod h1:P1wt9Z3DP8O6W3rvwCt0REIlshg1InHImaLW0t3ObY0= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/container-storage-interface/spec v1.3.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= +github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= +github.com/container-storage-interface/spec v1.2.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= @@ -132,7 +130,7 @@ github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8h github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/coredns/corefile-migration v1.0.11/go.mod h1:RMy/mXdeDlYwzt0vdMEJvT2hGJ2I86/eO0UdXmH9XNI= +github.com/coredns/corefile-migration v1.0.10/go.mod h1:RMy/mXdeDlYwzt0vdMEJvT2hGJ2I86/eO0UdXmH9XNI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -145,7 +143,6 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.1/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= @@ -153,9 +150,6 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -168,10 +162,11 @@ github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyG github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= @@ -183,7 +178,6 @@ github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw= -github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= @@ -192,17 +186,16 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -243,14 +236,12 @@ github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29g github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw= -github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= @@ -258,11 +249,10 @@ github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tF github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -305,11 +295,13 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= +github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/cadvisor v0.39.0/go.mod h1:rjQFmK4jPCpxeUdLq9bYhNFFsjgGOtpnDmDeap0+nsw= +github.com/google/cadvisor v0.38.8/go.mod h1:1OFB9sOOMkBdUBGCO/1SArawTnDscgMzTodacVDe8mA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -340,7 +332,6 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= @@ -351,6 +342,7 @@ github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyyc github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -388,7 +380,7 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/heketi/heketi v10.2.0+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o= +github.com/heketi/heketi v9.0.1-0.20190917153846-c2e2a4ab7ab9+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o= github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -421,16 +413,17 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= @@ -449,20 +442,18 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -476,11 +467,9 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hxQ= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -488,9 +477,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/mrunalp/fileutils v0.0.0-20200520151820-abd8a0e76976/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -499,14 +487,11 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -526,12 +511,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.0-rc95/go.mod h1:z+bZxa/+Tz/FmYVWkhUajJdzFeOqjc5vrqskhVyHGUM= +github.com/opencontainers/runc v1.0.0-rc92/go.mod h1:X1zlU4p7wOlX4+WRCz+hvlRv8phdL7UqbYD+vQwNMmE= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -585,13 +568,12 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= @@ -631,7 +613,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -647,10 +629,9 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -676,7 +657,6 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= @@ -701,9 +681,8 @@ golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -711,7 +690,6 @@ golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= @@ -719,7 +697,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20210220032938-85be41e4509f/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -736,15 +713,12 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -772,7 +746,6 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -793,9 +766,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -850,12 +821,9 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -878,18 +846,17 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -897,13 +864,9 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -917,9 +880,8 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -949,10 +911,8 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1085,9 +1045,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -1110,7 +1069,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -1121,8 +1079,6 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1130,52 +1086,54 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.21.4 h1:WtDkzTAuI31WZKDPeIYpEUA+WeUfXAmA7gwj6nzFfbc= -k8s.io/api v0.21.4/go.mod h1:fTVGP+M4D8+00FN2cMnJqk/eb/GH53bvmNs2SVTmpFk= -k8s.io/apiextensions-apiserver v0.21.4/go.mod h1:OoC8LhI9LnV+wKjZkXIBbLUwtnOGJiTRE33qctH5CIk= -k8s.io/apimachinery v0.21.4 h1:KDq0lWZVslHkuE5I7iGAQHwpK0aDTlar1E7IWEc4CNw= -k8s.io/apimachinery v0.21.4/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI= -k8s.io/apiserver v0.21.4 h1:egJgdhW0ueq5iJSY0c5YedPvRM2Ft/D3dcXOgwvs9jY= -k8s.io/apiserver v0.21.4/go.mod h1:SErUuFBBPZUcD2nsUU8hItxoYheqyYr2o/pCINEPW8g= -k8s.io/cli-runtime v0.21.4/go.mod h1:eRbLHYkdVWzvG87yrkgGd8CqX6/+fAG9DTdAqTXmlRY= -k8s.io/client-go v0.21.4 h1:tcwj167If+v+pIGrCjaPG7hFo6SqFPFCCgMJy+Vm8Jc= -k8s.io/client-go v0.21.4/go.mod h1:t0/eMKyUAq/DoQ7vW8NVVA00/nomlwC+eInsS8PxSew= -k8s.io/cloud-provider v0.21.4 h1:BPGDdyz49/ohnK3QMDWBtm39QnDm+bXIP5L7mj8AHUQ= -k8s.io/cloud-provider v0.21.4/go.mod h1:9ogsWpFKWcYC0sGPu0YZ3FMLZIlaGBSFDCNXxhlCF1o= -k8s.io/cluster-bootstrap v0.21.4/go.mod h1:GtXGuiEtdV4XQJcscR6qQCm/vtQWkhUi3qnl9KL9jzw= -k8s.io/code-generator v0.21.4/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo= -k8s.io/component-base v0.21.4 h1:Bc0AttSyhJFVXEIHz+VX+D11j/5z7SPPhl6whiXaRzs= -k8s.io/component-base v0.21.4/go.mod h1:ZKG0eHVX+tUDcaoIGpU3Vtk4TIjMddN9uhEWDmW6Nyg= -k8s.io/component-helpers v0.21.4 h1:Q6L3sQ+L5uaaUcsJkhlzU5UchcIYBZ56Y2Bq5k4qOtk= -k8s.io/component-helpers v0.21.4/go.mod h1:/5TBNWmxaAymZweO1JWv3Pt5rcYJV1LbWWY0x1rDdVU= -k8s.io/controller-manager v0.21.4/go.mod h1:a/iL7W19zkyirHDaupk9cyC11nejVznGwZI6I8tbyQY= -k8s.io/cri-api v0.21.4/go.mod h1:ukzeKnOkrG9/+ghKZA57WeZbQfRtqlGLF5GcF3RtHZ8= -k8s.io/csi-translation-lib v0.21.4 h1:BXmdC3qh9hRlOfXRNDHzkwGdACB0ZB9YGIR8LxdR+Lg= -k8s.io/csi-translation-lib v0.21.4/go.mod h1:WtxJW4/3XGhllbRCO4SRkL/MyLhjaRsL6Ds+q0pDHTg= +k8s.io/api v0.20.10 h1:kAdgi1zcyenV88/uVEzS9B/fn1m4KRbmdKB0Lxl6z/M= +k8s.io/api v0.20.10/go.mod h1:0kei3F6biGjtRQBo5dUeujq6Ji3UCh9aOSfp/THYd7I= +k8s.io/apiextensions-apiserver v0.20.10/go.mod h1:am9XHHsM/FJBgPtl586TGSDAouRTLZC6wu25rb2VqCQ= +k8s.io/apimachinery v0.20.10 h1:GcFwz5hsGgKLohcNgv8GrInk60vUdFgBXW7uOY1i1YM= +k8s.io/apimachinery v0.20.10/go.mod h1:kQa//VOAwyVwJ2+L9kOREbsnryfsGSkSM1przND4+mw= +k8s.io/apiserver v0.20.10 h1:9Th11BLOMY5HHHp2AciNNgp/y71XKO+FLHO0pultyrQ= +k8s.io/apiserver v0.20.10/go.mod h1:bBm1wyFuID+0CsEYIdyiamOw9wEniEOq3HrCU40ky2M= +k8s.io/cli-runtime v0.20.10/go.mod h1:O8xLwPPEJsTyiCB/ePt4JgEPbStQUzSD8rt7GJJj6Kw= +k8s.io/client-go v0.20.10 h1:TgAL2pqcNWMH4eZoS9Uw0BLh2lu5a2A4pmegjp5pmsk= +k8s.io/client-go v0.20.10/go.mod h1:fFg+aLoasv/R+xiVaWjxeqGFYltzgQcOQzkFaSRfnJ0= +k8s.io/cloud-provider v0.20.10 h1:aM+UcHOE/woo14Lnc27swz6moi3wpc8FRwBTIHJKrYo= +k8s.io/cloud-provider v0.20.10/go.mod h1:8HQ0NgW661PiH+QK1BPYD0QorpaxOXQs1ZsMfOe3uc0= +k8s.io/cluster-bootstrap v0.20.10/go.mod h1:D+cqd8iJYeajaIUBUca4J3f/87L4qiFvMPzM5qs8x1o= +k8s.io/code-generator v0.20.10/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibghWToU= +k8s.io/component-base v0.20.10 h1:QNlekT6M2zBb4feHHmZ+YHZHcDbhbrYS7xHHY+v+kOE= +k8s.io/component-base v0.20.10/go.mod h1:ZKOEin1xu68aJzxgzl5DZSp5J1IrjAOPlPN90/t6OI8= +k8s.io/component-helpers v0.20.10 h1:6qvZDKiKfyl/Zhxskx+JXMbUDJd+833C6spc2j069xI= +k8s.io/component-helpers v0.20.10/go.mod h1:9SuOCO69yzUr8t9oajyO40NPAYK3JCYXwwyLS3YINR4= +k8s.io/controller-manager v0.20.10 h1:qiiEH0a8c/o6cooCVjtlTnRAOoy7/EBASvhCuXQOTDE= +k8s.io/controller-manager v0.20.10/go.mod h1:NwFcdJR5ZK0pjKNUZuZbus/tO8I0zSkGpp0ifQi2DK0= +k8s.io/cri-api v0.20.10/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/csi-translation-lib v0.20.10 h1:j5Tg/uWJc0WnR+wk0ZO1oJzGoOb/UdjztqVVoYrIgnU= +k8s.io/csi-translation-lib v0.20.10/go.mod h1:dVQvr/Y/74jFZU955V/KqgZJ4E4hRF4IcsxUq0WbUrc= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-aggregator v0.21.4/go.mod h1:SykygeaVEQfqYH5IV8ve7Ia3dEGOGpGrdfD5NBi5yYI= -k8s.io/kube-controller-manager v0.21.4/go.mod h1:/wPS1gIX++/WjsIiimESnkpMqsjiIAMOpjVwjqLo7ng= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= -k8s.io/kube-proxy v0.21.4/go.mod h1:eUxSO/0Z/0JjKYz/aCZdwGea7lazumkTFrqS+OWcVNI= -k8s.io/kube-scheduler v0.21.4 h1:oUVUCM+v6rum1i5vn5C3ZrqPNkp7exWiy7/Tfzbs9ZQ= -k8s.io/kube-scheduler v0.21.4/go.mod h1:zFiUfgeM/dJajfHYG8Bx5fSrNAcLxMHFgN7ARdSJXqQ= -k8s.io/kubectl v0.21.4/go.mod h1:rRYB5HeScoGQKxZDQmus17pTSVIuqfm0D31ApET/qSM= -k8s.io/kubelet v0.21.4/go.mod h1:kgXUz8upYNIngMSEZP1rpg2kp4gfUrsB7ir5u9Cm4HE= -k8s.io/kubernetes v1.21.4 h1:uKnn+MDBG4Bsed/iD3L6gMkq/szAnMqeHuSjkc3WOzQ= -k8s.io/kubernetes v1.21.4/go.mod h1:yNRsD2sfx76jpLKTgr0lJdVnILFWRo7b+HCo94tD48c= -k8s.io/legacy-cloud-providers v0.21.4/go.mod h1:WzvDvkWfD7lKQSaSqqaYsoY3VQeAjhXYN2telpMx8co= -k8s.io/metrics v0.21.4/go.mod h1:uhWoVuVumUMSeCa1B1p2tm4Y4XuZIg0n24QEtB54wuA= -k8s.io/mount-utils v0.21.4 h1:T24Y4FJ9IRkXgA+UkQHr+F+f/nm7sqdkdmdSxTtF+lw= -k8s.io/mount-utils v0.21.4/go.mod h1:dwXbIPxKtTjrBEaX1aK/CMEf1KZ8GzMHpe3NEBfdFXI= -k8s.io/sample-apiserver v0.21.4/go.mod h1:rpVLxky91DoN2OehmyZf/IE+sgop/BBoZl78VJrrs0I= -k8s.io/system-validators v1.4.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6gRrl0Q= +k8s.io/kube-aggregator v0.20.10/go.mod h1:Y8T4ttD/sJIhaL84giy6aP9Q7aF9B23N/2u5KqEa/gQ= +k8s.io/kube-controller-manager v0.20.10/go.mod h1:6XCS/QLajausNkLu/fPe0Pj6TxbpuNd51BQTqSwBIbg= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kube-proxy v0.20.10/go.mod h1:hNgnO70MFOwZHQb87EYukEuy2uUvhrg/XZG2OTnyMy4= +k8s.io/kube-scheduler v0.20.10 h1:McaE+cSjx7gAfX/qV34vNIwAJTJKIPHR9ggDXt6EALw= +k8s.io/kube-scheduler v0.20.10/go.mod h1:GYb7hAdtx2DZ3l8SLhtcS9Qm+U5lgjCJIgF/QYKr1s0= +k8s.io/kubectl v0.20.10/go.mod h1:nM2vIk+39DHuXDmdCL3AehkkhbGUgRAlyPgkfycHoXY= +k8s.io/kubelet v0.20.10/go.mod h1:OYBcBg22jbmycu0V6IMtq0Xpjoxj2iJ+LtH6XWsuesA= +k8s.io/kubernetes v1.20.10 h1:4ng4jMVHQfLtUwD1Bv+xjzVPP2Zp41GeFVWK8x1Wwl0= +k8s.io/kubernetes v1.20.10/go.mod h1:iE/QvEbLD6Ne9U03MX/BTBVSbj3pnj/HewBr8XDzLxw= +k8s.io/legacy-cloud-providers v0.20.10/go.mod h1:5xDo9brr2bkusQr9KLWnUKY2bNZ52+9CJiIaqyGn7CE= +k8s.io/metrics v0.20.10/go.mod h1:M0IJySt6DSTjZQZamJ70b+F7I12pXqaeQmv/VpexC9I= +k8s.io/mount-utils v0.20.10 h1:5FhpTcOnebwLevXDyo457yoGppNl73EiSLPGuv63kME= +k8s.io/mount-utils v0.20.10/go.mod h1:Jv9NRZ5L2LF87A17GaGlArD+r3JAJdZFvo4XD1cG4Kc= +k8s.io/sample-apiserver v0.20.10/go.mod h1:TSEZsVS5JKpV6r1aSVlNlMY17b1Ra2wkuT4LMCRealY= +k8s.io/system-validators v1.2.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6gRrl0Q= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210629042839-4a2b36d8d73f h1:6Cyc8f2OS555SrragQyv4rQ5G7F2haZ6KY2oxO1wzlE= k8s.io/utils v0.0.0-20210629042839-4a2b36d8d73f/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= @@ -1190,10 +1148,7 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22 h1:fmRfl9WJ4ApJn7LxNuED4m0t18qivVQOxP6aAYG9J6c= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/kustomize/api v0.8.8/go.mod h1:He1zoK0nk43Pc6NlV085xDXDXTNprtcyKZVm3swsdNY= -sigs.k8s.io/kustomize/cmd/config v0.9.10/go.mod h1:Mrby0WnRH7hA6OwOYnYpfpiY0WJIMgYrEDfwOeFdMK0= -sigs.k8s.io/kustomize/kustomize/v4 v4.1.2/go.mod h1:PxBvo4WGYlCLeRPL+ziT64wBXqbgfcalOS/SXa/tcyo= -sigs.k8s.io/kustomize/kyaml v0.10.17/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg= +sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= From 84a1560616633c7f9e8ae6e1a1083f52c55f639f Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Wed, 6 Oct 2021 16:28:49 +0900 Subject: [PATCH 12/16] rename scheduler_plugin/New --> scheduler_plugin/NewPlugin --- cmd/kube_scheduler.go | 2 +- pkg/scheduler_plugin/plugin.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/kube_scheduler.go b/cmd/kube_scheduler.go index 31ed426..f36db2b 100644 --- a/cmd/kube_scheduler.go +++ b/cmd/kube_scheduler.go @@ -31,7 +31,7 @@ import ( func kubeSchedulerCmd() *cobra.Command { rand.Seed(time.Now().UnixNano()) command := app.NewSchedulerCommand( - app.WithPlugin(kubethrottler.PluginName, kubethrottler.New), + app.WithPlugin(kubethrottler.PluginName, kubethrottler.NewPlugin), ) command.Short = "run kube-scheduler with kube-throttler plugin (need to enable 'KubeThrottler' plugin in config)" // TODO: once we switch everything over to Cobra commands, we can go back to calling diff --git a/pkg/scheduler_plugin/plugin.go b/pkg/scheduler_plugin/plugin.go index abf03c7..93ae422 100644 --- a/pkg/scheduler_plugin/plugin.go +++ b/pkg/scheduler_plugin/plugin.go @@ -60,8 +60,8 @@ func (p *KubeThrottler) Name() string { return PluginName } -// New initializes a new plugin and returns it. -func New(configuration runtime.Object, fh framework.Handle) (framework.Plugin, error) { +// NewPlugin initializes a new plugin and returns it. +func NewPlugin(configuration runtime.Object, fh framework.Handle) (framework.Plugin, error) { ctx := context.TODO() kubeThrottlerArgs, err := DecodePluginArgs(configuration) From c02545960d147d708fd6798c72045dadb22abb08 Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Wed, 6 Oct 2021 16:36:23 +0900 Subject: [PATCH 13/16] update kuberntes version to 0.20.5 --- go.mod | 64 ++++++++++++++++++++++----------------------- go.sum | 82 +++++++++++++++++++++++++++++----------------------------- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/go.mod b/go.mod index 26eb97e..84b9c1a 100644 --- a/go.mod +++ b/go.mod @@ -9,43 +9,43 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.7.1 github.com/spf13/cobra v1.2.1 - k8s.io/api v0.20.10 - k8s.io/apimachinery v0.20.10 - k8s.io/apiserver v0.20.10 - k8s.io/client-go v0.20.10 - k8s.io/component-base v0.20.10 + k8s.io/api v0.20.5 + k8s.io/apimachinery v0.20.5 + k8s.io/apiserver v0.20.5 + k8s.io/client-go v0.20.5 + k8s.io/component-base v0.20.5 k8s.io/klog/v2 v2.8.0 - k8s.io/kube-scheduler v0.20.10 // indirect + k8s.io/kube-scheduler v0.20.5 // indirect k8s.io/kubernetes v1.20.5 k8s.io/utils v0.0.0-20210629042839-4a2b36d8d73f ) replace ( - // see https://github.com/kubernetes/kubernetes/blob/v1.20.10/go.mod#L475 + // see https://github.com/kubernetes/kubernetes/blob/v1.20.5/go.mod#L473 google.golang.org/grpc => google.golang.org/grpc v1.27.1 - k8s.io/api => k8s.io/api v0.20.10 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.20.10 - k8s.io/apimachinery => k8s.io/apimachinery v0.20.10 - k8s.io/apiserver => k8s.io/apiserver v0.20.10 - k8s.io/cli-runtime => k8s.io/cli-runtime v0.20.10 - k8s.io/client-go => k8s.io/client-go v0.20.10 - k8s.io/cloud-provider => k8s.io/cloud-provider v0.20.10 - k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.20.10 - k8s.io/code-generator => k8s.io/code-generator v0.20.10 - k8s.io/component-base => k8s.io/component-base v0.20.10 - k8s.io/component-helpers => k8s.io/component-helpers v0.20.10 - k8s.io/controller-manager => k8s.io/controller-manager v0.20.10 - k8s.io/cri-api => k8s.io/cri-api v0.20.10 - k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.20.10 - k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.20.10 - k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.20.10 - k8s.io/kube-proxy => k8s.io/kube-proxy v0.20.10 - k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.20.10 - k8s.io/kubectl => k8s.io/kubectl v0.20.10 - k8s.io/kubelet => k8s.io/kubelet v0.20.10 - k8s.io/kubernetes => k8s.io/kubernetes v1.20.10 - k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.20.10 - k8s.io/metrics => k8s.io/metrics v0.20.10 - k8s.io/mount-utils => k8s.io/mount-utils v0.20.10 - k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.20.10 + k8s.io/api => k8s.io/api v0.20.5 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.20.5 + k8s.io/apimachinery => k8s.io/apimachinery v0.20.5 + k8s.io/apiserver => k8s.io/apiserver v0.20.5 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.20.5 + k8s.io/client-go => k8s.io/client-go v0.20.5 + k8s.io/cloud-provider => k8s.io/cloud-provider v0.20.5 + k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.20.5 + k8s.io/code-generator => k8s.io/code-generator v0.20.5 + k8s.io/component-base => k8s.io/component-base v0.20.5 + k8s.io/component-helpers => k8s.io/component-helpers v0.20.5 + k8s.io/controller-manager => k8s.io/controller-manager v0.20.5 + k8s.io/cri-api => k8s.io/cri-api v0.20.5 + k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.20.5 + k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.20.5 + k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.20.5 + k8s.io/kube-proxy => k8s.io/kube-proxy v0.20.5 + k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.20.5 + k8s.io/kubectl => k8s.io/kubectl v0.20.5 + k8s.io/kubelet => k8s.io/kubelet v0.20.5 + k8s.io/kubernetes => k8s.io/kubernetes v1.20.5 + k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.20.5 + k8s.io/metrics => k8s.io/metrics v0.20.5 + k8s.io/mount-utils => k8s.io/mount-utils v0.20.5 + k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.20.5 ) diff --git a/go.sum b/go.sum index 9142f39..44b553d 100644 --- a/go.sum +++ b/go.sum @@ -928,6 +928,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -1086,29 +1087,29 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.10 h1:kAdgi1zcyenV88/uVEzS9B/fn1m4KRbmdKB0Lxl6z/M= -k8s.io/api v0.20.10/go.mod h1:0kei3F6biGjtRQBo5dUeujq6Ji3UCh9aOSfp/THYd7I= -k8s.io/apiextensions-apiserver v0.20.10/go.mod h1:am9XHHsM/FJBgPtl586TGSDAouRTLZC6wu25rb2VqCQ= -k8s.io/apimachinery v0.20.10 h1:GcFwz5hsGgKLohcNgv8GrInk60vUdFgBXW7uOY1i1YM= -k8s.io/apimachinery v0.20.10/go.mod h1:kQa//VOAwyVwJ2+L9kOREbsnryfsGSkSM1przND4+mw= -k8s.io/apiserver v0.20.10 h1:9Th11BLOMY5HHHp2AciNNgp/y71XKO+FLHO0pultyrQ= -k8s.io/apiserver v0.20.10/go.mod h1:bBm1wyFuID+0CsEYIdyiamOw9wEniEOq3HrCU40ky2M= -k8s.io/cli-runtime v0.20.10/go.mod h1:O8xLwPPEJsTyiCB/ePt4JgEPbStQUzSD8rt7GJJj6Kw= -k8s.io/client-go v0.20.10 h1:TgAL2pqcNWMH4eZoS9Uw0BLh2lu5a2A4pmegjp5pmsk= -k8s.io/client-go v0.20.10/go.mod h1:fFg+aLoasv/R+xiVaWjxeqGFYltzgQcOQzkFaSRfnJ0= -k8s.io/cloud-provider v0.20.10 h1:aM+UcHOE/woo14Lnc27swz6moi3wpc8FRwBTIHJKrYo= -k8s.io/cloud-provider v0.20.10/go.mod h1:8HQ0NgW661PiH+QK1BPYD0QorpaxOXQs1ZsMfOe3uc0= -k8s.io/cluster-bootstrap v0.20.10/go.mod h1:D+cqd8iJYeajaIUBUca4J3f/87L4qiFvMPzM5qs8x1o= -k8s.io/code-generator v0.20.10/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibghWToU= -k8s.io/component-base v0.20.10 h1:QNlekT6M2zBb4feHHmZ+YHZHcDbhbrYS7xHHY+v+kOE= -k8s.io/component-base v0.20.10/go.mod h1:ZKOEin1xu68aJzxgzl5DZSp5J1IrjAOPlPN90/t6OI8= -k8s.io/component-helpers v0.20.10 h1:6qvZDKiKfyl/Zhxskx+JXMbUDJd+833C6spc2j069xI= -k8s.io/component-helpers v0.20.10/go.mod h1:9SuOCO69yzUr8t9oajyO40NPAYK3JCYXwwyLS3YINR4= -k8s.io/controller-manager v0.20.10 h1:qiiEH0a8c/o6cooCVjtlTnRAOoy7/EBASvhCuXQOTDE= -k8s.io/controller-manager v0.20.10/go.mod h1:NwFcdJR5ZK0pjKNUZuZbus/tO8I0zSkGpp0ifQi2DK0= -k8s.io/cri-api v0.20.10/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/csi-translation-lib v0.20.10 h1:j5Tg/uWJc0WnR+wk0ZO1oJzGoOb/UdjztqVVoYrIgnU= -k8s.io/csi-translation-lib v0.20.10/go.mod h1:dVQvr/Y/74jFZU955V/KqgZJ4E4hRF4IcsxUq0WbUrc= +k8s.io/api v0.20.5 h1:zsMTffV0Le2EiI0aKvlTHEnXGxk1HiqGRhJcCPiI7JI= +k8s.io/api v0.20.5/go.mod h1:FQjAceXnVaWDeov2YUWhOb6Yt+5UjErkp6UO3nczO1Y= +k8s.io/apiextensions-apiserver v0.20.5/go.mod h1:1HoTwgjWNizJBIgg0Y9P4RdLtaQquilJ5ArGHv9ZpFk= +k8s.io/apimachinery v0.20.5 h1:wO/FxMVRn223rAKxnBbwCyuN96bS9MFTIvP0e/V7cps= +k8s.io/apimachinery v0.20.5/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apiserver v0.20.5 h1:J8l/MJ9pdYncrc1lhxzObDa7jgrUNfettKGNF8mFV+c= +k8s.io/apiserver v0.20.5/go.mod h1:AY3lKhcJ2Tm81XvvcBzk2VnKINSoN+qczYsdo2YEvIc= +k8s.io/cli-runtime v0.20.5/go.mod h1:ihjPeQWDk7NGVIkNEvpwxA3gJvqtU+LtkDj11TvyXn4= +k8s.io/client-go v0.20.5 h1:dJGtYUvFrFGjQ+GjXEIby0gZWdlAOc0xJBJqY3VyDxA= +k8s.io/client-go v0.20.5/go.mod h1:Ee5OOMMYvlH8FCZhDsacjMlCBwetbGZETwo1OA+e6Zw= +k8s.io/cloud-provider v0.20.5 h1:vF/8qZRIfwqNQhd9gv3apZvnvTc4qcZJvYWzzZb0K08= +k8s.io/cloud-provider v0.20.5/go.mod h1:GrzNM+VAk1cy88FJPnF9F/PUPeeD5aqfIZmp2QONG7Y= +k8s.io/cluster-bootstrap v0.20.5/go.mod h1:vr2e5AAGqdWBupioz62IRLvk+SjWqAOq2J2DtIuK6Ak= +k8s.io/code-generator v0.20.5/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= +k8s.io/component-base v0.20.5 h1:8BZQKLJGhWrxtB7kIOEejKDtAKr1HOYvB0PZNeTyLS0= +k8s.io/component-base v0.20.5/go.mod h1:l0isoBLGyQKwRoTWbPHR6jNDd3/VqQD43cNlsjddGng= +k8s.io/component-helpers v0.20.5 h1:JmmGqBM7CaJRUKL6oVFoiM7BT2hE9cxg/yjrPkMhSbk= +k8s.io/component-helpers v0.20.5/go.mod h1:AzTdoPj6YAN2SUfhBX/FUUU3ntfFuse03q/VMLovEsE= +k8s.io/controller-manager v0.20.5 h1:2OZPUfW5Y7LUePa2MckvyhguVO9Ka+iE0/CnsxdOOT0= +k8s.io/controller-manager v0.20.5/go.mod h1:r6R3hxyqNz5De1apuLEJxsZ6hvf3TQPhiH+uPWZXB38= +k8s.io/cri-api v0.20.5/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/csi-translation-lib v0.20.5 h1:H8Olsd1f24fHgY011OmUkOyFCeSy/9VdHZQSNpG665Q= +k8s.io/csi-translation-lib v0.20.5/go.mod h1:KASK4nHVw/T8YW8pyMPh/sLkCpICxXN+A+Z83BplHUk= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM= @@ -1117,22 +1118,22 @@ k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-aggregator v0.20.10/go.mod h1:Y8T4ttD/sJIhaL84giy6aP9Q7aF9B23N/2u5KqEa/gQ= -k8s.io/kube-controller-manager v0.20.10/go.mod h1:6XCS/QLajausNkLu/fPe0Pj6TxbpuNd51BQTqSwBIbg= +k8s.io/kube-aggregator v0.20.5/go.mod h1:0S88kjWs/0UzOMOko6fjy4nwu1OTRrxlpa7rsx0PErA= +k8s.io/kube-controller-manager v0.20.5/go.mod h1:oC7TO9YGTI23FDtgens9eIX8ceXntHeG8xhaPSEgAV4= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-proxy v0.20.10/go.mod h1:hNgnO70MFOwZHQb87EYukEuy2uUvhrg/XZG2OTnyMy4= -k8s.io/kube-scheduler v0.20.10 h1:McaE+cSjx7gAfX/qV34vNIwAJTJKIPHR9ggDXt6EALw= -k8s.io/kube-scheduler v0.20.10/go.mod h1:GYb7hAdtx2DZ3l8SLhtcS9Qm+U5lgjCJIgF/QYKr1s0= -k8s.io/kubectl v0.20.10/go.mod h1:nM2vIk+39DHuXDmdCL3AehkkhbGUgRAlyPgkfycHoXY= -k8s.io/kubelet v0.20.10/go.mod h1:OYBcBg22jbmycu0V6IMtq0Xpjoxj2iJ+LtH6XWsuesA= -k8s.io/kubernetes v1.20.10 h1:4ng4jMVHQfLtUwD1Bv+xjzVPP2Zp41GeFVWK8x1Wwl0= -k8s.io/kubernetes v1.20.10/go.mod h1:iE/QvEbLD6Ne9U03MX/BTBVSbj3pnj/HewBr8XDzLxw= -k8s.io/legacy-cloud-providers v0.20.10/go.mod h1:5xDo9brr2bkusQr9KLWnUKY2bNZ52+9CJiIaqyGn7CE= -k8s.io/metrics v0.20.10/go.mod h1:M0IJySt6DSTjZQZamJ70b+F7I12pXqaeQmv/VpexC9I= -k8s.io/mount-utils v0.20.10 h1:5FhpTcOnebwLevXDyo457yoGppNl73EiSLPGuv63kME= -k8s.io/mount-utils v0.20.10/go.mod h1:Jv9NRZ5L2LF87A17GaGlArD+r3JAJdZFvo4XD1cG4Kc= -k8s.io/sample-apiserver v0.20.10/go.mod h1:TSEZsVS5JKpV6r1aSVlNlMY17b1Ra2wkuT4LMCRealY= +k8s.io/kube-proxy v0.20.5/go.mod h1:DBxEvwMdK9/dJHxY6+VCONxHzBMWeebPMQM0Icr0VfY= +k8s.io/kube-scheduler v0.20.5 h1:V6WUUSawUur3okzD8+ShBPt85V32FcSLHbXhfufOEqI= +k8s.io/kube-scheduler v0.20.5/go.mod h1:oCOwGvakNU458nFM1jRC5rzp1USDOFBFoie0OAEN4I8= +k8s.io/kubectl v0.20.5/go.mod h1:mlNQgyV18D4XFt5BmfSkrxQNS+arT2pXDQxxnH5lMiw= +k8s.io/kubelet v0.20.5/go.mod h1:iM18y0xm/1VlznuHFGBd9YVT9MM15TgEWJrJHrZ4mtQ= +k8s.io/kubernetes v1.20.5 h1:oY1KI7d/2rHETR3xngvQ46vuC1cNPp7R/5vQAVd2vqs= +k8s.io/kubernetes v1.20.5/go.mod h1:aOH+RZJ0PFt6Y/G3vbR0zLeGURGW8X4aX9khigekwAo= +k8s.io/legacy-cloud-providers v0.20.5/go.mod h1:YhCukXmwAh+PLncIZMMMIUD0wSZqw4UGukAKe6ZDMbI= +k8s.io/metrics v0.20.5/go.mod h1:vsptOayjKWKWHvWR1vFQY++vxydzaEo/2+JC7kSDKPU= +k8s.io/mount-utils v0.20.5 h1:S2z/6YtfT1Nc+wRxbSwkfFbVHYDFLoLmD4Nw3Nu8qQY= +k8s.io/mount-utils v0.20.5/go.mod h1:Jv9NRZ5L2LF87A17GaGlArD+r3JAJdZFvo4XD1cG4Kc= +k8s.io/sample-apiserver v0.20.5/go.mod h1:QX9q+uZk/a9+EoRTH56rpoUlgLrsBIaRJukuck27K1o= k8s.io/system-validators v1.2.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6gRrl0Q= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210629042839-4a2b36d8d73f h1:6Cyc8f2OS555SrragQyv4rQ5G7F2haZ6KY2oxO1wzlE= @@ -1146,12 +1147,11 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22 h1:fmRfl9WJ4ApJn7LxNuED4m0t18qivVQOxP6aAYG9J6c= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15 h1:4uqm9Mv+w2MmBYD+F4qf/v6tDFUdPOk29C095RbU5mY= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= From 692c5026e79bf808f6d613a2541869b1146db396 Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Thu, 7 Oct 2021 14:02:26 +0900 Subject: [PATCH 14/16] fix typo in metrics recorders --- pkg/controllers/clusterthrottle_metrics.go | 2 +- pkg/controllers/throttle_metrics.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/controllers/clusterthrottle_metrics.go b/pkg/controllers/clusterthrottle_metrics.go index 1e8a3b4..39b5d7a 100644 --- a/pkg/controllers/clusterthrottle_metrics.go +++ b/pkg/controllers/clusterthrottle_metrics.go @@ -119,7 +119,7 @@ func (r *ClusterThrottleMetricsRecorder) recordClusterThrottleMetrics(thr *v1alp r.recordResourceRequests(r.specThresholdResourceRequestsGauge.MustCurryWith(labels), thr.Spec.Threshold.ResourceRequests) r.recordIsResourceCountThrottled(r.statusThrottledResourceCountsGauge.MustCurryWith(labels), thr.Status.Throttled.ResourceCounts) - r.recordIsResourceRequestsThrottled(r.specThresholdResourceRequestsGauge.MustCurryWith(labels), thr.Status.Throttled.ResourceRequests) + r.recordIsResourceRequestsThrottled(r.statusThrottledResourceRequstsGauge.MustCurryWith(labels), thr.Status.Throttled.ResourceRequests) r.recordResourceCounts(r.statusUsedResourceCountsGauge.MustCurryWith(labels), thr.Status.Used.ResourceCounts) r.recordResourceRequests(r.statusUsedResourceRequestsGauge.MustCurryWith(labels), thr.Status.Used.ResourceRequests) diff --git a/pkg/controllers/throttle_metrics.go b/pkg/controllers/throttle_metrics.go index 3ad4d78..47253a0 100644 --- a/pkg/controllers/throttle_metrics.go +++ b/pkg/controllers/throttle_metrics.go @@ -120,7 +120,7 @@ func (r *ThrottleMetricsRecorder) recordThrottleMetrics(thr *v1alpha1.Throttle) r.recordResourceRequests(r.specThresholdResourceRequestsGauge.MustCurryWith(labels), thr.Spec.Threshold.ResourceRequests) r.recordIsResourceCountThrottled(r.statusThrottledResourceCountsGauge.MustCurryWith(labels), thr.Status.Throttled.ResourceCounts) - r.recordIsResourceRequestsThrottled(r.specThresholdResourceRequestsGauge.MustCurryWith(labels), thr.Status.Throttled.ResourceRequests) + r.recordIsResourceRequestsThrottled(r.statusThrottledResourceRequstsGauge.MustCurryWith(labels), thr.Status.Throttled.ResourceRequests) r.recordResourceCounts(r.statusUsedResourceCountsGauge.MustCurryWith(labels), thr.Status.Used.ResourceCounts) r.recordResourceRequests(r.statusUsedResourceRequestsGauge.MustCurryWith(labels), thr.Status.Used.ResourceRequests) From 90a9f5bf65d028f1637398731d1f6810359698cc Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Mon, 11 Oct 2021 12:49:01 +0900 Subject: [PATCH 15/16] fix docs --- README.md | 181 ++++++++++++++++++++++---------------- example/my-scheduler.yaml | 74 ---------------- 2 files changed, 106 insertions(+), 149 deletions(-) delete mode 100644 example/my-scheduler.yaml diff --git a/README.md b/README.md index 5147ee5..621d98e 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,94 @@ -# kube-throttler : throttling your pods in kubernetes cluster. -[![Build Status](https://travis-ci.org/everpeace/kube-throttler.svg?branch=master)](https://travis-ci.org/everpeace/kube-throttler) -[![Docker Pulls](https://img.shields.io/docker/pulls/everpeace/kube-throttler.svg)](https://hub.docker.com/r/everpeace/kube-throttler/) +# kube-throttler : throttling your pods in kubernetes cluster `kube-throttler` enables you to throttle your pods. It means that `kube-throttler` can prohibit to schedule any pods when it detects total amount of computational resource(in terms of `resources.requests` field) or the count of `Running` pods may exceeds a threshold . `kube-throttler` provides you very flexible and fine-grained throttle control. You can specify a set of pods which you want to throttle by [label selector](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) and its threshold by `Throttle`/`ClusterThrottle` CRD (see [deploy/0-crd.yaml](deploy/0-crd.yaml) for complete definition). -Throttle control is fully dynamic. Once you update throttle setting, `kube-throttler` follow the setting and change its status in up-to-date. +Throttle control is fully dynamic. Once you update throttle setting, `kube-throttler` follow the setting and change its status in up-to-date. +## What differs from `Quota`? -### What differs from `Quota`? -`Quota` returns error when you tried to create pods if you requested resource which exceeds the quota. However `Throttle` won't return any errors when creating pods but keep your pods stay `Pending` state by just throttling running pods. +`Quota` returns error when you tried to create pods if you requested resource which exceeds the quota. However `Throttle` won't return any errors when creating pods but keep your pods stay `Pending` state by just throttling running pods. + +And `Quota` is based on `Namespace` which is the unit of multi tenancy in Kubernetes. `Throttle` provides a kind of virtual computational resource pools in more dynamic and more finer grained way. -And `Quota` is based on `Namespace` which is the unit of multi tenancy in Kubernetes. `Throttle` provides a kind of virtual computational resource pools in more dynamic and more finer grained way. - ## Installation -`kube-throttler` works as [kubernetes scheduler extender](https://github.com/kubernetes/community/blob/master/contributors/design-proposals/scheduling/scheduler_extender.md). +`kube-throttler` is implemented as a kubernetes scheduler plugin by [Scheduling Framework](https://kubernetes.io/docs/concepts/scheduling-eviction/scheduling-framework/). + +There are two ways to use `kube-throttler`: -So, installation will be two steps +- Using pre-build binary +- Integrate `kube-throttler` with your scheduler plugins -1. deploy `kube-throttler` in your cluster -2. configure your `kube-scheduler` to integrate `kube-throttler` +### Pre-build binary -### 1. deploy `kube-throttler` in your cluster +`kube-throttler` ships pre-build binary/container images which `kube-throttler` is integrated with kube-scheduler. + +#### 1. deploy `kube-throttler` in your cluster ```shell kubectl create -f deploy/ -``` +``` This creates: + - `kube-throttler` namespace, service accounts, RBAC entries - this will create a cluster role and cluster role binding. please see [deploy/2-rbac.yaml](deploy/2-rbac.yaml) for detail. -- `kube-throttller` deployment and its service so that kubernetes scheduler connect to it. - - its throttler name is `kube-throttler` - - its target scheduler is `my-scheduler` (this throttler only counts running pods which is responsible for `my-scheduler`) - - if you want to change this, please see [`application.conf` in `kube-throttler-application-ini` configmap](deploy/3-deployment.yaml) - - -### 2. configure your `kube-scheduler` - -`kube-scheduler` supports policy based configuration. You will need to set `kube-throttler` as an extender like below: - -```json -{ - "kind" : "Policy", - "apiVersion" : "v1", - ... - "extenders" : [ - { - "urlPrefix": "http://extender.kube-throttler/", - "filterVerb": "check_throttle", - "prioritizeVerb": "", - "preemptVerb": "preempt_if_not_throttled", - "bindVerb": "", - "weight": 1, - "enableHttps": false, - "nodeCacheCapable": false - } - ] +- custom `kube-throttler` integrated `kube-scheduler` [deployment](deployment.yaml) + - with sample [scheduler config](deploy/config.yaml) + - scheduler name is `my-scheduler` + - throttler name is `kube-throttler` + +### Integrate kube-throttler with your kube-scheduler plugins + +#### 1. Register `kube-throttler` in your scheduler + +You need to register `kube-throttler` to your scheduler by calling `app.WithPlugin()` like this: + +```go +... +import ( + "math/rand" + "time" + + kubethrottler "github.com/everpeace/kube-throttler/pkg/scheduler_plugin" + "k8s.io/component-base/logs" + "k8s.io/kubernetes/cmd/kube-scheduler/app" +) + +func main() { + rand.Seed(time.Now().UnixNano()) + + command := app.NewSchedulerCommand( + ... + app.WithPlugin(kubethrottler.PluginName, kubethrottler.NewPlugin), + ) + + logs.InitLogs() + defer logs.FlushLogs() + + if err := command.Execute(); err != nil { + os.Exit(1) + } } ``` -please see [example/my-scheudler.yaml](example/my-scheduler.yaml) for complete example. +See these documents and repos for details of Scheduling Framework: + +- [Scheduling Framework's Official Document](https://kubernetes.io/docs/concepts/scheduling-eviction/scheduling-framework/) +- [Scheduler Plugins - Repository for out-of-tree scheduler plugins based on the scheduler framework.](https://github.com/kubernetes-sigs/scheduler-plugins) + +### 2. add roles to your scheduler service account + +`kube-throttler` requires [`kube-throttler`] cluster roles defined in [deploy/rbac.yaml](deploy/rbac.yaml) + +### 3. enable kube-throttler in your scheduler config + +You need to enable `kube-throttler` in your scheduler config. See [deploy/config.yaml](deploy/config.yaml) ## `Throttle` CRD + a `Throttle` custom resource defines three things: - throttler name which is responsible for this `Throttle` custom resource. @@ -130,7 +154,7 @@ status: User sometimes increase/decrease threshold value. You can edit `spec.threshold` directly. However, what if the increase/decrease is expected in limited term?? Temporary threshold overrides can solve it. Temporary threshold overrides provides declarative threshold override. It means, override automatically activated when the term started and expired automatically when the term finished. It would greatly reduces operational tasks. - + `spec` can have `temporaryThresholdOverrides` like this: ```yaml @@ -177,6 +201,7 @@ resourceRequests: These calculated threshold value are recoreded in `staus.calculatedThrottle` field. __The field matters when deciding throttle is active or not.__ ## How `kube-throttler` works + I describe a simple scenario here. _Note that this scenario holds with `ClusterThrottle`. The only difference between them is `ClusterThrottles` can targets pods in multiple namespaces but `Throttle` can targets pods only in the same namespace with it._ - define a throttle `t1` which targets `throttle=t1` label and threshold `cpu=200m` and `memory=1Gi`. @@ -332,7 +357,7 @@ status: resourceRequests: cpu: "0.500" memory: "536870912" -``` +``` Now, `t1` remains `cpu:200m` capacity. Then, create `pod3` requesting `cpu:300m`. `pod3` stays `Pending` state because `t1` does not have enough capacity on `cpu` resources. @@ -350,57 +375,54 @@ Events: ``` ## Monitoring with Prometheus -`kube-throttler` exports prometheus metrics powered by [Kamon](https://kamon.io/). metrics are served on `http://kube-throttler.kube-throttler.svc:9095/metrics`. -`kube-throttler` exports metrics below: +`kube-throttler` exports prometheus metrics. Metrics are served on kube-scheduler's metrics endpoint. `kube-throttler` exports metrics below: | metrics name | definition | example | ---------------|------------|--------- -| throttle_status_throttled_resourceRequests | resourceRequests of the throttle is throttled or not on specific resource (`1=throttled`, `0=not throttled`). |`throttle_status_throttled_resourceRequests{name="t1", namespace="default",uuid="...",resource="cpu"} 1.0` -| throttle_status_throttled_resourceCounts | resourceCounts of the throttle is throttled or not on specific resource (`1=throttled`, `0=not throttled`). |`throttle_status_throttled_resourceRequests{name="t1", namespace="default",uuid="...",resource="pod"} 1.0` -| throttle_status_used_resourceRequests | used amount of resource requests of the throttle |`throttle_status_used_resourceRequests{name="t1", namespace="default",uuid="...",resource="cpu"} 200` +--------------|------------|--------- +| throttle_status_throttled_resourceRequests | resourceRequests of the throttle is throttled or not on specific resource (`1=throttled`, `0=not throttled`). |`throttle_status_throttled_resourceRequests{name="t1", namespace="default",uuid="...",resource="cpu"} 1.0` +| throttle_status_throttled_resourceCounts | resourceCounts of the throttle is throttled or not on specific resource (`1=throttled`, `0=not throttled`). |`throttle_status_throttled_resourceRequests{name="t1", namespace="default",uuid="...",resource="pod"} 1.0` +| throttle_status_used_resourceRequests | used amount of resource requests of the throttle |`throttle_status_used_resourceRequests{name="t1", namespace="default",uuid="...",resource="cpu"} 200` | throttle_status_used_resourceCounts | used resource counts of the throttle |`throttle_status_used_resourceCounts{name="t1", namespace="default",uuid="...",resource="pod"} 2` -| throttle_status_calculated_threshold_resourceRequests | calculated threshold on specific resourceRequests of the throttle |`throttle_status_calculated_threshold_resourceRequests{name="t1", namespace="default",uuid="...",resource="pod"} 2` -| throttle_status_calculated_threshold_resourceCounts | calculated threshold on specific resourceCounts of the throttle |`throttle_status_calculated_threshold_resourceCounts{name="t1", namespace="default",uuid="...",resource="cpu"} 200` -| throttle_spec_threshold_resourceRequests | threshold on specific resourceRequests of the throttle |`throttle_spec_threshold_resourceRequests{name="t1", namespace="default",uuid="...",resource="pod"} 2` -| throttle_spec_threshold_resourceCounts | threshold on specific resourceCounts of the throttle |`throttle_spec_threshold_resourceCounts{name="t1", namespace="default",uuid="...",resource="cpu"} 200` -| clusterthrottle_status_throttled_resourceRequests | resourceRequests of the clusterthrottle is throttled or not on specific resource (`1=throttled`, `0=not throttled`). |`clusterthrottle_status_throttled_resourceRequests{name="clt1",uuid="...",resource="cpu"} 1.0` -| clusterthrottle_status_throttled_resourceCounts | resourceCounts of the clusterthrottle is throttled or not on specific resource (`1=throttled`, `0=not throttled`). |`clusterthrottle_status_throttled_resourceRequests{name="clt1",uuid="...",resource="pod"} 1.0` -| clusterthrottle_status_used_resourceRequests | used amount of resource requests of the clusterthrottle |`clusterthrottle_status_used_resourceRequests{name="t1",uuid="...",resource="cpu"} 200` -| clusterthrottle_status_used_resourceCounts | used resource counts of the clusterthrottle |`clusterthrottle_status_used_resourceCounts{name="clt1",uuid="...",resource="pod"} 2` -| clusterthrottle_status_calculated_threshold_resourceRequests | calculated threshold on specific resourceRequests of the clusterthrottle |`clusterthrottle_status_calculated_threshold_resourceRequests{name="t1",uuid="...",resource="pod"} 2` -| clusterthrottle_status_calculated_threshold_resourceCounts | calculated threshold on specific resourceCounts of the clusterthrottle |`clusterthrottle_status_calculated_threshold_resourceCounts{name="t1",uuid="...",resource="cpu"} 200` -| clusterthrottle_spec_threshold_resourceRequests | threshold on specific resourceRequests of the clusterthrottle |`clusterthrottle_spec_threshold_resourceRequests{name="t1",uuid="...",resource="pod"} 2` -| clusterthrottle_spec_threshold_resourceCounts | threshold on specific resourceCounts of the clusterthrottle |`clusterthrottle_spec_threshold_resourceCounts{name="t1",uuid="...",resource="cpu"} 200` - -other metrics exported by [kamon-system-metrics](https://github.com/kamon-io/kamon-system-metrics), [kamon-akka](https://github.com/kamon-io/kamon-akka), [kamon-akka-http](https://github.com/kamon-io/kamon-akka-http) are available. - -### `ServiceMonitor` of Prometheus Operator -Used [prometheus-operator](https://github.com/coreos/prometheus-operator), this repository ships `ServiceMonitor` spec. So, setup is super easy. +| throttle_status_calculated_threshold_resourceRequests | calculated threshold on specific resourceRequests of the throttle |`throttle_status_calculated_threshold_resourceRequests{name="t1", namespace="default",uuid="...",resource="pod"} 2` +| throttle_status_calculated_threshold_resourceCounts | calculated threshold on specific resourceCounts of the throttle |`throttle_status_calculated_threshold_resourceCounts{name="t1", namespace="default",uuid="...",resource="cpu"} 200` +| throttle_spec_threshold_resourceRequests | threshold on specific resourceRequests of the throttle |`throttle_spec_threshold_resourceRequests{name="t1", namespace="default",uuid="...",resource="pod"} 2` +| throttle_spec_threshold_resourceCounts | threshold on specific resourceCounts of the throttle |`throttle_spec_threshold_resourceCounts{name="t1", namespace="default",uuid="...",resource="cpu"} 200` +| clusterthrottle_status_throttled_resourceRequests | resourceRequests of the clusterthrottle is throttled or not on specific resource (`1=throttled`, `0=not throttled`). |`clusterthrottle_status_throttled_resourceRequests{name="clt1",uuid="...",resource="cpu"} 1.0` +| clusterthrottle_status_throttled_resourceCounts | resourceCounts of the clusterthrottle is throttled or not on specific resource (`1=throttled`, `0=not throttled`). |`clusterthrottle_status_throttled_resourceRequests{name="clt1",uuid="...",resource="pod"} 1.0` +| clusterthrottle_status_used_resourceRequests | used amount of resource requests of the clusterthrottle |`clusterthrottle_status_used_resourceRequests{name="t1",uuid="...",resource="cpu"} 200` +| clusterthrottle_status_used_resourceCounts | used resource counts of the clusterthrottle |`clusterthrottle_status_used_resourceCounts{name="clt1",uuid="...",resource="pod"} 2` +| clusterthrottle_status_calculated_threshold_resourceRequests | calculated threshold on specific resourceRequests of the clusterthrottle |`clusterthrottle_status_calculated_threshold_resourceRequests{name="t1",uuid="...",resource="pod"} 2` +| clusterthrottle_status_calculated_threshold_resourceCounts | calculated threshold on specific resourceCounts of the clusterthrottle |`clusterthrottle_status_calculated_threshold_resourceCounts{name="t1",uuid="...",resource="cpu"} 200` +| clusterthrottle_spec_threshold_resourceRequests | threshold on specific resourceRequests of the clusterthrottle |`clusterthrottle_spec_threshold_resourceRequests{name="t1",uuid="...",resource="pod"} 2` +| clusterthrottle_spec_threshold_resourceCounts | threshold on specific resourceCounts of the clusterthrottle |`clusterthrottle_spec_threshold_resourceCounts{name="t1",uuid="...",resource="cpu"} 200` + +## License -```shell -kubectl create -f prometheus/servicemonitor.yaml -``` +Apache License 2.0 -# License +## Change Logs (`< 1.0.0`) -Apache License 2.0 +Since `1.0.0`, change logs have been published in Github releases. -# Change Logs ## `0.7.4` + - Fixed - fail fast the liveness probe when kubernetes api watch stopped(#23) ## `0.7.3` + - Fixed - Watching Kubernetes events stopped when some watch source faced error (#22) ## `0.7.2` + - Changed - upgraded [`skuber`](https://github.com/doriordan/skuber) version to `v2.2.0` - periodic throttle reconciliation which limits those which really need to ## `0.7.1` + - Fixed - reduced memory usage for large cluster. `kube-throttler` does not cache completed(`status.phase=Succeeded|Failed`) pods anymore. @@ -441,7 +463,7 @@ all changes are for performance issue. - "too old resource version" on init reconciliation for clusters with large number of throttles/clusterthrottles - Changed - log level for all the metrics changes is now debug. - + ## `0.5.0` - Added @@ -454,6 +476,7 @@ all changes are for performance issue. - __BREAKING CHANGE__: change `spec.selector` object schema to support OR-ed multiple label selectors and `namespaceSelector` in clusterthrottles. (#6) ### Migration Notes from `0.3.x` or before + - stop kube-throttlers (recommend to make `replicas` to 0) - dump your all throttle/clusterthrottles `kubectl get clusterthrottles,throttles --all-namespaces` - replace `selector.matchLabels` with `selector.selectorTerms[0].podSelecter.matchLabels` in your crs @@ -478,37 +501,45 @@ all changes are for performance issue. - apply updated throttles/clusterthrottoles crs. ## `0.3.2` + - Changed - large refactoring #4 (moving throttle logic to model package from controller package) - skip un-marshalling `matchFields` field in `NodeSelectorTerm`. - the attribute has been supported since kubernetes `v1.11`. ## `0.3.1` + - Changed - sanitize invalid characters in metrics labels - remove `metadata.annotations` from metrics labels ## `0.3.0` + - Added - `resourceCounts.pod` in `Throttle`/`ClusterThrottle` so that user can throttle count of `running` pod. - Changed - previous compute resource threshold should be defined in `resourceRequests.{cpu|memory}`. - + ## `0.2.0` + - introduce `ClusterThrottle` which can target pods in multiple namespaces. - make `Throttle`/`ClusterThrottle` not burstable. This means if some throttle remains `cpu:200m` and pod requesting `cpu:300` is trie to schedule, kube-throttler does not allow the pod to be scheduled. At that case, message of `throttles[insufficient]=` will be returned to scheduler. ## `0.1.3` + - `watch-buff-size` can be configurable for large pods - properly handle initial sync error ## `0.1.2` + - multi-throttler, multi-scheduler deployment support - `throttlerName` is introduced in `Throttle` CRD - `throttler-name` and `target-scheduler-names` are introduced in throttler configuration ## `0.1.1` + - fixed returning filter error when normal throttled situation. ## `0.1.0` + first public release. diff --git a/example/my-scheduler.yaml b/example/my-scheduler.yaml deleted file mode 100644 index 28c2f4c..0000000 --- a/example/my-scheduler.yaml +++ /dev/null @@ -1,74 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: my-scheduler-config - namespace: kube-system -data: - config.yaml: | - apiVersion: kubescheduler.config.k8s.io/v1alpha1 - kind: KubeSchedulerConfiguration - schedulerName: my-scheduler - algorithmSource: - policy: - configMap: - namespace: kube-system - name: my-scheduler-policy - leaderElection: - leaderElect: true - lockObjectName: my-scheduler - lockObjectNamespace: kube-system ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: my-scheduler-policy - namespace: kube-system -data: - policy.cfg : | - { - "kind" : "Policy", - "apiVersion" : "v1", - "extenders" : [{ - "urlPrefix": "http://kube-throttler.kube-throttler/", - "filterVerb": "check_throttle", - "prioritizeVerb": "", - "preemptVerb": "preempt_if_not_throttled", - "bindVerb": "", - "weight": 1, - "enableHttps": false, - "nodeCacheCapable": false - }] - } ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: my-scheduler - namespace: kube-system - labels: - app: my-scheduler -spec: - replicas: 1 - selector: - matchLabels: - app: my-scheduler - template: - metadata: - labels: - app: my-scheduler - spec: - volumes: - - name: my-scheduler-config - configMap: - name: my-scheduler-config - containers: - - name: my-scheduler-ctr - image: gcr.io/google_containers/hyperkube:v1.14.3 - imagePullPolicy: IfNotPresent - args: - - kube-scheduler - - --config=/my-scheduler/config.yaml - - -v=3 - volumeMounts: - - name: my-scheduler-config - mountPath: /my-scheduler From 6970b950bba4ede76591f14ed27ba77bcd2eda9a Mon Sep 17 00:00:00 2001 From: Shingo Omura Date: Mon, 11 Oct 2021 13:06:28 +0900 Subject: [PATCH 16/16] set released image in deploy/kustomization.yaml --- README.md | 4 ++-- deploy/kustomization.yaml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 621d98e..e7e2343 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,8 @@ func main() { rand.Seed(time.Now().UnixNano()) command := app.NewSchedulerCommand( - ... - app.WithPlugin(kubethrottler.PluginName, kubethrottler.NewPlugin), + ... + app.WithPlugin(kubethrottler.PluginName, kubethrottler.NewPlugin), ) logs.InitLogs() diff --git a/deploy/kustomization.yaml b/deploy/kustomization.yaml index 67c455a..4972c92 100644 --- a/deploy/kustomization.yaml +++ b/deploy/kustomization.yaml @@ -12,3 +12,7 @@ configMapGenerator: - config.yaml generatorOptions: disableNameSuffixHash: true +images: +- name: kube-throttler + newName: ghcr.io/everpeace/kube-throttler + newTag: latest