mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-01-26 13:29:11 +01:00
Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae0a9ed525 | ||
|
|
0a815e8786 | ||
|
|
0115748fe8 | ||
|
|
d0305dac3f | ||
|
|
72d6a8aa33 | ||
|
|
654fdbba94 | ||
|
|
d73471327b | ||
|
|
1294c8a2c2 | ||
|
|
ddd3dd6f19 | ||
|
|
fad9e8dc39 | ||
|
|
450a5c290b | ||
|
|
af2198428e | ||
|
|
bc7be54e2e | ||
|
|
ccb61fc800 | ||
|
|
61819e3fec | ||
|
|
a7ceb67109 | ||
|
|
57a28e9a8f | ||
|
|
c1d87dd93c | ||
|
|
34fb602101 | ||
|
|
a6af54ab30 | ||
|
|
e41ef8cca3 | ||
|
|
d26cd4b317 | ||
|
|
f7d0acb731 | ||
|
|
f1f8b2eaa7 | ||
|
|
0a7f14d75e | ||
|
|
de76f9b14c | ||
|
|
8b84bb26ff | ||
|
|
bb25192163 | ||
|
|
40bb490f4c | ||
|
|
08729f6ef9 | ||
|
|
3dd7de8132 | ||
|
|
471aeb5ea4 | ||
|
|
65e7093ee7 | ||
|
|
fc0cd4ba30 | ||
|
|
ba3eac6c57 | ||
|
|
11a95ce8fb | ||
|
|
29a9fc6b56 | ||
|
|
d3c2f25685 | ||
|
|
e858c9ee80 | ||
|
|
44752e5e83 | ||
|
|
7123f30783 | ||
|
|
a82cf7cea4 | ||
|
|
72318868b0 | ||
|
|
589fb95236 | ||
|
|
7df543d137 | ||
|
|
1d7f429ba1 | ||
|
|
bf29a6073f | ||
|
|
65635bdb2e | ||
|
|
fd961557d0 | ||
|
|
c29bdc1dbe | ||
|
|
ffecc54bf5 | ||
|
|
f6f6fbab10 | ||
|
|
12c217477c | ||
|
|
20a4798465 | ||
|
|
a201f222e5 | ||
|
|
6e705fde85 | ||
|
|
5763554be4 | ||
|
|
6f873d5e69 | ||
|
|
445ae92caa | ||
|
|
8f3c0cf4b8 | ||
|
|
2a280f9a20 | ||
|
|
0503f53904 | ||
|
|
e0a9dfcb76 | ||
|
|
5db49f2ce1 | ||
|
|
582bd67681 | ||
|
|
a63f815116 | ||
|
|
afc17a62ea | ||
|
|
6dbc8a1fcc | ||
|
|
d2bd16a12d | ||
|
|
955d0eb228 | ||
|
|
a490726245 | ||
|
|
16a504fb87 | ||
|
|
d54b73a6ba | ||
|
|
344dc0f3c2 | ||
|
|
9f38146bbf | ||
|
|
1473e1d024 | ||
|
|
7b4b9d9e7e | ||
|
|
7d079813e5 | ||
|
|
e02857e00a | ||
|
|
acfd4f8680 | ||
|
|
c29c9db41e | ||
|
|
5d3f987dde | ||
|
|
97732cf62d | ||
|
|
4afc4dfb16 | ||
|
|
023ccd99f5 | ||
|
|
dd831d0d03 | ||
|
|
04dd7a5902 | ||
|
|
d395332793 | ||
|
|
73d9803a46 | ||
|
|
40a19396d0 | ||
|
|
10593fa427 | ||
|
|
257312929e | ||
|
|
6aa10f2169 | ||
|
|
9e536da99e | ||
|
|
b272dbec29 | ||
|
|
70e45297bc | ||
|
|
8007f4af20 | ||
|
|
32c8898ec7 |
@@ -1,6 +1,7 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.8.3
|
||||
- 1.9.1
|
||||
script:
|
||||
- hack/verify-gofmt.sh
|
||||
- make build
|
||||
- make test
|
||||
- make test-unit
|
||||
|
||||
12
Dockerfile
12
Dockerfile
@@ -11,10 +11,16 @@
|
||||
# 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.
|
||||
FROM golang:1.9.2
|
||||
|
||||
FROM fedora
|
||||
WORKDIR /go/src/github.com/kubernetes-incubator/descheduler
|
||||
COPY . .
|
||||
RUN make
|
||||
|
||||
FROM scratch
|
||||
|
||||
MAINTAINER Avesh Agarwal <avagarwa@redhat.com>
|
||||
|
||||
COPY _output/bin/descheduler /bin/descheduler
|
||||
CMD ["/bin/descheduler --help"]
|
||||
COPY --from=0 /go/src/github.com/kubernetes-incubator/descheduler/_output/bin/descheduler /bin/descheduler
|
||||
|
||||
CMD ["/bin/descheduler", "--help"]
|
||||
|
||||
20
Dockerfile.dev
Normal file
20
Dockerfile.dev
Normal file
@@ -0,0 +1,20 @@
|
||||
# Copyright 2017 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
FROM scratch
|
||||
|
||||
MAINTAINER Avesh Agarwal <avagarwa@redhat.com>
|
||||
|
||||
COPY _output/bin/descheduler /bin/descheduler
|
||||
|
||||
CMD ["/bin/descheduler", "--help"]
|
||||
27
Makefile
27
Makefile
@@ -15,21 +15,40 @@
|
||||
.PHONY: test
|
||||
|
||||
# VERSION is currently based on the last commit
|
||||
VERSION:=$(shell git rev-parse --short HEAD)
|
||||
VERSION=`git describe --tags`
|
||||
COMMIT=`git rev-parse HEAD`
|
||||
BUILD=`date +%FT%T%z`
|
||||
LDFLAG_LOCATION=github.com/kubernetes-incubator/descheduler/cmd/descheduler/app
|
||||
|
||||
LDFLAGS=-ldflags "-X ${LDFLAG_LOCATION}.version=${VERSION} -X ${LDFLAG_LOCATION}.buildDate=${BUILD} -X ${LDFLAG_LOCATION}.gitCommit=${COMMIT}"
|
||||
|
||||
|
||||
# IMAGE is the image name of descheduler
|
||||
# Should this be changed?
|
||||
IMAGE:=descheduler:$(VERSION)
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
go build -o _output/bin/descheduler github.com/kubernetes-incubator/descheduler/cmd/descheduler
|
||||
CGO_ENABLED=0 go build ${LDFLAGS} -o _output/bin/descheduler github.com/kubernetes-incubator/descheduler/cmd/descheduler
|
||||
|
||||
image: build
|
||||
dev-image: build
|
||||
docker build -f Dockerfile.dev -t $(IMAGE) .
|
||||
|
||||
image:
|
||||
docker build -t $(IMAGE) .
|
||||
|
||||
clean:
|
||||
rm -rf _output
|
||||
|
||||
test:
|
||||
test-unit:
|
||||
./test/run-unit-tests.sh
|
||||
|
||||
test-e2e:
|
||||
./test/run-e2e-tests.sh
|
||||
|
||||
gen:
|
||||
./hack/update-codecgen.sh
|
||||
./hack/update-generated-conversions.sh
|
||||
./hack/update-generated-deep-copies.sh
|
||||
./hack/update-generated-defaulters.sh
|
||||
|
||||
8
OWNERS
Normal file
8
OWNERS
Normal file
@@ -0,0 +1,8 @@
|
||||
approvers:
|
||||
- aveshagarwal
|
||||
- ravisantoshgudimetla
|
||||
- jayunit100
|
||||
reviewers:
|
||||
- aveshagarwal
|
||||
- ravisantoshgudimetla
|
||||
- jayunit100
|
||||
167
README.md
167
README.md
@@ -1,3 +1,6 @@
|
||||
[](https://travis-ci.org/kubernetes-incubator/descheduler)
|
||||
[](https://goreportcard.com/report/github.com/kubernetes-incubator/descheduler)
|
||||
|
||||
# Descheduler for Kubernetes
|
||||
|
||||
## Introduction
|
||||
@@ -40,10 +43,126 @@ For more information about available options run:
|
||||
$ ./_output/bin/descheduler --help
|
||||
```
|
||||
|
||||
## Running Descheduler as a Job Inside of a Pod
|
||||
|
||||
Descheduler can be run as a job inside of a pod. It has the advantage of
|
||||
being able to be run multiple times without needing user intervention.
|
||||
Descheduler pod is run as a critical pod to avoid being evicted by itself,
|
||||
or by kubelet due to an eviction event. Since critical pods are created in
|
||||
`kube-system` namespace, descheduler job and its pod will also be created
|
||||
in `kube-system` namespace.
|
||||
|
||||
### Create a container image
|
||||
|
||||
First we create a simple Docker image utilizing the Dockerfile found in the root directory:
|
||||
|
||||
```
|
||||
$ make dev-image
|
||||
```
|
||||
|
||||
This creates an image based off the binary we've built before. To build both the
|
||||
binary and image in one step you can run the following command:
|
||||
|
||||
```
|
||||
$ make image
|
||||
```
|
||||
|
||||
This eliminates the need to have Go installed locally and builds the binary
|
||||
within it's own container.
|
||||
|
||||
### Create a cluster role
|
||||
|
||||
To give necessary permissions for the descheduler to work in a pod, create a cluster role:
|
||||
|
||||
```
|
||||
$ cat << EOF| kubectl create -f -
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: descheduler-cluster-role
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["get", "watch", "list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get", "watch", "list", "delete"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods/eviction"]
|
||||
verbs: ["create"]
|
||||
EOF
|
||||
```
|
||||
|
||||
### Create the service account which will be used to run the job:
|
||||
|
||||
```
|
||||
$ kubectl create sa descheduler-sa -n kube-system
|
||||
```
|
||||
|
||||
### Bind the cluster role to the service account:
|
||||
|
||||
```
|
||||
$ kubectl create clusterrolebinding descheduler-cluster-role-binding \
|
||||
--clusterrole=descheduler-cluster-role \
|
||||
--serviceaccount=kube-system:descheduler-sa
|
||||
```
|
||||
### Create a configmap to store descheduler policy
|
||||
|
||||
Descheduler policy is created as a ConfigMap in `kube-system` namespace
|
||||
so that it can be mounted as a volume inside pod.
|
||||
|
||||
```
|
||||
$ kubectl create configmap descheduler-policy-configmap \
|
||||
-n kube-system --from-file=<path-to-policy-dir/policy.yaml>
|
||||
```
|
||||
### Create the job specification (descheduler-job.yaml)
|
||||
|
||||
```
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: descheduler-job
|
||||
namespace: kube-system
|
||||
spec:
|
||||
parallelism: 1
|
||||
completions: 1
|
||||
template:
|
||||
metadata:
|
||||
name: descheduler-pod
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/critical-pod: ""
|
||||
spec:
|
||||
containers:
|
||||
- name: descheduler
|
||||
image: descheduler
|
||||
volumeMounts:
|
||||
- mountPath: /policy-dir
|
||||
name: policy-volume
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-ec"
|
||||
- |
|
||||
/bin/descheduler --policy-config-file /policy-dir/policy.yaml
|
||||
restartPolicy: "Never"
|
||||
serviceAccountName: descheduler-sa
|
||||
volumes:
|
||||
- name: policy-volume
|
||||
configMap:
|
||||
name: descheduler-policy-configmap
|
||||
```
|
||||
|
||||
Please note that the pod template is configured with critical pod annotation, and
|
||||
the policy `policy-file` is mounted as a volume from the config map.
|
||||
|
||||
### Run the descheduler as a job in a pod:
|
||||
```
|
||||
$ kubectl create -f descheduler-job.yaml
|
||||
```
|
||||
|
||||
## Policy and Strategies
|
||||
|
||||
|
||||
Descheduler's policy is configurable and includes strategies to be enabled or disabled.
|
||||
Two strategies, `RemoveDuplicates` and `LowNodeUtilization` are currently implemented.
|
||||
Four strategies, `RemoveDuplicates`, `LowNodeUtilization`, `RemovePodsViolatingInterPodAntiAffinity`, `RemovePodsViolatingNodeAffinity` are currently implemented.
|
||||
As part of the policy, the parameters associated with the strategies can be configured too.
|
||||
By default, all strategies are enabled.
|
||||
|
||||
@@ -55,7 +174,7 @@ those duplicate pods are evicted for better spreading of pods in a cluster. This
|
||||
if some nodes went down due to whatever reasons, and pods on them were moved to other nodes leading to
|
||||
more than one pod associated with RS or RC, for example, running on same node. Once the failed nodes
|
||||
are ready again, this strategy could be enabled to evict those duplicate pods. Currently, there are no
|
||||
parameters associated with this strategy. To disable this strategy, the policy would look like:
|
||||
parameters associated with this strategy. To disable this strategy, the policy should look like:
|
||||
|
||||
```
|
||||
apiVersion: "descheduler/v1alpha1"
|
||||
@@ -107,16 +226,43 @@ This parameter can be configured to activate the strategy only when number of un
|
||||
are above the configured value. This could be helpful in large clusters where a few nodes could go
|
||||
under utilized frequently or for a short period of time. By default, `numberOfNodes` is set to zero.
|
||||
|
||||
### RemovePodsViolatingInterPodAntiAffinity
|
||||
|
||||
This strategy makes sure that pods violating interpod anti-affinity are removed from nodes. For example, if there is podA on node and podB and podC(running on same node) have antiaffinity rules which prohibit them to run on the same node, then podA will be evicted from the node so that podB and podC could run. This issue could happen, when the anti-affinity rules for pods B,C are created when they are already running on node. Currently, there are no parameters associated with this strategy. To disable this strategy, the policy should look like:
|
||||
|
||||
```
|
||||
apiVersion: "descheduler/v1alpha1"
|
||||
kind: "DeschedulerPolicy"
|
||||
strategies:
|
||||
"RemovePodsViolatingInterPodAntiAffinity":
|
||||
enabled: false
|
||||
```
|
||||
|
||||
### RemovePodsViolatingNodeAffinity
|
||||
|
||||
This strategy makes sure that pods violating node affinity are removed from nodes. For example, there is podA that was scheduled on nodeA which satisfied the node affinity rule `requiredDuringSchedulingIgnoredDuringExecution` at the time of scheduling, but over time nodeA no longer satisfies the rule, then if another node nodeB is available that satisfies the node affinity rule, then podA will be evicted from nodeA. The policy file should like this -
|
||||
|
||||
```
|
||||
apiVersion: "descheduler/v1alpha1"
|
||||
kind: "DeschedulerPolicy"
|
||||
strategies:
|
||||
"RemovePodsViolatingNodeAffinity":
|
||||
enabled: true
|
||||
params:
|
||||
nodeAffinityType:
|
||||
- "requiredDuringSchedulingIgnoredDuringExecution"
|
||||
```
|
||||
|
||||
## Pod Evictions
|
||||
|
||||
When the descheduler decides to evict pods from a node, it employs following general mechanism:
|
||||
|
||||
* Critical pods (with annotations scheduler.alpha.kubernetes.io/critical-pod) are never evicted.
|
||||
* Critical pods (with annotations scheduler.alpha.kubernetes.io/critical-pod) are never evicted.
|
||||
* Pods (static or mirrored pods or stand alone pods) not part of an RC, RS, Deployment or Jobs are
|
||||
never evicted because these pods won't be recreated.
|
||||
* Pods associated with DaemonSets are never evicted.
|
||||
* Pods with local storage are never evicted.
|
||||
* Best efforts pods are evicted before Burstable and Guaranteed pods.
|
||||
* Best efforts pods are evicted before Burstable and Guaranteed pods.
|
||||
|
||||
### Pod disruption Budget (PDB)
|
||||
Pods subject to Pod Disruption Budget (PDB) are not evicted if descheduling violates its pod
|
||||
@@ -126,10 +272,8 @@ disruption budget (PDB). The pods are evicted by using eviction subresource to h
|
||||
|
||||
This roadmap is not in any particular order.
|
||||
|
||||
* Addition of test cases (unit and end-to-end)
|
||||
* Ability to run inside a pod as a job
|
||||
* Strategy to consider taints and tolerations
|
||||
* Consideration of pod affinity and anti-affinity
|
||||
* Consideration of pod affinity
|
||||
* Strategy to consider pod life time
|
||||
* Strategy to consider number of pending pods
|
||||
* Integration with cluster autoscaler
|
||||
@@ -137,6 +281,13 @@ This roadmap is not in any particular order.
|
||||
* Consideration of Kubernetes's scheduler's predicates
|
||||
|
||||
|
||||
## Compatibility matrix
|
||||
|
||||
Descheduler | supported Kubernetes version
|
||||
-------------|-----------------------------
|
||||
0.4 | 1.9+
|
||||
0.1-0.3 | 1.7-1.8
|
||||
|
||||
## Note
|
||||
|
||||
This project is under active development, and is not intended for production use.
|
||||
|
||||
14
SECURITY_CONTACTS
Normal file
14
SECURITY_CONTACTS
Normal file
@@ -0,0 +1,14 @@
|
||||
# Defined below are the security contacts for this repo.
|
||||
#
|
||||
# They are the contact point for the Product Security Team to reach out
|
||||
# to for triaging and handling of incoming issues.
|
||||
#
|
||||
# The below names agree to abide by the
|
||||
# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy)
|
||||
# and will be removed and replaced if they violate that agreement.
|
||||
#
|
||||
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
|
||||
# INSTRUCTIONS AT https://kubernetes.io/security/
|
||||
|
||||
aveshagarwal
|
||||
ravisantoshgudimetla
|
||||
@@ -18,7 +18,7 @@ limitations under the License.
|
||||
package options
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
|
||||
// install the componentconfig api so we get its defaulting and conversion functions
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/apis/componentconfig"
|
||||
@@ -50,7 +50,11 @@ func NewDeschedulerServer() *DeschedulerServer {
|
||||
// AddFlags adds flags for a specific SchedulerServer to the specified FlagSet
|
||||
func (rs *DeschedulerServer) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.DurationVar(&rs.DeschedulingInterval, "descheduling-interval", rs.DeschedulingInterval, "time interval between two consecutive descheduler executions")
|
||||
fs.StringVar(&rs.KubeconfigFile, "kubeconfig-file", rs.KubeconfigFile, "File with kube configuration.")
|
||||
fs.StringVar(&rs.KubeconfigFile, "kubeconfig", rs.KubeconfigFile, "File with kube configuration.")
|
||||
fs.StringVar(&rs.PolicyConfigFile, "policy-config-file", rs.PolicyConfigFile, "File with descheduler policy configuration.")
|
||||
fs.BoolVar(&rs.DryRun, "dry-run", rs.DryRun, "execute descheduler in dry run mode.")
|
||||
// node-selector query causes descheduler to run only on nodes that matches the node labels in the query
|
||||
fs.StringVar(&rs.NodeSelector, "node-selector", rs.NodeSelector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
|
||||
// max-no-pods-to-evict limits the maximum number of pods to be evicted per node by descheduler.
|
||||
fs.IntVar(&rs.MaxNoOfPodsToEvictPerNode, "max-pods-to-evict-per-node", rs.MaxNoOfPodsToEvictPerNode, "Limits the maximum number of pods to be evicted per node by descheduler")
|
||||
}
|
||||
|
||||
@@ -18,32 +18,41 @@ limitations under the License.
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"flag"
|
||||
"io"
|
||||
|
||||
"github.com/kubernetes-incubator/descheduler/cmd/descheduler/app/options"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/descheduler"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
aflag "k8s.io/apiserver/pkg/util/flag"
|
||||
"k8s.io/apiserver/pkg/util/logs"
|
||||
)
|
||||
|
||||
// NewDeschedulerCommand creates a *cobra.Command object with default parameters
|
||||
func NewDeschedulerCommand() *cobra.Command {
|
||||
func NewDeschedulerCommand(out io.Writer) *cobra.Command {
|
||||
s := options.NewDeschedulerServer()
|
||||
s.AddFlags(pflag.CommandLine)
|
||||
cmd := &cobra.Command{
|
||||
Use: "descheduler",
|
||||
Short: "descheduler",
|
||||
Long: `The descheduler evicts pods which may be bound to less desired nodes`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
logs.InitLogs()
|
||||
defer logs.FlushLogs()
|
||||
err := Run(s)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
glog.Errorf("%v", err)
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
cmd.SetOutput(out)
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.SetNormalizeFunc(aflag.WordSepNormalizeFunc)
|
||||
flags.AddGoFlagSet(flag.CommandLine)
|
||||
s.AddFlags(flags)
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
86
cmd/descheduler/app/version.go
Normal file
86
cmd/descheduler/app/version.go
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// gitCommit is a constant representing the source version that
|
||||
// generated this build. It should be set during build via -ldflags.
|
||||
gitCommit string
|
||||
// version is a constant representing the version tag that
|
||||
// generated this build. It should be set during build via -ldflags.
|
||||
version string
|
||||
// buildDate in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
//It should be set during build via -ldflags.
|
||||
buildDate string
|
||||
)
|
||||
|
||||
// Info holds the information related to descheduler app version.
|
||||
type Info struct {
|
||||
Major string `json:"major"`
|
||||
Minor string `json:"minor"`
|
||||
GitCommit string `json:"gitCommit"`
|
||||
GitVersion string `json:"gitVersion"`
|
||||
BuildDate string `json:"buildDate"`
|
||||
GoVersion string `json:"goVersion"`
|
||||
Compiler string `json:"compiler"`
|
||||
Platform string `json:"platform"`
|
||||
}
|
||||
|
||||
// Get returns the overall codebase version. It's for detecting
|
||||
// what code a binary was built from.
|
||||
func Get() Info {
|
||||
majorVersion, minorVersion := splitVersion(version)
|
||||
return Info{
|
||||
Major: majorVersion,
|
||||
Minor: minorVersion,
|
||||
GitCommit: gitCommit,
|
||||
GitVersion: version,
|
||||
BuildDate: buildDate,
|
||||
GoVersion: runtime.Version(),
|
||||
Compiler: runtime.Compiler,
|
||||
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
|
||||
}
|
||||
}
|
||||
|
||||
func NewVersionCommand() *cobra.Command {
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Version of descheduler",
|
||||
Long: `Prints the version of descheduler.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Descheduler version %+v\n", Get())
|
||||
},
|
||||
}
|
||||
return versionCmd
|
||||
}
|
||||
|
||||
// splitVersion splits the git version to generate major and minor versions needed.
|
||||
func splitVersion(version string) (string, string) {
|
||||
if version == "" {
|
||||
return "", ""
|
||||
}
|
||||
// A sample version would be of form v0.1.0-7-ge884046, so split at first '.' and
|
||||
// then return 0 and 1+(+ appended to follow semver convention) for major and minor versions.
|
||||
return strings.Trim(strings.Split(version, ".")[0], "v"), strings.Split(version, ".")[1] + "+"
|
||||
}
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
@@ -24,7 +25,10 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd := app.NewDeschedulerCommand()
|
||||
out := os.Stdout
|
||||
cmd := app.NewDeschedulerCommand(out)
|
||||
cmd.AddCommand(app.NewVersionCommand())
|
||||
flag.CommandLine.Parse([]string{})
|
||||
if err := cmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -1,58 +1,3 @@
|
||||
## Kubernetes Community Code of Conduct
|
||||
# Kubernetes Community Code of Conduct
|
||||
|
||||
### Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, and in the interest of fostering
|
||||
an open and welcoming community, we pledge to respect all people who contribute
|
||||
through reporting issues, posting feature requests, updating documentation,
|
||||
submitting pull requests or patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for
|
||||
everyone, regardless of level of experience, gender, gender identity and expression,
|
||||
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
|
||||
religion, or nationality.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery
|
||||
* Personal attacks
|
||||
* Trolling or insulting/derogatory comments
|
||||
* Public or private harassment
|
||||
* Publishing other's private information, such as physical or electronic addresses,
|
||||
without explicit permission
|
||||
* Other unethical or unprofessional conduct.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are not
|
||||
aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers
|
||||
commit themselves to fairly and consistently applying these principles to every aspect
|
||||
of managing this project. Project maintainers who do not follow or enforce the Code of
|
||||
Conduct may be permanently removed from the project team.
|
||||
|
||||
This code of conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a Kubernetes maintainer, Sarah Novotny <sarahnovotny@google.com>, and/or Dan Kohn <dan@linuxfoundation.org>.
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant
|
||||
(http://contributor-covenant.org), version 1.2.0, available at
|
||||
http://contributor-covenant.org/version/1/2/0/
|
||||
|
||||
### Kubernetes Events Code of Conduct
|
||||
|
||||
Kubernetes events are working conferences intended for professional networking and collaboration in the
|
||||
Kubernetes community. Attendees are expected to behave according to professional standards and in accordance
|
||||
with their employer's policies on appropriate workplace behavior.
|
||||
|
||||
While at Kubernetes events or related social networking opportunities, attendees should not engage in
|
||||
discriminatory or offensive speech or actions regarding gender, sexuality, race, or religion. Speakers should
|
||||
be especially aware of these concerns.
|
||||
|
||||
The Kubernetes team does not condone any statements by speakers contrary to these standards. The Kubernetes
|
||||
team reserves the right to deny entrance and/or eject from an event (without refund) any individual found to
|
||||
be engaging in discriminatory or offensive speech or actions.
|
||||
|
||||
Please bring any concerns to to the immediate attention of Kubernetes event staff
|
||||
|
||||
|
||||
[]()
|
||||
Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)
|
||||
|
||||
8
examples/node-affinity.yml
Normal file
8
examples/node-affinity.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: "descheduler/v1alpha1"
|
||||
kind: "DeschedulerPolicy"
|
||||
strategies:
|
||||
"RemovePodsViolatingNodeAffinity":
|
||||
enabled: true
|
||||
params:
|
||||
nodeAffinityType:
|
||||
- "requiredDuringSchedulingIgnoredDuringExecution"
|
||||
@@ -3,6 +3,8 @@ kind: "DeschedulerPolicy"
|
||||
strategies:
|
||||
"RemoveDuplicates":
|
||||
enabled: true
|
||||
"RemovePodsViolatingInterPodAntiAffinity":
|
||||
enabled: true
|
||||
"LowNodeUtilization":
|
||||
enabled: true
|
||||
params:
|
||||
|
||||
394
glide.lock
generated
394
glide.lock
generated
@@ -1,67 +1,83 @@
|
||||
hash: bf18ba8a038a73e8f11f808c8babdd34734279b050e0223f704eaabbd13830fd
|
||||
updated: 2017-08-05T10:03:09.300557448-04:00
|
||||
hash: ed51a8e643db6e9996ef0ffca671fb31ab5b7fe0d61ecdda828192871f9da366
|
||||
updated: 2018-05-22T18:05:00.26435-07:00
|
||||
imports:
|
||||
- name: cloud.google.com/go
|
||||
version: 3b1ae45394a234c385be014e9a488f2bb6eef821
|
||||
subpackages:
|
||||
- compute/metadata
|
||||
- internal
|
||||
- name: github.com/Azure/go-autorest
|
||||
version: e14a70c556c8e0db173358d1a903dca345a8e75e
|
||||
subpackages:
|
||||
- autorest
|
||||
- autorest/adal
|
||||
- autorest/azure
|
||||
- autorest/date
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||
version: 782f4967f2dc4564575ca782fe2d04090b5faca8
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/dgrijalva/jwt-go
|
||||
version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20
|
||||
- name: github.com/docker/distribution
|
||||
version: cd27f179f2c10c5d300e6d09025b538c475b0d51
|
||||
version: edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c
|
||||
subpackages:
|
||||
- digest
|
||||
- digestset
|
||||
- reference
|
||||
- name: github.com/emicklei/go-restful
|
||||
version: ff4f55a206334ef123e4f79bbf348980da81ca46
|
||||
subpackages:
|
||||
- log
|
||||
- name: github.com/emicklei/go-restful-swagger12
|
||||
version: dcef7f55730566d41eae5db10e7d6981829720f6
|
||||
- name: github.com/ghodss/yaml
|
||||
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
|
||||
- name: github.com/go-openapi/analysis
|
||||
version: b44dc874b601d9e4e2f6e19140e794ba24bead3b
|
||||
- name: github.com/go-openapi/jsonpointer
|
||||
version: 46af16f9f7b149af66e5d1bd010e3574dc06de98
|
||||
- name: github.com/go-openapi/jsonreference
|
||||
version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272
|
||||
- name: github.com/go-openapi/loads
|
||||
version: 18441dfa706d924a39a030ee2c3b1d8d81917b38
|
||||
- name: github.com/go-openapi/spec
|
||||
version: 6aced65f8501fe1217321abf0749d354824ba2ff
|
||||
version: 7abd5745472fff5eb3685386d5fb8bf38683154d
|
||||
- name: github.com/go-openapi/swag
|
||||
version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72
|
||||
version: f3f9494671f93fcff853e3c6e9e948b3eb71e590
|
||||
- name: github.com/gogo/protobuf
|
||||
version: c0656edd0d9eab7c66d1eb0c568f9039345796f7
|
||||
subpackages:
|
||||
- gogoproto
|
||||
- plugin/compare
|
||||
- plugin/defaultcheck
|
||||
- plugin/description
|
||||
- plugin/embedcheck
|
||||
- plugin/enumstringer
|
||||
- plugin/equal
|
||||
- plugin/face
|
||||
- plugin/gostring
|
||||
- plugin/marshalto
|
||||
- plugin/oneofcheck
|
||||
- plugin/populate
|
||||
- plugin/size
|
||||
- plugin/stringer
|
||||
- plugin/testgen
|
||||
- plugin/union
|
||||
- plugin/unmarshal
|
||||
- proto
|
||||
- protoc-gen-gogo/descriptor
|
||||
- protoc-gen-gogo/generator
|
||||
- protoc-gen-gogo/grpc
|
||||
- protoc-gen-gogo/plugin
|
||||
- sortkeys
|
||||
- vanity
|
||||
- vanity/command
|
||||
- name: github.com/golang/glog
|
||||
version: 44145f04b68cf362d9c4df2182967c2275eaefed
|
||||
version: 23def4e6c14b4da8ac2ed8007337bc5eb5007998
|
||||
- name: github.com/golang/protobuf
|
||||
version: 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9
|
||||
subpackages:
|
||||
- jsonpb
|
||||
- proto
|
||||
- ptypes
|
||||
- ptypes/any
|
||||
- ptypes/duration
|
||||
- ptypes/struct
|
||||
- ptypes/timestamp
|
||||
- name: github.com/google/btree
|
||||
version: 7d79101e329e5a3adf994758c578dab82b90c017
|
||||
- name: github.com/google/gofuzz
|
||||
version: 44d81051d367757e1c7c6a5a86423ece9afcf63c
|
||||
- name: github.com/googleapis/gnostic
|
||||
version: 0c5108395e2debce0d731cf0287ddf7242066aba
|
||||
subpackages:
|
||||
- OpenAPIv2
|
||||
- compiler
|
||||
- extensions
|
||||
- name: github.com/gophercloud/gophercloud
|
||||
version: 8183543f90d1aef267a5ecc209f2e0715b355acb
|
||||
subpackages:
|
||||
- openstack
|
||||
- openstack/identity/v2/tenants
|
||||
- openstack/identity/v2/tokens
|
||||
- openstack/identity/v3/tokens
|
||||
- openstack/utils
|
||||
- pagination
|
||||
- name: github.com/gregjones/httpcache
|
||||
version: 787624de3eb7bd915c329cba748687a3b22666a6
|
||||
subpackages:
|
||||
- diskcache
|
||||
- name: github.com/hashicorp/golang-lru
|
||||
version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
|
||||
subpackages:
|
||||
@@ -72,16 +88,22 @@ imports:
|
||||
version: 6633656539c1639d9d78127b7d47c622b5d7b6dc
|
||||
- name: github.com/inconshreveable/mousetrap
|
||||
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
- name: github.com/json-iterator/go
|
||||
version: 36b14963da70d11297d313183d7e6388c8510e1e
|
||||
- name: github.com/juju/ratelimit
|
||||
version: 5b9ff866471762aa2ab2dced63c9fb6f53921342
|
||||
- name: github.com/kubernetes/repo-infra
|
||||
version: f521b5d472e00e05da5394994942064510a6e8bf
|
||||
version: dbcbd7624d5e4eb29f33c48edf1b1651809827a3
|
||||
- name: github.com/mailru/easyjson
|
||||
version: d5b7844b561a7bc640052f1b935f7b800330d7e0
|
||||
version: 2f5df55504ebc322e4d52d34df6a1f5b503bf26d
|
||||
subpackages:
|
||||
- buffer
|
||||
- jlexer
|
||||
- jwriter
|
||||
- name: github.com/opencontainers/go-digest
|
||||
version: a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
||||
- name: github.com/peterbourgon/diskv
|
||||
version: 5f041e8faa004a95c88a202771f4cc3e991971e6
|
||||
- name: github.com/PuerkitoBio/purell
|
||||
version: 8a290539e2e8629dbc4e6bad948158f790ec31f4
|
||||
- name: github.com/PuerkitoBio/urlesc
|
||||
@@ -89,29 +111,22 @@ imports:
|
||||
- name: github.com/spf13/cobra
|
||||
version: f62e98d28ab7ad31d707ba837a966378465c7b57
|
||||
- name: github.com/spf13/pflag
|
||||
version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7
|
||||
version: 4c012f6dcd9546820e378d0bdda4d8fc772cdfea
|
||||
- name: github.com/ugorji/go
|
||||
version: ded73eae5db7e7a0ef6f55aace87a2873c5d2b74
|
||||
version: f57d8945648dbfe4c332cff9c50fb57548958e3f
|
||||
subpackages:
|
||||
- codec
|
||||
- codec/codecgen
|
||||
- name: golang.org/x/crypto
|
||||
version: d172538b2cfce0c13cee31e647d0367aa8cd2486
|
||||
version: 81e90905daefcd6fd217b62423c0908922eadb30
|
||||
subpackages:
|
||||
- bcrypt
|
||||
- blowfish
|
||||
- curve25519
|
||||
- ed25519
|
||||
- ed25519/internal/edwards25519
|
||||
- nacl/secretbox
|
||||
- pkcs12
|
||||
- pkcs12/internal/rc2
|
||||
- poly1305
|
||||
- salsa20/salsa
|
||||
- ssh
|
||||
- ssh/terminal
|
||||
- name: golang.org/x/net
|
||||
version: f2499483f923065a842d38eb4c7f1927e6fc6e6d
|
||||
version: 1c05540f6879653db88113bc4a2b70aec4bd491f
|
||||
subpackages:
|
||||
- context
|
||||
- context/ctxhttp
|
||||
@@ -122,24 +137,26 @@ imports:
|
||||
- idna
|
||||
- internal/timeseries
|
||||
- lex/httplex
|
||||
- proxy
|
||||
- trace
|
||||
- websocket
|
||||
- name: golang.org/x/oauth2
|
||||
version: a6bd8cefa1811bd24b86f8902872e4e8225f74c4
|
||||
subpackages:
|
||||
- google
|
||||
- internal
|
||||
- jws
|
||||
- jwt
|
||||
- name: golang.org/x/sys
|
||||
version: 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9
|
||||
version: 95c6576299259db960f6c5b9b69ea52422860fce
|
||||
subpackages:
|
||||
- unix
|
||||
- windows
|
||||
- name: golang.org/x/text
|
||||
version: 2910a502d2bf9e43193af9d68ca516529614eed3
|
||||
version: b19bf474d317b857955b12035d2c5acb57ce8b01
|
||||
subpackages:
|
||||
- cases
|
||||
- encoding
|
||||
- encoding/internal
|
||||
- encoding/internal/identifier
|
||||
- encoding/unicode
|
||||
- internal
|
||||
- internal/tag
|
||||
- internal/utf8internal
|
||||
- language
|
||||
- runes
|
||||
- secure/bidirule
|
||||
@@ -148,29 +165,80 @@ imports:
|
||||
- unicode/bidi
|
||||
- unicode/norm
|
||||
- width
|
||||
- name: golang.org/x/tools
|
||||
version: 8cab8a1319f0be9798e7fe78b15da75e5f94b2e9
|
||||
subpackages:
|
||||
- imports
|
||||
- name: google.golang.org/appengine
|
||||
version: b1f26356af11148e710935ed1ac8a7f5702c7612
|
||||
subpackages:
|
||||
- internal
|
||||
- internal/app_identity
|
||||
- internal/base
|
||||
- internal/datastore
|
||||
- internal/log
|
||||
- internal/modules
|
||||
- internal/remote_api
|
||||
- internal/urlfetch
|
||||
- urlfetch
|
||||
- name: gopkg.in/inf.v0
|
||||
version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: 53feefa2559fb8dfa8d81baad31be332c97d6c77
|
||||
- name: k8s.io/apimachinery
|
||||
version: abe34e4f5b4413c282a83011892cbeea5b32223b
|
||||
- name: k8s.io/api
|
||||
version: af4bc157c3a209798fc897f6d4aaaaeb6c2e0d6a
|
||||
subpackages:
|
||||
- admission/v1beta1
|
||||
- admissionregistration/v1alpha1
|
||||
- admissionregistration/v1beta1
|
||||
- apps/v1
|
||||
- apps/v1beta1
|
||||
- apps/v1beta2
|
||||
- authentication/v1
|
||||
- authentication/v1beta1
|
||||
- authorization/v1
|
||||
- authorization/v1beta1
|
||||
- autoscaling/v1
|
||||
- autoscaling/v2beta1
|
||||
- batch/v1
|
||||
- batch/v1beta1
|
||||
- batch/v2alpha1
|
||||
- certificates/v1beta1
|
||||
- core/v1
|
||||
- events/v1beta1
|
||||
- extensions/v1beta1
|
||||
- imagepolicy/v1alpha1
|
||||
- networking/v1
|
||||
- policy/v1beta1
|
||||
- rbac/v1
|
||||
- rbac/v1alpha1
|
||||
- rbac/v1beta1
|
||||
- scheduling/v1alpha1
|
||||
- settings/v1alpha1
|
||||
- storage/v1
|
||||
- storage/v1alpha1
|
||||
- storage/v1beta1
|
||||
- name: k8s.io/apiextensions-apiserver
|
||||
version: aaccab68c17a51ccdfd3c8559221cce9334d3394
|
||||
subpackages:
|
||||
- pkg/features
|
||||
- name: k8s.io/apimachinery
|
||||
version: 180eddb345a5be3a157cea1c624700ad5bd27b8f
|
||||
subpackages:
|
||||
- pkg/api/equality
|
||||
- pkg/api/errors
|
||||
- pkg/api/meta
|
||||
- pkg/api/resource
|
||||
- pkg/apimachinery
|
||||
- pkg/apimachinery/announced
|
||||
- pkg/apimachinery/registered
|
||||
- pkg/apis/meta/internalversion
|
||||
- pkg/apis/meta/v1
|
||||
- pkg/apis/meta/v1/unstructured
|
||||
- pkg/apis/meta/v1alpha1
|
||||
- pkg/conversion
|
||||
- pkg/conversion/queryparams
|
||||
- pkg/conversion/unstructured
|
||||
- pkg/fields
|
||||
- pkg/labels
|
||||
- pkg/openapi
|
||||
- pkg/runtime
|
||||
- pkg/runtime/schema
|
||||
- pkg/runtime/serializer
|
||||
@@ -189,7 +257,6 @@ imports:
|
||||
- pkg/util/intstr
|
||||
- pkg/util/json
|
||||
- pkg/util/net
|
||||
- pkg/util/rand
|
||||
- pkg/util/runtime
|
||||
- pkg/util/sets
|
||||
- pkg/util/validation
|
||||
@@ -200,51 +267,87 @@ imports:
|
||||
- pkg/watch
|
||||
- third_party/forked/golang/reflect
|
||||
- name: k8s.io/apiserver
|
||||
version: ab57ed5a72c3b67058f665d660e23bae18339fc2
|
||||
version: 91e14f394e4796abf5a994a349a222e7081d86b6
|
||||
subpackages:
|
||||
- pkg/features
|
||||
- pkg/util/feature
|
||||
- pkg/util/flag
|
||||
- pkg/util/logs
|
||||
- name: k8s.io/client-go
|
||||
version: e356aa2e77ab4a5914c216c12ba14cce25a25ab0
|
||||
version: 78700dec6369ba22221b72770783300f143df150
|
||||
subpackages:
|
||||
- discovery
|
||||
- discovery/fake
|
||||
- kubernetes
|
||||
- kubernetes/fake
|
||||
- kubernetes/scheme
|
||||
- pkg/api
|
||||
- pkg/api/v1
|
||||
- pkg/apis/admissionregistration
|
||||
- pkg/apis/admissionregistration/v1alpha1
|
||||
- pkg/apis/apps
|
||||
- pkg/apis/apps/v1beta1
|
||||
- pkg/apis/authentication
|
||||
- pkg/apis/authentication/v1
|
||||
- pkg/apis/authentication/v1beta1
|
||||
- pkg/apis/authorization
|
||||
- pkg/apis/authorization/v1
|
||||
- pkg/apis/authorization/v1beta1
|
||||
- pkg/apis/autoscaling
|
||||
- pkg/apis/autoscaling/v1
|
||||
- pkg/apis/autoscaling/v2alpha1
|
||||
- pkg/apis/batch
|
||||
- pkg/apis/batch/v1
|
||||
- pkg/apis/batch/v2alpha1
|
||||
- pkg/apis/certificates
|
||||
- pkg/apis/certificates/v1beta1
|
||||
- pkg/apis/extensions
|
||||
- pkg/apis/extensions/v1beta1
|
||||
- pkg/apis/networking
|
||||
- pkg/apis/networking/v1
|
||||
- pkg/apis/policy
|
||||
- pkg/apis/policy/v1beta1
|
||||
- pkg/apis/rbac
|
||||
- pkg/apis/rbac/v1alpha1
|
||||
- pkg/apis/rbac/v1beta1
|
||||
- pkg/apis/settings
|
||||
- pkg/apis/settings/v1alpha1
|
||||
- pkg/apis/storage
|
||||
- pkg/apis/storage/v1
|
||||
- pkg/apis/storage/v1beta1
|
||||
- pkg/util
|
||||
- pkg/util/parsers
|
||||
- kubernetes/typed/admissionregistration/v1alpha1
|
||||
- kubernetes/typed/admissionregistration/v1alpha1/fake
|
||||
- kubernetes/typed/admissionregistration/v1beta1
|
||||
- kubernetes/typed/admissionregistration/v1beta1/fake
|
||||
- kubernetes/typed/apps/v1
|
||||
- kubernetes/typed/apps/v1/fake
|
||||
- kubernetes/typed/apps/v1beta1
|
||||
- kubernetes/typed/apps/v1beta1/fake
|
||||
- kubernetes/typed/apps/v1beta2
|
||||
- kubernetes/typed/apps/v1beta2/fake
|
||||
- kubernetes/typed/authentication/v1
|
||||
- kubernetes/typed/authentication/v1/fake
|
||||
- kubernetes/typed/authentication/v1beta1
|
||||
- kubernetes/typed/authentication/v1beta1/fake
|
||||
- kubernetes/typed/authorization/v1
|
||||
- kubernetes/typed/authorization/v1/fake
|
||||
- kubernetes/typed/authorization/v1beta1
|
||||
- kubernetes/typed/authorization/v1beta1/fake
|
||||
- kubernetes/typed/autoscaling/v1
|
||||
- kubernetes/typed/autoscaling/v1/fake
|
||||
- kubernetes/typed/autoscaling/v2beta1
|
||||
- kubernetes/typed/autoscaling/v2beta1/fake
|
||||
- kubernetes/typed/batch/v1
|
||||
- kubernetes/typed/batch/v1/fake
|
||||
- kubernetes/typed/batch/v1beta1
|
||||
- kubernetes/typed/batch/v1beta1/fake
|
||||
- kubernetes/typed/batch/v2alpha1
|
||||
- kubernetes/typed/batch/v2alpha1/fake
|
||||
- kubernetes/typed/certificates/v1beta1
|
||||
- kubernetes/typed/certificates/v1beta1/fake
|
||||
- kubernetes/typed/core/v1
|
||||
- kubernetes/typed/core/v1/fake
|
||||
- kubernetes/typed/events/v1beta1
|
||||
- kubernetes/typed/events/v1beta1/fake
|
||||
- kubernetes/typed/extensions/v1beta1
|
||||
- kubernetes/typed/extensions/v1beta1/fake
|
||||
- kubernetes/typed/networking/v1
|
||||
- kubernetes/typed/networking/v1/fake
|
||||
- kubernetes/typed/policy/v1beta1
|
||||
- kubernetes/typed/policy/v1beta1/fake
|
||||
- kubernetes/typed/rbac/v1
|
||||
- kubernetes/typed/rbac/v1/fake
|
||||
- kubernetes/typed/rbac/v1alpha1
|
||||
- kubernetes/typed/rbac/v1alpha1/fake
|
||||
- kubernetes/typed/rbac/v1beta1
|
||||
- kubernetes/typed/rbac/v1beta1/fake
|
||||
- kubernetes/typed/scheduling/v1alpha1
|
||||
- kubernetes/typed/scheduling/v1alpha1/fake
|
||||
- kubernetes/typed/settings/v1alpha1
|
||||
- kubernetes/typed/settings/v1alpha1/fake
|
||||
- kubernetes/typed/storage/v1
|
||||
- kubernetes/typed/storage/v1/fake
|
||||
- kubernetes/typed/storage/v1alpha1
|
||||
- kubernetes/typed/storage/v1alpha1/fake
|
||||
- kubernetes/typed/storage/v1beta1
|
||||
- kubernetes/typed/storage/v1beta1/fake
|
||||
- listers/core/v1
|
||||
- pkg/version
|
||||
- plugin/pkg/client/auth
|
||||
- plugin/pkg/client/auth/azure
|
||||
- plugin/pkg/client/auth/gcp
|
||||
- plugin/pkg/client/auth/oidc
|
||||
- plugin/pkg/client/auth/openstack
|
||||
- rest
|
||||
- rest/watch
|
||||
- testing
|
||||
- third_party/forked/golang/template
|
||||
- tools/auth
|
||||
- tools/cache
|
||||
- tools/clientcmd
|
||||
@@ -252,27 +355,45 @@ imports:
|
||||
- tools/clientcmd/api/latest
|
||||
- tools/clientcmd/api/v1
|
||||
- tools/metrics
|
||||
- tools/pager
|
||||
- tools/reference
|
||||
- transport
|
||||
- util/buffer
|
||||
- util/cert
|
||||
- util/flowcontrol
|
||||
- util/homedir
|
||||
- util/integer
|
||||
- util/jsonpath
|
||||
- name: k8s.io/code-generator
|
||||
version: fef8bcdbaf36ac6a1a18c9ef7d85200b249fad30
|
||||
- name: k8s.io/gengo
|
||||
version: c79c13d131b0a8f42d05faa6491c12e94ccc6f30
|
||||
- name: k8s.io/kubernetes
|
||||
version: ebb8d6e0fadfc95f3d64ccecc36c8ed2ac9224ef
|
||||
version: 1ef560bbde5195c01629039ad3b337ce63e7b321
|
||||
- name: k8s.io/kube-openapi
|
||||
version: 39a7bf85c140f972372c2a0d1ee40adbf0c8bfe1
|
||||
subpackages:
|
||||
- pkg/api
|
||||
- pkg/api/install
|
||||
- pkg/api/v1
|
||||
- pkg/api/v1/helper/qos
|
||||
- pkg/api/v1/ref
|
||||
- pkg/builder
|
||||
- pkg/common
|
||||
- pkg/handler
|
||||
- pkg/util
|
||||
- pkg/util/proto
|
||||
- name: k8s.io/kubernetes
|
||||
version: 925c127ec6b946659ad0fd596fa959be43f0cc05
|
||||
subpackages:
|
||||
- pkg/api/legacyscheme
|
||||
- pkg/api/testapi
|
||||
- pkg/api/v1/resource
|
||||
- pkg/apis/admission
|
||||
- pkg/apis/admission/install
|
||||
- pkg/apis/admission/v1beta1
|
||||
- pkg/apis/admissionregistration
|
||||
- pkg/apis/admissionregistration/install
|
||||
- pkg/apis/admissionregistration/v1alpha1
|
||||
- pkg/apis/admissionregistration/v1beta1
|
||||
- pkg/apis/apps
|
||||
- pkg/apis/apps/install
|
||||
- pkg/apis/apps/v1
|
||||
- pkg/apis/apps/v1beta1
|
||||
- pkg/apis/apps/v1beta2
|
||||
- pkg/apis/authentication
|
||||
- pkg/apis/authentication/install
|
||||
- pkg/apis/authentication/v1
|
||||
@@ -284,57 +405,60 @@ imports:
|
||||
- pkg/apis/autoscaling
|
||||
- pkg/apis/autoscaling/install
|
||||
- pkg/apis/autoscaling/v1
|
||||
- pkg/apis/autoscaling/v2alpha1
|
||||
- pkg/apis/autoscaling/v2beta1
|
||||
- pkg/apis/batch
|
||||
- pkg/apis/batch/install
|
||||
- pkg/apis/batch/v1
|
||||
- pkg/apis/batch/v1beta1
|
||||
- pkg/apis/batch/v2alpha1
|
||||
- pkg/apis/certificates
|
||||
- pkg/apis/certificates/install
|
||||
- pkg/apis/certificates/v1beta1
|
||||
- pkg/apis/componentconfig
|
||||
- pkg/apis/componentconfig/install
|
||||
- pkg/apis/componentconfig/v1alpha1
|
||||
- pkg/apis/core
|
||||
- pkg/apis/core/helper
|
||||
- pkg/apis/core/install
|
||||
- pkg/apis/core/v1
|
||||
- pkg/apis/core/v1/helper
|
||||
- pkg/apis/core/v1/helper/qos
|
||||
- pkg/apis/events
|
||||
- pkg/apis/events/install
|
||||
- pkg/apis/events/v1beta1
|
||||
- pkg/apis/extensions
|
||||
- pkg/apis/extensions/install
|
||||
- pkg/apis/extensions/v1beta1
|
||||
- pkg/apis/imagepolicy
|
||||
- pkg/apis/imagepolicy/install
|
||||
- pkg/apis/imagepolicy/v1alpha1
|
||||
- pkg/apis/networking
|
||||
- pkg/apis/networking/install
|
||||
- pkg/apis/networking/v1
|
||||
- pkg/apis/policy
|
||||
- pkg/apis/policy/install
|
||||
- pkg/apis/policy/v1beta1
|
||||
- pkg/apis/rbac
|
||||
- pkg/apis/rbac/install
|
||||
- pkg/apis/rbac/v1
|
||||
- pkg/apis/rbac/v1alpha1
|
||||
- pkg/apis/rbac/v1beta1
|
||||
- pkg/apis/scheduling
|
||||
- pkg/apis/scheduling/install
|
||||
- pkg/apis/scheduling/v1alpha1
|
||||
- pkg/apis/settings
|
||||
- pkg/apis/settings/install
|
||||
- pkg/apis/settings/v1alpha1
|
||||
- pkg/apis/storage
|
||||
- pkg/apis/storage/install
|
||||
- pkg/apis/storage/v1
|
||||
- pkg/apis/storage/v1alpha1
|
||||
- pkg/apis/storage/v1beta1
|
||||
- pkg/client/clientset_generated/clientset
|
||||
- pkg/client/clientset_generated/clientset/scheme
|
||||
- pkg/client/clientset_generated/clientset/typed/admissionregistration/v1alpha1
|
||||
- pkg/client/clientset_generated/clientset/typed/apps/v1beta1
|
||||
- pkg/client/clientset_generated/clientset/typed/authentication/v1
|
||||
- pkg/client/clientset_generated/clientset/typed/authentication/v1beta1
|
||||
- pkg/client/clientset_generated/clientset/typed/authorization/v1
|
||||
- pkg/client/clientset_generated/clientset/typed/authorization/v1beta1
|
||||
- pkg/client/clientset_generated/clientset/typed/autoscaling/v1
|
||||
- pkg/client/clientset_generated/clientset/typed/autoscaling/v2alpha1
|
||||
- pkg/client/clientset_generated/clientset/typed/batch/v1
|
||||
- pkg/client/clientset_generated/clientset/typed/batch/v2alpha1
|
||||
- pkg/client/clientset_generated/clientset/typed/certificates/v1beta1
|
||||
- pkg/client/clientset_generated/clientset/typed/core/v1
|
||||
- pkg/client/clientset_generated/clientset/typed/extensions/v1beta1
|
||||
- pkg/client/clientset_generated/clientset/typed/networking/v1
|
||||
- pkg/client/clientset_generated/clientset/typed/policy/v1beta1
|
||||
- pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1
|
||||
- pkg/client/clientset_generated/clientset/typed/rbac/v1beta1
|
||||
- pkg/client/clientset_generated/clientset/typed/settings/v1alpha1
|
||||
- pkg/client/clientset_generated/clientset/typed/storage/v1
|
||||
- pkg/client/clientset_generated/clientset/typed/storage/v1beta1
|
||||
- pkg/client/listers/core/v1
|
||||
- pkg/features
|
||||
- pkg/kubelet/apis
|
||||
- pkg/kubelet/types
|
||||
- pkg/util
|
||||
- pkg/master/ports
|
||||
- pkg/util/parsers
|
||||
- pkg/util/pointer
|
||||
- plugin/pkg/scheduler/algorithm/priorities/util
|
||||
testImports: []
|
||||
|
||||
20
glide.yaml
20
glide.yaml
@@ -1,15 +1,25 @@
|
||||
package: github.com/kubernetes-incubator/descheduler
|
||||
import:
|
||||
- package: k8s.io/client-go
|
||||
version: e356aa2e77ab4a5914c216c12ba14cce25a25ab0 # kube 1.7.0
|
||||
version: 78700dec6369ba22221b72770783300f143df150
|
||||
- package: k8s.io/api
|
||||
version: af4bc157c3a209798fc897f6d4aaaaeb6c2e0d6a
|
||||
- package: k8s.io/apiserver
|
||||
version: ab57ed5a72c3b67058f665d660e23bae18339fc2
|
||||
version: 91e14f394e4796abf5a994a349a222e7081d86b6
|
||||
- package: k8s.io/apimachinery
|
||||
version: abe34e4f5b4413c282a83011892cbeea5b32223b # kube 1.7.0
|
||||
version: 180eddb345a5be3a157cea1c624700ad5bd27b8f
|
||||
- package: k8s.io/kubernetes
|
||||
version: ebb8d6e0fadfc95f3d64ccecc36c8ed2ac9224ef # kube 1.7.0
|
||||
version: v1.9.0
|
||||
- package: k8s.io/code-generator
|
||||
version: kubernetes-1.9.0
|
||||
- package: github.com/kubernetes/repo-infra
|
||||
- package: github.com/spf13/cobra
|
||||
version: f62e98d28ab7ad31d707ba837a966378465c7b57
|
||||
- package: k8s.io/gengo
|
||||
version: c79c13d131b0a8f42d05faa6491c12e94ccc6f30
|
||||
- package: github.com/ugorji/go
|
||||
version: v.1.1-beta
|
||||
- package: github.com/Azure/go-autorest
|
||||
version: e14a70c556c8e0db173358d1a903dca345a8e75e
|
||||
- package: golang.org/x/tools
|
||||
subpackages:
|
||||
- imports
|
||||
|
||||
95
hack/e2e-gce/gcloud_create_cluster.sh
Executable file
95
hack/e2e-gce/gcloud_create_cluster.sh
Executable file
@@ -0,0 +1,95 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
echo "Make sure that uuid package is installed"
|
||||
|
||||
master_uuid=$(uuid)
|
||||
node1_uuid=$(uuid)
|
||||
node2_uuid=$(uuid)
|
||||
kube_apiserver_port=6443
|
||||
kube_version=1.11.1
|
||||
|
||||
DESCHEDULER_ROOT=$(dirname "${BASH_SOURCE}")/../../
|
||||
E2E_GCE_HOME=$DESCHEDULER_ROOT/hack/e2e-gce
|
||||
|
||||
|
||||
create_cluster() {
|
||||
echo "#################### Creating instances ##########################"
|
||||
gcloud compute instances create descheduler-$master_uuid --image="ubuntu-1604-xenial-v20180306" --image-project="ubuntu-os-cloud" --zone=us-east1-b
|
||||
# Keeping the --zone here so as to make sure that e2e's can run locally.
|
||||
echo "gcloud compute instances delete descheduler-$master_uuid --zone=us-east1-b --quiet" > $E2E_GCE_HOME/delete_cluster.sh
|
||||
|
||||
gcloud compute instances create descheduler-$node1_uuid --image="ubuntu-1604-xenial-v20180306" --image-project="ubuntu-os-cloud" --zone=us-east1-b
|
||||
echo "gcloud compute instances delete descheduler-$node1_uuid --zone=us-east1-b --quiet" >> $E2E_GCE_HOME/delete_cluster.sh
|
||||
|
||||
gcloud compute instances create descheduler-$node2_uuid --image="ubuntu-1604-xenial-v20180306" --image-project="ubuntu-os-cloud" --zone=us-east1-b
|
||||
echo "gcloud compute instances delete descheduler-$node2_uuid --zone=us-east1-b --quiet" >> $E2E_GCE_HOME/delete_cluster.sh
|
||||
|
||||
# Delete the firewall port created for master.
|
||||
echo "gcloud compute firewall-rules delete kubeapiserver-$master_uuid --quiet" >> $E2E_GCE_HOME/delete_cluster.sh
|
||||
chmod 755 $E2E_GCE_HOME/delete_cluster.sh
|
||||
}
|
||||
|
||||
|
||||
generate_kubeadm_instance_files() {
|
||||
# TODO: Check if they have come up. awk $6 contains the state(RUNNING or not).
|
||||
master_public_ip=$(gcloud compute instances list | grep $master_uuid|awk '{print $5}')
|
||||
node1_public_ip=$(gcloud compute instances list | grep $node1_uuid|awk '{print $5}')
|
||||
node2_public_ip=$(gcloud compute instances list | grep $node2_uuid|awk '{print $5}')
|
||||
echo "kubeadm init --kubernetes-version=${kube_version} --apiserver-advertise-address=${master_public_ip}" --ignore-preflight-errors=all --pod-network-cidr=10.96.0.0/12 > $E2E_GCE_HOME/kubeadm_install.sh
|
||||
}
|
||||
|
||||
|
||||
transfer_install_files() {
|
||||
gcloud compute scp $E2E_GCE_HOME/kubeadm_preinstall.sh descheduler-$master_uuid:/tmp --zone=us-east1-b
|
||||
gcloud compute scp $E2E_GCE_HOME/kubeadm_install.sh descheduler-$master_uuid:/tmp --zone=us-east1-b
|
||||
gcloud compute scp $E2E_GCE_HOME/kubeadm_preinstall.sh descheduler-$node1_uuid:/tmp --zone=us-east1-b
|
||||
gcloud compute scp $E2E_GCE_HOME/kubeadm_preinstall.sh descheduler-$node2_uuid:/tmp --zone=us-east1-b
|
||||
}
|
||||
|
||||
|
||||
install_kube() {
|
||||
# Docker installation.
|
||||
gcloud compute ssh descheduler-$master_uuid --command "sudo apt-get update; sudo apt-get install -y docker.io" --zone=us-east1-b
|
||||
gcloud compute ssh descheduler-$node1_uuid --command "sudo apt-get update; sudo apt-get install -y docker.io" --zone=us-east1-b
|
||||
gcloud compute ssh descheduler-$node2_uuid --command "sudo apt-get update; sudo apt-get install -y docker.io" --zone=us-east1-b
|
||||
# kubeadm installation.
|
||||
# 1. Transfer files to master, nodes.
|
||||
transfer_install_files
|
||||
# 2. Install kubeadm.
|
||||
#TODO: Add rm /tmp/kubeadm_install.sh
|
||||
# Open port for kube API server
|
||||
gcloud compute firewall-rules create kubeapiserver-$master_uuid --allow tcp:6443 --source-tags=descheduler-$master_uuid --source-ranges=0.0.0.0/0 --description="Opening api server port"
|
||||
|
||||
gcloud compute ssh descheduler-$master_uuid --command "sudo chmod 755 /tmp/kubeadm_preinstall.sh; sudo /tmp/kubeadm_preinstall.sh" --zone=us-east1-b
|
||||
kubeadm_join_command=$(gcloud compute ssh descheduler-$master_uuid --command "sudo chmod 755 /tmp/kubeadm_install.sh; sudo /tmp/kubeadm_install.sh" --zone=us-east1-b|grep 'kubeadm join')
|
||||
|
||||
# Copy the kubeconfig file onto /tmp for e2e tests.
|
||||
gcloud compute ssh descheduler-$master_uuid --command "sudo cp /etc/kubernetes/admin.conf /tmp; sudo chmod 777 /tmp/admin.conf" --zone=us-east1-b
|
||||
gcloud compute scp descheduler-$master_uuid:/tmp/admin.conf /tmp/admin.conf --zone=us-east1-b
|
||||
|
||||
# Postinstall on master, need to add a network plugin for kube-dns to come to running state.
|
||||
gcloud compute ssh descheduler-$master_uuid --command "sudo kubectl apply -f https://raw.githubusercontent.com/cloudnativelabs/kube-router/master/daemonset/kubeadm-kuberouter.yaml --kubeconfig /etc/kubernetes/admin.conf" --zone=us-east1-b
|
||||
echo $kubeadm_join_command > $E2E_GCE_HOME/kubeadm_join.sh
|
||||
|
||||
# Copy kubeadm_join to every node.
|
||||
#TODO: Put these in a loop, so that extension becomes possible.
|
||||
gcloud compute ssh descheduler-$node1_uuid --command "sudo chmod 755 /tmp/kubeadm_preinstall.sh; sudo /tmp/kubeadm_preinstall.sh" --zone=us-east1-b
|
||||
gcloud compute scp $E2E_GCE_HOME/kubeadm_join.sh descheduler-$node1_uuid:/tmp --zone=us-east1-b
|
||||
gcloud compute ssh descheduler-$node1_uuid --command "sudo chmod 755 /tmp/kubeadm_join.sh; sudo /tmp/kubeadm_join.sh" --zone=us-east1-b
|
||||
|
||||
gcloud compute ssh descheduler-$node2_uuid --command "sudo chmod 755 /tmp/kubeadm_preinstall.sh; sudo /tmp/kubeadm_preinstall.sh" --zone=us-east1-b
|
||||
gcloud compute scp $E2E_GCE_HOME/kubeadm_join.sh descheduler-$node2_uuid:/tmp --zone=us-east1-b
|
||||
gcloud compute ssh descheduler-$node2_uuid --command "sudo chmod 755 /tmp/kubeadm_join.sh; sudo /tmp/kubeadm_join.sh" --zone=us-east1-b
|
||||
|
||||
}
|
||||
|
||||
|
||||
create_cluster
|
||||
|
||||
generate_kubeadm_instance_files
|
||||
|
||||
install_kube
|
||||
8
hack/e2e-gce/gcloud_sdk_configure.sh
Executable file
8
hack/e2e-gce/gcloud_sdk_configure.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
|
||||
gcloud auth activate-service-account --key-file "${GCE_SA_CREDS}"
|
||||
gcloud config set project $GCE_PROJECT_ID
|
||||
gcloud config set compute/zone $GCE_ZONE
|
||||
9
hack/e2e-gce/install_gcloud.sh
Executable file
9
hack/e2e-gce/install_gcloud.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
wget https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-176.0.0-linux-x86_64.tar.gz
|
||||
|
||||
tar -xvzf google-cloud-sdk-176.0.0-linux-x86_64.tar.gz
|
||||
|
||||
./google-cloud-sdk/install.sh -q
|
||||
11
hack/e2e-gce/kubeadm_preinstall.sh
Normal file
11
hack/e2e-gce/kubeadm_preinstall.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
apt-get update
|
||||
apt-get install -y docker.io
|
||||
|
||||
apt-get update && apt-get install -y apt-transport-https
|
||||
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
|
||||
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
|
||||
deb http://apt.kubernetes.io/ kubernetes-xenial main
|
||||
EOF
|
||||
apt-get update
|
||||
apt-get install -y kubelet kubeadm kubectl
|
||||
exit 0
|
||||
@@ -43,5 +43,5 @@ OS_ROOT="$( os::util::absolute_path "${init_source}" )"
|
||||
export OS_ROOT
|
||||
cd "${OS_ROOT}"
|
||||
|
||||
PRJ_PREFIX="github.com/kubernetes-incubator/descheduler"
|
||||
PRJ_PREFIX="github.com/${REPO_ORG:-kubernetes-incubator}/descheduler"
|
||||
OS_OUTPUT_BINPATH="${OS_ROOT}/_output/bin"
|
||||
|
||||
@@ -40,7 +40,7 @@ generated_files=($(
|
||||
|
||||
# We only work for deps within this prefix.
|
||||
#my_prefix="k8s.io/kubernetes"
|
||||
my_prefix="github.com/kubernetes-incubator/descheduler"
|
||||
my_prefix="github.com/${REPO_ORG:-kubernetes-incubator}/descheduler"
|
||||
|
||||
# Register function to be called on EXIT to remove codecgen
|
||||
# binary and also to touch the files that should be regenerated
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
source "$(dirname "${BASH_SOURCE}")/lib/init.sh"
|
||||
|
||||
go build -o "${OS_OUTPUT_BINPATH}/conversion-gen" "${PRJ_PREFIX}/vendor/k8s.io/kubernetes/cmd/libs/go2idl/conversion-gen"
|
||||
go build -o "${OS_OUTPUT_BINPATH}/conversion-gen" "${PRJ_PREFIX}/vendor/k8s.io/code-generator/cmd/conversion-gen"
|
||||
|
||||
${OS_OUTPUT_BINPATH}/conversion-gen \
|
||||
--go-header-file "hack/boilerplate/boilerplate.go.txt" \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
source "$(dirname "${BASH_SOURCE}")/lib/init.sh"
|
||||
|
||||
go build -o "${OS_OUTPUT_BINPATH}/deepcopy-gen" "${PRJ_PREFIX}/vendor/k8s.io/kubernetes/cmd/libs/go2idl/deepcopy-gen"
|
||||
go build -o "${OS_OUTPUT_BINPATH}/deepcopy-gen" "${PRJ_PREFIX}/vendor/k8s.io/code-generator/cmd/deepcopy-gen"
|
||||
|
||||
${OS_OUTPUT_BINPATH}/deepcopy-gen \
|
||||
--go-header-file "hack/boilerplate/boilerplate.go.txt" \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
source "$(dirname "${BASH_SOURCE}")/lib/init.sh"
|
||||
|
||||
go build -o "${OS_OUTPUT_BINPATH}/defaulter-gen" "${PRJ_PREFIX}/vendor/k8s.io/kubernetes/cmd/libs/go2idl/defaulter-gen"
|
||||
go build -o "${OS_OUTPUT_BINPATH}/defaulter-gen" "${PRJ_PREFIX}/vendor/k8s.io/code-generator/cmd/defaulter-gen"
|
||||
|
||||
${OS_OUTPUT_BINPATH}/defaulter-gen \
|
||||
--go-header-file "hack/boilerplate/boilerplate.go.txt" \
|
||||
|
||||
54
hack/verify-gofmt.sh
Executable file
54
hack/verify-gofmt.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2017 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
DESCHEDULER_ROOT=$(dirname "${BASH_SOURCE}")/..
|
||||
|
||||
GO_VERSION=($(go version))
|
||||
|
||||
if [[ -z $(echo "${GO_VERSION[2]}" | grep -E 'go1.2|go1.3|go1.4|go1.5|go1.6|go1.7|go1.8|go1.9|go1.10') ]]; then
|
||||
echo "Unknown go version '${GO_VERSION}', skipping gofmt."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "${DESCHEDULER_ROOT}"
|
||||
|
||||
find_files() {
|
||||
find . -not \( \
|
||||
\( \
|
||||
-wholename './output' \
|
||||
-o -wholename './_output' \
|
||||
-o -wholename './release' \
|
||||
-o -wholename './target' \
|
||||
-o -wholename './.git' \
|
||||
-o -wholename '*/third_party/*' \
|
||||
-o -wholename '*/Godeps/*' \
|
||||
-o -wholename '*/vendor/*' \
|
||||
\) -prune \
|
||||
\) -name '*.go'
|
||||
}
|
||||
|
||||
GOFMT="gofmt -s"
|
||||
bad_files=$(find_files | xargs $GOFMT -l)
|
||||
if [[ -n "${bad_files}" ]]; then
|
||||
echo "!!! '$GOFMT' needs to be run on the following files: "
|
||||
echo "${bad_files}"
|
||||
exit 1
|
||||
fi
|
||||
@@ -37,7 +37,6 @@ func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *r
|
||||
&announced.GroupMetaFactoryArgs{
|
||||
GroupName: deschedulerapi.GroupName,
|
||||
VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version},
|
||||
ImportPrefix: "github.com/kubernetes-incubator/descheduler/pkg/api",
|
||||
AddInternalObjectsToScheme: deschedulerapi.AddToScheme,
|
||||
},
|
||||
announced.VersionToSchemeFunc{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,10 +17,12 @@ limitations under the License.
|
||||
package api
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
type DeschedulerPolicy struct {
|
||||
metav1.TypeMeta
|
||||
|
||||
@@ -45,6 +47,7 @@ type DeschedulerStrategy struct {
|
||||
// Only one of its members may be specified
|
||||
type StrategyParameters struct {
|
||||
NodeResourceUtilizationThresholds NodeResourceUtilizationThresholds
|
||||
NodeAffinityType []string
|
||||
}
|
||||
|
||||
type Percentage float64
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,10 +17,12 @@ limitations under the License.
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
type DeschedulerPolicy struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
@@ -45,6 +47,7 @@ type DeschedulerStrategy struct {
|
||||
// Only one of its members may be specified
|
||||
type StrategyParameters struct {
|
||||
NodeResourceUtilizationThresholds NodeResourceUtilizationThresholds `json:"nodeResourceUtilizationThresholds,omitempty"`
|
||||
NodeAffinityType []string `json:"nodeAffinityType,omitempty"`
|
||||
}
|
||||
|
||||
type Percentage float64
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -21,14 +21,15 @@ limitations under the License.
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
unsafe "unsafe"
|
||||
|
||||
api "github.com/kubernetes-incubator/descheduler/pkg/api"
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(RegisterConversions)
|
||||
localSchemeBuilder.Register(RegisterConversions)
|
||||
}
|
||||
|
||||
// RegisterConversions adds conversion functions to the given scheme.
|
||||
@@ -122,6 +123,7 @@ func autoConvert_v1alpha1_StrategyParameters_To_api_StrategyParameters(in *Strat
|
||||
if err := Convert_v1alpha1_NodeResourceUtilizationThresholds_To_api_NodeResourceUtilizationThresholds(&in.NodeResourceUtilizationThresholds, &out.NodeResourceUtilizationThresholds, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.NodeAffinityType = *(*[]string)(unsafe.Pointer(&in.NodeAffinityType))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -134,6 +136,7 @@ func autoConvert_api_StrategyParameters_To_v1alpha1_StrategyParameters(in *api.S
|
||||
if err := Convert_api_NodeResourceUtilizationThresholds_To_v1alpha1_NodeResourceUtilizationThresholds(&in.NodeResourceUtilizationThresholds, &out.NodeResourceUtilizationThresholds, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.NodeAffinityType = *(*[]string)(unsafe.Pointer(&in.NodeAffinityType))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -21,93 +21,109 @@ limitations under the License.
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(RegisterDeepCopies)
|
||||
}
|
||||
|
||||
// RegisterDeepCopies adds deep-copy functions to the given scheme. Public
|
||||
// to allow building arbitrary schemes.
|
||||
func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
return scheme.AddGeneratedDeepCopyFuncs(
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_DeschedulerPolicy, InType: reflect.TypeOf(&DeschedulerPolicy{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_DeschedulerStrategy, InType: reflect.TypeOf(&DeschedulerStrategy{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_NodeResourceUtilizationThresholds, InType: reflect.TypeOf(&NodeResourceUtilizationThresholds{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_StrategyParameters, InType: reflect.TypeOf(&StrategyParameters{})},
|
||||
)
|
||||
}
|
||||
|
||||
// DeepCopy_v1alpha1_DeschedulerPolicy is an autogenerated deepcopy function.
|
||||
func DeepCopy_v1alpha1_DeschedulerPolicy(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeschedulerPolicy)
|
||||
out := out.(*DeschedulerPolicy)
|
||||
*out = *in
|
||||
if in.Strategies != nil {
|
||||
in, out := &in.Strategies, &out.Strategies
|
||||
*out = make(StrategyList)
|
||||
for key, val := range *in {
|
||||
newVal := new(DeschedulerStrategy)
|
||||
if err := DeepCopy_v1alpha1_DeschedulerStrategy(&val, newVal, c); err != nil {
|
||||
return err
|
||||
}
|
||||
(*out)[key] = *newVal
|
||||
}
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DeschedulerPolicy) DeepCopyInto(out *DeschedulerPolicy) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.Strategies != nil {
|
||||
in, out := &in.Strategies, &out.Strategies
|
||||
*out = make(StrategyList, len(*in))
|
||||
for key, val := range *in {
|
||||
newVal := new(DeschedulerStrategy)
|
||||
val.DeepCopyInto(newVal)
|
||||
(*out)[key] = *newVal
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeschedulerPolicy.
|
||||
func (in *DeschedulerPolicy) DeepCopy() *DeschedulerPolicy {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DeschedulerPolicy)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DeschedulerPolicy) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy_v1alpha1_DeschedulerStrategy is an autogenerated deepcopy function.
|
||||
func DeepCopy_v1alpha1_DeschedulerStrategy(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeschedulerStrategy)
|
||||
out := out.(*DeschedulerStrategy)
|
||||
*out = *in
|
||||
if err := DeepCopy_v1alpha1_StrategyParameters(&in.Params, &out.Params, c); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DeschedulerStrategy) DeepCopyInto(out *DeschedulerStrategy) {
|
||||
*out = *in
|
||||
in.Params.DeepCopyInto(&out.Params)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy_v1alpha1_NodeResourceUtilizationThresholds is an autogenerated deepcopy function.
|
||||
func DeepCopy_v1alpha1_NodeResourceUtilizationThresholds(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*NodeResourceUtilizationThresholds)
|
||||
out := out.(*NodeResourceUtilizationThresholds)
|
||||
*out = *in
|
||||
if in.Thresholds != nil {
|
||||
in, out := &in.Thresholds, &out.Thresholds
|
||||
*out = make(ResourceThresholds)
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.TargetThresholds != nil {
|
||||
in, out := &in.TargetThresholds, &out.TargetThresholds
|
||||
*out = make(ResourceThresholds)
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeschedulerStrategy.
|
||||
func (in *DeschedulerStrategy) DeepCopy() *DeschedulerStrategy {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DeschedulerStrategy)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopy_v1alpha1_StrategyParameters is an autogenerated deepcopy function.
|
||||
func DeepCopy_v1alpha1_StrategyParameters(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*StrategyParameters)
|
||||
out := out.(*StrategyParameters)
|
||||
*out = *in
|
||||
if err := DeepCopy_v1alpha1_NodeResourceUtilizationThresholds(&in.NodeResourceUtilizationThresholds, &out.NodeResourceUtilizationThresholds, c); err != nil {
|
||||
return err
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeResourceUtilizationThresholds) DeepCopyInto(out *NodeResourceUtilizationThresholds) {
|
||||
*out = *in
|
||||
if in.Thresholds != nil {
|
||||
in, out := &in.Thresholds, &out.Thresholds
|
||||
*out = make(ResourceThresholds, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.TargetThresholds != nil {
|
||||
in, out := &in.TargetThresholds, &out.TargetThresholds
|
||||
*out = make(ResourceThresholds, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeResourceUtilizationThresholds.
|
||||
func (in *NodeResourceUtilizationThresholds) DeepCopy() *NodeResourceUtilizationThresholds {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NodeResourceUtilizationThresholds)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StrategyParameters) DeepCopyInto(out *StrategyParameters) {
|
||||
*out = *in
|
||||
in.NodeResourceUtilizationThresholds.DeepCopyInto(&out.NodeResourceUtilizationThresholds)
|
||||
if in.NodeAffinityType != nil {
|
||||
in, out := &in.NodeAffinityType, &out.NodeAffinityType
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyParameters.
|
||||
func (in *StrategyParameters) DeepCopy() *StrategyParameters {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(StrategyParameters)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -21,93 +21,109 @@ limitations under the License.
|
||||
package api
|
||||
|
||||
import (
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(RegisterDeepCopies)
|
||||
}
|
||||
|
||||
// RegisterDeepCopies adds deep-copy functions to the given scheme. Public
|
||||
// to allow building arbitrary schemes.
|
||||
func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
return scheme.AddGeneratedDeepCopyFuncs(
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeschedulerPolicy, InType: reflect.TypeOf(&DeschedulerPolicy{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeschedulerStrategy, InType: reflect.TypeOf(&DeschedulerStrategy{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_NodeResourceUtilizationThresholds, InType: reflect.TypeOf(&NodeResourceUtilizationThresholds{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_StrategyParameters, InType: reflect.TypeOf(&StrategyParameters{})},
|
||||
)
|
||||
}
|
||||
|
||||
// DeepCopy_api_DeschedulerPolicy is an autogenerated deepcopy function.
|
||||
func DeepCopy_api_DeschedulerPolicy(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeschedulerPolicy)
|
||||
out := out.(*DeschedulerPolicy)
|
||||
*out = *in
|
||||
if in.Strategies != nil {
|
||||
in, out := &in.Strategies, &out.Strategies
|
||||
*out = make(StrategyList)
|
||||
for key, val := range *in {
|
||||
newVal := new(DeschedulerStrategy)
|
||||
if err := DeepCopy_api_DeschedulerStrategy(&val, newVal, c); err != nil {
|
||||
return err
|
||||
}
|
||||
(*out)[key] = *newVal
|
||||
}
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DeschedulerPolicy) DeepCopyInto(out *DeschedulerPolicy) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.Strategies != nil {
|
||||
in, out := &in.Strategies, &out.Strategies
|
||||
*out = make(StrategyList, len(*in))
|
||||
for key, val := range *in {
|
||||
newVal := new(DeschedulerStrategy)
|
||||
val.DeepCopyInto(newVal)
|
||||
(*out)[key] = *newVal
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeschedulerPolicy.
|
||||
func (in *DeschedulerPolicy) DeepCopy() *DeschedulerPolicy {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DeschedulerPolicy)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DeschedulerPolicy) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy_api_DeschedulerStrategy is an autogenerated deepcopy function.
|
||||
func DeepCopy_api_DeschedulerStrategy(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeschedulerStrategy)
|
||||
out := out.(*DeschedulerStrategy)
|
||||
*out = *in
|
||||
if err := DeepCopy_api_StrategyParameters(&in.Params, &out.Params, c); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DeschedulerStrategy) DeepCopyInto(out *DeschedulerStrategy) {
|
||||
*out = *in
|
||||
in.Params.DeepCopyInto(&out.Params)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy_api_NodeResourceUtilizationThresholds is an autogenerated deepcopy function.
|
||||
func DeepCopy_api_NodeResourceUtilizationThresholds(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*NodeResourceUtilizationThresholds)
|
||||
out := out.(*NodeResourceUtilizationThresholds)
|
||||
*out = *in
|
||||
if in.Thresholds != nil {
|
||||
in, out := &in.Thresholds, &out.Thresholds
|
||||
*out = make(ResourceThresholds)
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.TargetThresholds != nil {
|
||||
in, out := &in.TargetThresholds, &out.TargetThresholds
|
||||
*out = make(ResourceThresholds)
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeschedulerStrategy.
|
||||
func (in *DeschedulerStrategy) DeepCopy() *DeschedulerStrategy {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DeschedulerStrategy)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopy_api_StrategyParameters is an autogenerated deepcopy function.
|
||||
func DeepCopy_api_StrategyParameters(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*StrategyParameters)
|
||||
out := out.(*StrategyParameters)
|
||||
*out = *in
|
||||
if err := DeepCopy_api_NodeResourceUtilizationThresholds(&in.NodeResourceUtilizationThresholds, &out.NodeResourceUtilizationThresholds, c); err != nil {
|
||||
return err
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeResourceUtilizationThresholds) DeepCopyInto(out *NodeResourceUtilizationThresholds) {
|
||||
*out = *in
|
||||
if in.Thresholds != nil {
|
||||
in, out := &in.Thresholds, &out.Thresholds
|
||||
*out = make(ResourceThresholds, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.TargetThresholds != nil {
|
||||
in, out := &in.TargetThresholds, &out.TargetThresholds
|
||||
*out = make(ResourceThresholds, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeResourceUtilizationThresholds.
|
||||
func (in *NodeResourceUtilizationThresholds) DeepCopy() *NodeResourceUtilizationThresholds {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NodeResourceUtilizationThresholds)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StrategyParameters) DeepCopyInto(out *StrategyParameters) {
|
||||
*out = *in
|
||||
in.NodeResourceUtilizationThresholds.DeepCopyInto(&out.NodeResourceUtilizationThresholds)
|
||||
if in.NodeAffinityType != nil {
|
||||
in, out := &in.NodeAffinityType, &out.NodeAffinityType
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyParameters.
|
||||
func (in *StrategyParameters) DeepCopy() *StrategyParameters {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(StrategyParameters)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *r
|
||||
&announced.GroupMetaFactoryArgs{
|
||||
GroupName: componentconfig.GroupName,
|
||||
VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version},
|
||||
ImportPrefix: "github.com/kubernetes-incubator/descheduler/pkg/apis/componentconfig",
|
||||
AddInternalObjectsToScheme: componentconfig.AddToScheme,
|
||||
},
|
||||
announced.VersionToSchemeFunc{
|
||||
|
||||
@@ -14,10 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// ************************************************************
|
||||
// DO NOT EDIT.
|
||||
// THIS FILE IS AUTO-GENERATED BY codecgen.
|
||||
// ************************************************************
|
||||
// Code generated by codecgen - DO NOT EDIT.
|
||||
|
||||
package componentconfig
|
||||
|
||||
@@ -33,31 +30,29 @@ import (
|
||||
|
||||
const (
|
||||
// ----- content types ----
|
||||
codecSelferC_UTF81234 = 1
|
||||
codecSelferC_RAW1234 = 0
|
||||
codecSelferCcUTF81234 = 1
|
||||
codecSelferCcRAW1234 = 0
|
||||
// ----- value types used ----
|
||||
codecSelferValueTypeArray1234 = 10
|
||||
codecSelferValueTypeMap1234 = 9
|
||||
// ----- containerStateValues ----
|
||||
codecSelfer_containerMapKey1234 = 2
|
||||
codecSelfer_containerMapValue1234 = 3
|
||||
codecSelfer_containerMapEnd1234 = 4
|
||||
codecSelfer_containerArrayElem1234 = 6
|
||||
codecSelfer_containerArrayEnd1234 = 7
|
||||
codecSelferValueTypeArray1234 = 10
|
||||
codecSelferValueTypeMap1234 = 9
|
||||
codecSelferValueTypeString1234 = 6
|
||||
codecSelferValueTypeInt1234 = 2
|
||||
codecSelferValueTypeUint1234 = 3
|
||||
codecSelferValueTypeFloat1234 = 4
|
||||
)
|
||||
|
||||
var (
|
||||
codecSelferBitsize1234 = uint8(reflect.TypeOf(uint(0)).Bits())
|
||||
codecSelferOnlyMapOrArrayEncodeToStructErr1234 = errors.New(`only encoded map or array can be decoded into a struct`)
|
||||
errCodecSelferOnlyMapOrArrayEncodeToStruct1234 = errors.New(`only encoded map or array can be decoded into a struct`)
|
||||
)
|
||||
|
||||
type codecSelfer1234 struct{}
|
||||
|
||||
func init() {
|
||||
if codec1978.GenVersion != 5 {
|
||||
if codec1978.GenVersion != 8 {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v",
|
||||
5, codec1978.GenVersion, file)
|
||||
8, codec1978.GenVersion, file)
|
||||
panic(err)
|
||||
}
|
||||
if false { // reference the types, but skip this branch at build/run time
|
||||
@@ -77,139 +72,142 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
yym1 := z.EncBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x) {
|
||||
} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
|
||||
z.EncExtension(x, yyxt1)
|
||||
} else {
|
||||
yysep2 := !z.EncBinary()
|
||||
yy2arr2 := z.EncBasicHandle().StructToArray
|
||||
var yyq2 [6]bool
|
||||
_, _, _ = yysep2, yyq2, yy2arr2
|
||||
var yyq2 [8]bool
|
||||
_ = yyq2
|
||||
_, _ = yysep2, yy2arr2
|
||||
const yyr2 bool = false
|
||||
yyq2[0] = x.Kind != ""
|
||||
yyq2[1] = x.APIVersion != ""
|
||||
var yynn2 int
|
||||
if yyr2 || yy2arr2 {
|
||||
r.EncodeArrayStart(6)
|
||||
r.WriteArrayStart(8)
|
||||
} else {
|
||||
yynn2 = 4
|
||||
var yynn2 = 6
|
||||
for _, b := range yyq2 {
|
||||
if b {
|
||||
yynn2++
|
||||
}
|
||||
}
|
||||
r.EncodeMapStart(yynn2)
|
||||
r.WriteMapStart(yynn2)
|
||||
yynn2 = 0
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.WriteArrayElem()
|
||||
if yyq2[0] {
|
||||
yym4 := z.EncBinary()
|
||||
_ = yym4
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Kind))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.Kind))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
r.EncodeString(codecSelferCcUTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[0] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("kind"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `kind`)
|
||||
r.WriteMapElemValue()
|
||||
yym5 := z.EncBinary()
|
||||
_ = yym5
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Kind))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.Kind))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.WriteArrayElem()
|
||||
if yyq2[1] {
|
||||
yym7 := z.EncBinary()
|
||||
_ = yym7
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.APIVersion))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
r.EncodeString(codecSelferCcUTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[1] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("apiVersion"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `apiVersion`)
|
||||
r.WriteMapElemValue()
|
||||
yym8 := z.EncBinary()
|
||||
_ = yym8
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.APIVersion))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.WriteArrayElem()
|
||||
yym10 := z.EncBinary()
|
||||
_ = yym10
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x.DeschedulingInterval) {
|
||||
} else if yyxt10 := z.Extension(z.I2Rtid(x.DeschedulingInterval)); yyxt10 != nil {
|
||||
z.EncExtension(x.DeschedulingInterval, yyxt10)
|
||||
} else {
|
||||
r.EncodeInt(int64(x.DeschedulingInterval))
|
||||
}
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("DeschedulingInterval"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `DeschedulingInterval`)
|
||||
r.WriteMapElemValue()
|
||||
yym11 := z.EncBinary()
|
||||
_ = yym11
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x.DeschedulingInterval) {
|
||||
} else if yyxt11 := z.Extension(z.I2Rtid(x.DeschedulingInterval)); yyxt11 != nil {
|
||||
z.EncExtension(x.DeschedulingInterval, yyxt11)
|
||||
} else {
|
||||
r.EncodeInt(int64(x.DeschedulingInterval))
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.WriteArrayElem()
|
||||
yym13 := z.EncBinary()
|
||||
_ = yym13
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.KubeconfigFile))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.KubeconfigFile))
|
||||
}
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("KubeconfigFile"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `KubeconfigFile`)
|
||||
r.WriteMapElemValue()
|
||||
yym14 := z.EncBinary()
|
||||
_ = yym14
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.KubeconfigFile))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.KubeconfigFile))
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.WriteArrayElem()
|
||||
yym16 := z.EncBinary()
|
||||
_ = yym16
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.PolicyConfigFile))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.PolicyConfigFile))
|
||||
}
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("PolicyConfigFile"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `PolicyConfigFile`)
|
||||
r.WriteMapElemValue()
|
||||
yym17 := z.EncBinary()
|
||||
_ = yym17
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.PolicyConfigFile))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.PolicyConfigFile))
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.WriteArrayElem()
|
||||
yym19 := z.EncBinary()
|
||||
_ = yym19
|
||||
if false {
|
||||
@@ -217,9 +215,9 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
r.EncodeBool(bool(x.DryRun))
|
||||
}
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("DryRun"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `DryRun`)
|
||||
r.WriteMapElemValue()
|
||||
yym20 := z.EncBinary()
|
||||
_ = yym20
|
||||
if false {
|
||||
@@ -228,9 +226,47 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
r.WriteArrayElem()
|
||||
yym22 := z.EncBinary()
|
||||
_ = yym22
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.NodeSelector))
|
||||
}
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `NodeSelector`)
|
||||
r.WriteMapElemValue()
|
||||
yym23 := z.EncBinary()
|
||||
_ = yym23
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.NodeSelector))
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
r.WriteArrayElem()
|
||||
yym25 := z.EncBinary()
|
||||
_ = yym25
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeInt(int64(x.MaxNoOfPodsToEvictPerNode))
|
||||
}
|
||||
} else {
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `MaxNoOfPodsToEvictPerNode`)
|
||||
r.WriteMapElemValue()
|
||||
yym26 := z.EncBinary()
|
||||
_ = yym26
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeInt(int64(x.MaxNoOfPodsToEvictPerNode))
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
r.WriteArrayEnd()
|
||||
} else {
|
||||
r.WriteMapEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,25 +279,26 @@ func (x *DeschedulerConfiguration) CodecDecodeSelf(d *codec1978.Decoder) {
|
||||
yym1 := z.DecBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(x) {
|
||||
} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
|
||||
z.DecExtension(x, yyxt1)
|
||||
} else {
|
||||
yyct2 := r.ContainerType()
|
||||
if yyct2 == codecSelferValueTypeMap1234 {
|
||||
yyl2 := r.ReadMapStart()
|
||||
if yyl2 == 0 {
|
||||
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
r.ReadMapEnd()
|
||||
} else {
|
||||
x.codecDecodeSelfFromMap(yyl2, d)
|
||||
}
|
||||
} else if yyct2 == codecSelferValueTypeArray1234 {
|
||||
yyl2 := r.ReadArrayStart()
|
||||
if yyl2 == 0 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
r.ReadArrayEnd()
|
||||
} else {
|
||||
x.codecDecodeSelfFromArray(yyl2, d)
|
||||
}
|
||||
} else {
|
||||
panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234)
|
||||
panic(errCodecSelferOnlyMapOrArrayEncodeToStruct1234)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,8 +307,6 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yys3Slc = z.DecScratchBuffer() // default slice to decode into
|
||||
_ = yys3Slc
|
||||
var yyhl3 bool = l >= 0
|
||||
for yyj3 := 0; ; yyj3++ {
|
||||
if yyhl3 {
|
||||
@@ -283,10 +318,9 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
|
||||
break
|
||||
}
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerMapKey1234)
|
||||
yys3Slc = r.DecodeBytes(yys3Slc, true, true)
|
||||
yys3 := string(yys3Slc)
|
||||
z.DecSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.ReadMapElemKey()
|
||||
yys3 := z.StringView(r.DecStructFieldKey(codecSelferValueTypeString1234, z.DecScratchArrayBuffer()))
|
||||
r.ReadMapElemValue()
|
||||
switch yys3 {
|
||||
case "kind":
|
||||
if r.TryDecodeAsNil() {
|
||||
@@ -320,7 +354,8 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
|
||||
yym9 := z.DecBinary()
|
||||
_ = yym9
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(yyv8) {
|
||||
} else if yyxt9 := z.Extension(z.I2Rtid(yyv8)); yyxt9 != nil {
|
||||
z.DecExtension(yyv8, yyxt9)
|
||||
} else {
|
||||
*((*int64)(yyv8)) = int64(r.DecodeInt(64))
|
||||
}
|
||||
@@ -361,102 +396,81 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
|
||||
*((*bool)(yyv14)) = r.DecodeBool()
|
||||
}
|
||||
}
|
||||
case "NodeSelector":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.NodeSelector = ""
|
||||
} else {
|
||||
yyv16 := &x.NodeSelector
|
||||
yym17 := z.DecBinary()
|
||||
_ = yym17
|
||||
if false {
|
||||
} else {
|
||||
*((*string)(yyv16)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
case "MaxNoOfPodsToEvictPerNode":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.MaxNoOfPodsToEvictPerNode = 0
|
||||
} else {
|
||||
yyv18 := &x.MaxNoOfPodsToEvictPerNode
|
||||
yym19 := z.DecBinary()
|
||||
_ = yym19
|
||||
if false {
|
||||
} else {
|
||||
*((*int)(yyv18)) = int(r.DecodeInt(codecSelferBitsize1234))
|
||||
}
|
||||
}
|
||||
default:
|
||||
z.DecStructFieldNotFound(-1, yys3)
|
||||
} // end switch yys3
|
||||
} // end for yyj3
|
||||
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
r.ReadMapEnd()
|
||||
}
|
||||
|
||||
func (x *DeschedulerConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yyj16 int
|
||||
var yyb16 bool
|
||||
var yyhl16 bool = l >= 0
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
var yyj20 int
|
||||
var yyb20 bool
|
||||
var yyhl20 bool = l >= 0
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Kind = ""
|
||||
} else {
|
||||
yyv17 := &x.Kind
|
||||
yym18 := z.DecBinary()
|
||||
_ = yym18
|
||||
if false {
|
||||
} else {
|
||||
*((*string)(yyv17)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.APIVersion = ""
|
||||
} else {
|
||||
yyv19 := &x.APIVersion
|
||||
yym20 := z.DecBinary()
|
||||
_ = yym20
|
||||
if false {
|
||||
} else {
|
||||
*((*string)(yyv19)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.DeschedulingInterval = 0
|
||||
} else {
|
||||
yyv21 := &x.DeschedulingInterval
|
||||
yyv21 := &x.Kind
|
||||
yym22 := z.DecBinary()
|
||||
_ = yym22
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(yyv21) {
|
||||
} else {
|
||||
*((*int64)(yyv21)) = int64(r.DecodeInt(64))
|
||||
*((*string)(yyv21)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.KubeconfigFile = ""
|
||||
x.APIVersion = ""
|
||||
} else {
|
||||
yyv23 := &x.KubeconfigFile
|
||||
yyv23 := &x.APIVersion
|
||||
yym24 := z.DecBinary()
|
||||
_ = yym24
|
||||
if false {
|
||||
@@ -464,62 +478,152 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.
|
||||
*((*string)(yyv23)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.PolicyConfigFile = ""
|
||||
x.DeschedulingInterval = 0
|
||||
} else {
|
||||
yyv25 := &x.PolicyConfigFile
|
||||
yyv25 := &x.DeschedulingInterval
|
||||
yym26 := z.DecBinary()
|
||||
_ = yym26
|
||||
if false {
|
||||
} else if yyxt26 := z.Extension(z.I2Rtid(yyv25)); yyxt26 != nil {
|
||||
z.DecExtension(yyv25, yyxt26)
|
||||
} else {
|
||||
*((*string)(yyv25)) = r.DecodeString()
|
||||
*((*int64)(yyv25)) = int64(r.DecodeInt(64))
|
||||
}
|
||||
}
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.DryRun = false
|
||||
x.KubeconfigFile = ""
|
||||
} else {
|
||||
yyv27 := &x.DryRun
|
||||
yyv27 := &x.KubeconfigFile
|
||||
yym28 := z.DecBinary()
|
||||
_ = yym28
|
||||
if false {
|
||||
} else {
|
||||
*((*bool)(yyv27)) = r.DecodeBool()
|
||||
*((*string)(yyv27)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.PolicyConfigFile = ""
|
||||
} else {
|
||||
yyv29 := &x.PolicyConfigFile
|
||||
yym30 := z.DecBinary()
|
||||
_ = yym30
|
||||
if false {
|
||||
} else {
|
||||
*((*string)(yyv29)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.DryRun = false
|
||||
} else {
|
||||
yyv31 := &x.DryRun
|
||||
yym32 := z.DecBinary()
|
||||
_ = yym32
|
||||
if false {
|
||||
} else {
|
||||
*((*bool)(yyv31)) = r.DecodeBool()
|
||||
}
|
||||
}
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.NodeSelector = ""
|
||||
} else {
|
||||
yyv33 := &x.NodeSelector
|
||||
yym34 := z.DecBinary()
|
||||
_ = yym34
|
||||
if false {
|
||||
} else {
|
||||
*((*string)(yyv33)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.MaxNoOfPodsToEvictPerNode = 0
|
||||
} else {
|
||||
yyv35 := &x.MaxNoOfPodsToEvictPerNode
|
||||
yym36 := z.DecBinary()
|
||||
_ = yym36
|
||||
if false {
|
||||
} else {
|
||||
*((*int)(yyv35)) = int(r.DecodeInt(codecSelferBitsize1234))
|
||||
}
|
||||
}
|
||||
for {
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
if yyb20 {
|
||||
break
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
z.DecStructFieldNotFound(yyj16-1, "")
|
||||
r.ReadArrayElem()
|
||||
z.DecStructFieldNotFound(yyj20-1, "")
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
r.ReadArrayEnd()
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
type DeschedulerConfiguration struct {
|
||||
metav1.TypeMeta
|
||||
|
||||
@@ -37,4 +39,10 @@ type DeschedulerConfiguration struct {
|
||||
|
||||
// Dry run
|
||||
DryRun bool
|
||||
|
||||
// Node selectors
|
||||
NodeSelector string
|
||||
|
||||
// MaxNoOfPodsToEvictPerNode restricts maximum of pods to be evicted per node.
|
||||
MaxNoOfPodsToEvictPerNode int
|
||||
}
|
||||
|
||||
@@ -14,10 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// ************************************************************
|
||||
// DO NOT EDIT.
|
||||
// THIS FILE IS AUTO-GENERATED BY codecgen.
|
||||
// ************************************************************
|
||||
// Code generated by codecgen - DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -33,31 +30,29 @@ import (
|
||||
|
||||
const (
|
||||
// ----- content types ----
|
||||
codecSelferC_UTF81234 = 1
|
||||
codecSelferC_RAW1234 = 0
|
||||
codecSelferCcUTF81234 = 1
|
||||
codecSelferCcRAW1234 = 0
|
||||
// ----- value types used ----
|
||||
codecSelferValueTypeArray1234 = 10
|
||||
codecSelferValueTypeMap1234 = 9
|
||||
// ----- containerStateValues ----
|
||||
codecSelfer_containerMapKey1234 = 2
|
||||
codecSelfer_containerMapValue1234 = 3
|
||||
codecSelfer_containerMapEnd1234 = 4
|
||||
codecSelfer_containerArrayElem1234 = 6
|
||||
codecSelfer_containerArrayEnd1234 = 7
|
||||
codecSelferValueTypeArray1234 = 10
|
||||
codecSelferValueTypeMap1234 = 9
|
||||
codecSelferValueTypeString1234 = 6
|
||||
codecSelferValueTypeInt1234 = 2
|
||||
codecSelferValueTypeUint1234 = 3
|
||||
codecSelferValueTypeFloat1234 = 4
|
||||
)
|
||||
|
||||
var (
|
||||
codecSelferBitsize1234 = uint8(reflect.TypeOf(uint(0)).Bits())
|
||||
codecSelferOnlyMapOrArrayEncodeToStructErr1234 = errors.New(`only encoded map or array can be decoded into a struct`)
|
||||
errCodecSelferOnlyMapOrArrayEncodeToStruct1234 = errors.New(`only encoded map or array can be decoded into a struct`)
|
||||
)
|
||||
|
||||
type codecSelfer1234 struct{}
|
||||
|
||||
func init() {
|
||||
if codec1978.GenVersion != 5 {
|
||||
if codec1978.GenVersion != 8 {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v",
|
||||
5, codec1978.GenVersion, file)
|
||||
8, codec1978.GenVersion, file)
|
||||
panic(err)
|
||||
}
|
||||
if false { // reference the types, but skip this branch at build/run time
|
||||
@@ -77,88 +72,92 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
yym1 := z.EncBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x) {
|
||||
} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
|
||||
z.EncExtension(x, yyxt1)
|
||||
} else {
|
||||
yysep2 := !z.EncBinary()
|
||||
yy2arr2 := z.EncBasicHandle().StructToArray
|
||||
var yyq2 [6]bool
|
||||
_, _, _ = yysep2, yyq2, yy2arr2
|
||||
var yyq2 [8]bool
|
||||
_ = yyq2
|
||||
_, _ = yysep2, yy2arr2
|
||||
const yyr2 bool = false
|
||||
yyq2[0] = x.Kind != ""
|
||||
yyq2[1] = x.APIVersion != ""
|
||||
yyq2[2] = x.DeschedulingInterval != 0
|
||||
yyq2[4] = x.PolicyConfigFile != ""
|
||||
yyq2[5] = x.DryRun != false
|
||||
var yynn2 int
|
||||
yyq2[6] = x.NodeSelector != ""
|
||||
yyq2[7] = x.MaxNoOfPodsToEvictPerNode != 0
|
||||
if yyr2 || yy2arr2 {
|
||||
r.EncodeArrayStart(6)
|
||||
r.WriteArrayStart(8)
|
||||
} else {
|
||||
yynn2 = 1
|
||||
var yynn2 = 1
|
||||
for _, b := range yyq2 {
|
||||
if b {
|
||||
yynn2++
|
||||
}
|
||||
}
|
||||
r.EncodeMapStart(yynn2)
|
||||
r.WriteMapStart(yynn2)
|
||||
yynn2 = 0
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.WriteArrayElem()
|
||||
if yyq2[0] {
|
||||
yym4 := z.EncBinary()
|
||||
_ = yym4
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Kind))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.Kind))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
r.EncodeString(codecSelferCcUTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[0] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("kind"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `kind`)
|
||||
r.WriteMapElemValue()
|
||||
yym5 := z.EncBinary()
|
||||
_ = yym5
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.Kind))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.Kind))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.WriteArrayElem()
|
||||
if yyq2[1] {
|
||||
yym7 := z.EncBinary()
|
||||
_ = yym7
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.APIVersion))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
r.EncodeString(codecSelferCcUTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[1] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("apiVersion"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `apiVersion`)
|
||||
r.WriteMapElemValue()
|
||||
yym8 := z.EncBinary()
|
||||
_ = yym8
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.APIVersion))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.WriteArrayElem()
|
||||
if yyq2[2] {
|
||||
yym10 := z.EncBinary()
|
||||
_ = yym10
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x.DeschedulingInterval) {
|
||||
} else if yyxt10 := z.Extension(z.I2Rtid(x.DeschedulingInterval)); yyxt10 != nil {
|
||||
z.EncExtension(x.DeschedulingInterval, yyxt10)
|
||||
} else {
|
||||
r.EncodeInt(int64(x.DeschedulingInterval))
|
||||
}
|
||||
@@ -167,64 +166,65 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
}
|
||||
} else {
|
||||
if yyq2[2] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("deschedulingInterval"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `deschedulingInterval`)
|
||||
r.WriteMapElemValue()
|
||||
yym11 := z.EncBinary()
|
||||
_ = yym11
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.EncExt(x.DeschedulingInterval) {
|
||||
} else if yyxt11 := z.Extension(z.I2Rtid(x.DeschedulingInterval)); yyxt11 != nil {
|
||||
z.EncExtension(x.DeschedulingInterval, yyxt11)
|
||||
} else {
|
||||
r.EncodeInt(int64(x.DeschedulingInterval))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.WriteArrayElem()
|
||||
yym13 := z.EncBinary()
|
||||
_ = yym13
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.KubeconfigFile))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.KubeconfigFile))
|
||||
}
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("kubeconfigFile"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `kubeconfigFile`)
|
||||
r.WriteMapElemValue()
|
||||
yym14 := z.EncBinary()
|
||||
_ = yym14
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.KubeconfigFile))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.KubeconfigFile))
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.WriteArrayElem()
|
||||
if yyq2[4] {
|
||||
yym16 := z.EncBinary()
|
||||
_ = yym16
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.PolicyConfigFile))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.PolicyConfigFile))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
r.EncodeString(codecSelferCcUTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[4] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("policyConfigFile"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `policyConfigFile`)
|
||||
r.WriteMapElemValue()
|
||||
yym17 := z.EncBinary()
|
||||
_ = yym17
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.PolicyConfigFile))
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.PolicyConfigFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.WriteArrayElem()
|
||||
if yyq2[5] {
|
||||
yym19 := z.EncBinary()
|
||||
_ = yym19
|
||||
@@ -237,9 +237,9 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
}
|
||||
} else {
|
||||
if yyq2[5] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("dryRun"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `dryRun`)
|
||||
r.WriteMapElemValue()
|
||||
yym20 := z.EncBinary()
|
||||
_ = yym20
|
||||
if false {
|
||||
@@ -249,9 +249,59 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
r.WriteArrayElem()
|
||||
if yyq2[6] {
|
||||
yym22 := z.EncBinary()
|
||||
_ = yym22
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.NodeSelector))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferCcUTF81234, "")
|
||||
}
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
if yyq2[6] {
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `nodeSelector`)
|
||||
r.WriteMapElemValue()
|
||||
yym23 := z.EncBinary()
|
||||
_ = yym23
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferCcUTF81234, string(x.NodeSelector))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
r.WriteArrayElem()
|
||||
if yyq2[7] {
|
||||
yym25 := z.EncBinary()
|
||||
_ = yym25
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeInt(int64(x.MaxNoOfPodsToEvictPerNode))
|
||||
}
|
||||
} else {
|
||||
r.EncodeInt(0)
|
||||
}
|
||||
} else {
|
||||
if yyq2[7] {
|
||||
r.WriteMapElemKey()
|
||||
r.EncStructFieldKey(codecSelferValueTypeString1234, `maxNoOfPodsToEvictPerNode`)
|
||||
r.WriteMapElemValue()
|
||||
yym26 := z.EncBinary()
|
||||
_ = yym26
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeInt(int64(x.MaxNoOfPodsToEvictPerNode))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
r.WriteArrayEnd()
|
||||
} else {
|
||||
r.WriteMapEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,25 +314,26 @@ func (x *DeschedulerConfiguration) CodecDecodeSelf(d *codec1978.Decoder) {
|
||||
yym1 := z.DecBinary()
|
||||
_ = yym1
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(x) {
|
||||
} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
|
||||
z.DecExtension(x, yyxt1)
|
||||
} else {
|
||||
yyct2 := r.ContainerType()
|
||||
if yyct2 == codecSelferValueTypeMap1234 {
|
||||
yyl2 := r.ReadMapStart()
|
||||
if yyl2 == 0 {
|
||||
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
r.ReadMapEnd()
|
||||
} else {
|
||||
x.codecDecodeSelfFromMap(yyl2, d)
|
||||
}
|
||||
} else if yyct2 == codecSelferValueTypeArray1234 {
|
||||
yyl2 := r.ReadArrayStart()
|
||||
if yyl2 == 0 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
r.ReadArrayEnd()
|
||||
} else {
|
||||
x.codecDecodeSelfFromArray(yyl2, d)
|
||||
}
|
||||
} else {
|
||||
panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234)
|
||||
panic(errCodecSelferOnlyMapOrArrayEncodeToStruct1234)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,8 +342,6 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yys3Slc = z.DecScratchBuffer() // default slice to decode into
|
||||
_ = yys3Slc
|
||||
var yyhl3 bool = l >= 0
|
||||
for yyj3 := 0; ; yyj3++ {
|
||||
if yyhl3 {
|
||||
@@ -304,10 +353,9 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
|
||||
break
|
||||
}
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerMapKey1234)
|
||||
yys3Slc = r.DecodeBytes(yys3Slc, true, true)
|
||||
yys3 := string(yys3Slc)
|
||||
z.DecSendContainerState(codecSelfer_containerMapValue1234)
|
||||
r.ReadMapElemKey()
|
||||
yys3 := z.StringView(r.DecStructFieldKey(codecSelferValueTypeString1234, z.DecScratchArrayBuffer()))
|
||||
r.ReadMapElemValue()
|
||||
switch yys3 {
|
||||
case "kind":
|
||||
if r.TryDecodeAsNil() {
|
||||
@@ -341,7 +389,8 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
|
||||
yym9 := z.DecBinary()
|
||||
_ = yym9
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(yyv8) {
|
||||
} else if yyxt9 := z.Extension(z.I2Rtid(yyv8)); yyxt9 != nil {
|
||||
z.DecExtension(yyv8, yyxt9)
|
||||
} else {
|
||||
*((*int64)(yyv8)) = int64(r.DecodeInt(64))
|
||||
}
|
||||
@@ -382,102 +431,81 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
|
||||
*((*bool)(yyv14)) = r.DecodeBool()
|
||||
}
|
||||
}
|
||||
case "nodeSelector":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.NodeSelector = ""
|
||||
} else {
|
||||
yyv16 := &x.NodeSelector
|
||||
yym17 := z.DecBinary()
|
||||
_ = yym17
|
||||
if false {
|
||||
} else {
|
||||
*((*string)(yyv16)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
case "maxNoOfPodsToEvictPerNode":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.MaxNoOfPodsToEvictPerNode = 0
|
||||
} else {
|
||||
yyv18 := &x.MaxNoOfPodsToEvictPerNode
|
||||
yym19 := z.DecBinary()
|
||||
_ = yym19
|
||||
if false {
|
||||
} else {
|
||||
*((*int)(yyv18)) = int(r.DecodeInt(codecSelferBitsize1234))
|
||||
}
|
||||
}
|
||||
default:
|
||||
z.DecStructFieldNotFound(-1, yys3)
|
||||
} // end switch yys3
|
||||
} // end for yyj3
|
||||
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
|
||||
r.ReadMapEnd()
|
||||
}
|
||||
|
||||
func (x *DeschedulerConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yyj16 int
|
||||
var yyb16 bool
|
||||
var yyhl16 bool = l >= 0
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
var yyj20 int
|
||||
var yyb20 bool
|
||||
var yyhl20 bool = l >= 0
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Kind = ""
|
||||
} else {
|
||||
yyv17 := &x.Kind
|
||||
yym18 := z.DecBinary()
|
||||
_ = yym18
|
||||
if false {
|
||||
} else {
|
||||
*((*string)(yyv17)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.APIVersion = ""
|
||||
} else {
|
||||
yyv19 := &x.APIVersion
|
||||
yym20 := z.DecBinary()
|
||||
_ = yym20
|
||||
if false {
|
||||
} else {
|
||||
*((*string)(yyv19)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.DeschedulingInterval = 0
|
||||
} else {
|
||||
yyv21 := &x.DeschedulingInterval
|
||||
yyv21 := &x.Kind
|
||||
yym22 := z.DecBinary()
|
||||
_ = yym22
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(yyv21) {
|
||||
} else {
|
||||
*((*int64)(yyv21)) = int64(r.DecodeInt(64))
|
||||
*((*string)(yyv21)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.KubeconfigFile = ""
|
||||
x.APIVersion = ""
|
||||
} else {
|
||||
yyv23 := &x.KubeconfigFile
|
||||
yyv23 := &x.APIVersion
|
||||
yym24 := z.DecBinary()
|
||||
_ = yym24
|
||||
if false {
|
||||
@@ -485,62 +513,152 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.
|
||||
*((*string)(yyv23)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.PolicyConfigFile = ""
|
||||
x.DeschedulingInterval = 0
|
||||
} else {
|
||||
yyv25 := &x.PolicyConfigFile
|
||||
yyv25 := &x.DeschedulingInterval
|
||||
yym26 := z.DecBinary()
|
||||
_ = yym26
|
||||
if false {
|
||||
} else if yyxt26 := z.Extension(z.I2Rtid(yyv25)); yyxt26 != nil {
|
||||
z.DecExtension(yyv25, yyxt26)
|
||||
} else {
|
||||
*((*string)(yyv25)) = r.DecodeString()
|
||||
*((*int64)(yyv25)) = int64(r.DecodeInt(64))
|
||||
}
|
||||
}
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.DryRun = false
|
||||
x.KubeconfigFile = ""
|
||||
} else {
|
||||
yyv27 := &x.DryRun
|
||||
yyv27 := &x.KubeconfigFile
|
||||
yym28 := z.DecBinary()
|
||||
_ = yym28
|
||||
if false {
|
||||
} else {
|
||||
*((*bool)(yyv27)) = r.DecodeBool()
|
||||
*((*string)(yyv27)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.PolicyConfigFile = ""
|
||||
} else {
|
||||
yyv29 := &x.PolicyConfigFile
|
||||
yym30 := z.DecBinary()
|
||||
_ = yym30
|
||||
if false {
|
||||
} else {
|
||||
*((*string)(yyv29)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.DryRun = false
|
||||
} else {
|
||||
yyv31 := &x.DryRun
|
||||
yym32 := z.DecBinary()
|
||||
_ = yym32
|
||||
if false {
|
||||
} else {
|
||||
*((*bool)(yyv31)) = r.DecodeBool()
|
||||
}
|
||||
}
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.NodeSelector = ""
|
||||
} else {
|
||||
yyv33 := &x.NodeSelector
|
||||
yym34 := z.DecBinary()
|
||||
_ = yym34
|
||||
if false {
|
||||
} else {
|
||||
*((*string)(yyv33)) = r.DecodeString()
|
||||
}
|
||||
}
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb20 {
|
||||
r.ReadArrayEnd()
|
||||
return
|
||||
}
|
||||
r.ReadArrayElem()
|
||||
if r.TryDecodeAsNil() {
|
||||
x.MaxNoOfPodsToEvictPerNode = 0
|
||||
} else {
|
||||
yyv35 := &x.MaxNoOfPodsToEvictPerNode
|
||||
yym36 := z.DecBinary()
|
||||
_ = yym36
|
||||
if false {
|
||||
} else {
|
||||
*((*int)(yyv35)) = int(r.DecodeInt(codecSelferBitsize1234))
|
||||
}
|
||||
}
|
||||
for {
|
||||
yyj16++
|
||||
if yyhl16 {
|
||||
yyb16 = yyj16 > l
|
||||
yyj20++
|
||||
if yyhl20 {
|
||||
yyb20 = yyj20 > l
|
||||
} else {
|
||||
yyb16 = r.CheckBreak()
|
||||
yyb20 = r.CheckBreak()
|
||||
}
|
||||
if yyb16 {
|
||||
if yyb20 {
|
||||
break
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
z.DecStructFieldNotFound(yyj16-1, "")
|
||||
r.ReadArrayElem()
|
||||
z.DecStructFieldNotFound(yyj20-1, "")
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
r.ReadArrayEnd()
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
type DeschedulerConfiguration struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
@@ -37,4 +39,10 @@ type DeschedulerConfiguration struct {
|
||||
|
||||
// Dry run
|
||||
DryRun bool `json:"dryRun,omitempty"`
|
||||
|
||||
// Node selectors
|
||||
NodeSelector string `json:"nodeSelector,omitempty"`
|
||||
|
||||
// MaxNoOfPodsToEvictPerNode restricts maximum of pods to be evicted per node.
|
||||
MaxNoOfPodsToEvictPerNode int `json:"maxNoOfPodsToEvictPerNode,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -21,14 +21,15 @@ limitations under the License.
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
time "time"
|
||||
|
||||
componentconfig "github.com/kubernetes-incubator/descheduler/pkg/apis/componentconfig"
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
time "time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(RegisterConversions)
|
||||
localSchemeBuilder.Register(RegisterConversions)
|
||||
}
|
||||
|
||||
// RegisterConversions adds conversion functions to the given scheme.
|
||||
@@ -45,6 +46,8 @@ func autoConvert_v1alpha1_DeschedulerConfiguration_To_componentconfig_Deschedule
|
||||
out.KubeconfigFile = in.KubeconfigFile
|
||||
out.PolicyConfigFile = in.PolicyConfigFile
|
||||
out.DryRun = in.DryRun
|
||||
out.NodeSelector = in.NodeSelector
|
||||
out.MaxNoOfPodsToEvictPerNode = in.MaxNoOfPodsToEvictPerNode
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -58,6 +61,8 @@ func autoConvert_componentconfig_DeschedulerConfiguration_To_v1alpha1_Deschedule
|
||||
out.KubeconfigFile = in.KubeconfigFile
|
||||
out.PolicyConfigFile = in.PolicyConfigFile
|
||||
out.DryRun = in.DryRun
|
||||
out.NodeSelector = in.NodeSelector
|
||||
out.MaxNoOfPodsToEvictPerNode = in.MaxNoOfPodsToEvictPerNode
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -21,29 +21,31 @@ limitations under the License.
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(RegisterDeepCopies)
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DeschedulerConfiguration) DeepCopyInto(out *DeschedulerConfiguration) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
return
|
||||
}
|
||||
|
||||
// RegisterDeepCopies adds deep-copy functions to the given scheme. Public
|
||||
// to allow building arbitrary schemes.
|
||||
func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
return scheme.AddGeneratedDeepCopyFuncs(
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_DeschedulerConfiguration, InType: reflect.TypeOf(&DeschedulerConfiguration{})},
|
||||
)
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeschedulerConfiguration.
|
||||
func (in *DeschedulerConfiguration) DeepCopy() *DeschedulerConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DeschedulerConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopy_v1alpha1_DeschedulerConfiguration is an autogenerated deepcopy function.
|
||||
func DeepCopy_v1alpha1_DeschedulerConfiguration(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeschedulerConfiguration)
|
||||
out := out.(*DeschedulerConfiguration)
|
||||
*out = *in
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DeschedulerConfiguration) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -21,29 +21,31 @@ limitations under the License.
|
||||
package componentconfig
|
||||
|
||||
import (
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(RegisterDeepCopies)
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DeschedulerConfiguration) DeepCopyInto(out *DeschedulerConfiguration) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
return
|
||||
}
|
||||
|
||||
// RegisterDeepCopies adds deep-copy functions to the given scheme. Public
|
||||
// to allow building arbitrary schemes.
|
||||
func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
return scheme.AddGeneratedDeepCopyFuncs(
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_componentconfig_DeschedulerConfiguration, InType: reflect.TypeOf(&DeschedulerConfiguration{})},
|
||||
)
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeschedulerConfiguration.
|
||||
func (in *DeschedulerConfiguration) DeepCopy() *DeschedulerConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DeschedulerConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopy_componentconfig_DeschedulerConfiguration is an autogenerated deepcopy function.
|
||||
func DeepCopy_componentconfig_DeschedulerConfiguration(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeschedulerConfiguration)
|
||||
out := out.(*DeschedulerConfiguration)
|
||||
*out = *in
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DeschedulerConfiguration) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,10 @@ package client
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
||||
)
|
||||
|
||||
func CreateClient(kubeconfig string) (clientset.Interface, error) {
|
||||
|
||||
@@ -19,6 +19,8 @@ package descheduler
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/kubernetes-incubator/descheduler/cmd/descheduler/app/options"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/descheduler/client"
|
||||
eutils "github.com/kubernetes-incubator/descheduler/pkg/descheduler/evictions/utils"
|
||||
@@ -39,22 +41,30 @@ func Run(rs *options.DeschedulerServer) error {
|
||||
return err
|
||||
}
|
||||
if deschedulerPolicy == nil {
|
||||
return fmt.Errorf("\ndeschedulerPolicy is nil\n")
|
||||
|
||||
return fmt.Errorf("deschedulerPolicy is nil")
|
||||
}
|
||||
|
||||
evictionPolicyGroupVersion, err := eutils.SupportEviction(rs.Client)
|
||||
if err != nil || len(evictionPolicyGroupVersion) == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
stopChannel := make(chan struct{})
|
||||
nodes, err := nodeutil.ReadyNodes(rs.Client, stopChannel)
|
||||
nodes, err := nodeutil.ReadyNodes(rs.Client, rs.NodeSelector, stopChannel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
strategies.RemoveDuplicatePods(rs, deschedulerPolicy.Strategies["RemoveDuplicates"], evictionPolicyGroupVersion, nodes)
|
||||
strategies.LowNodeUtilization(rs, deschedulerPolicy.Strategies["LowNodeUtilization"], evictionPolicyGroupVersion, nodes)
|
||||
if len(nodes) <= 1 {
|
||||
glog.V(1).Infof("The cluster size is 0 or 1 meaning eviction causes service disruption or degradation. So aborting..")
|
||||
return nil
|
||||
}
|
||||
|
||||
nodePodCount := strategies.InitializeNodePodCount(nodes)
|
||||
strategies.RemoveDuplicatePods(rs, deschedulerPolicy.Strategies["RemoveDuplicates"], evictionPolicyGroupVersion, nodes, nodePodCount)
|
||||
strategies.LowNodeUtilization(rs, deschedulerPolicy.Strategies["LowNodeUtilization"], evictionPolicyGroupVersion, nodes, nodePodCount)
|
||||
strategies.RemovePodsViolatingInterPodAntiAffinity(rs, deschedulerPolicy.Strategies["RemovePodsViolatingInterPodAntiAffinity"], evictionPolicyGroupVersion, nodes, nodePodCount)
|
||||
strategies.RemovePodsViolatingNodeAffinity(rs, deschedulerPolicy.Strategies["RemovePodsViolatingNodeAffinity"], evictionPolicyGroupVersion, nodes, nodePodCount)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@ package evictions
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
policy "k8s.io/api/policy/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
policy "k8s.io/kubernetes/pkg/apis/policy/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
|
||||
eutils "github.com/kubernetes-incubator/descheduler/pkg/descheduler/evictions/utils"
|
||||
)
|
||||
|
||||
@@ -18,10 +18,10 @@ package evictions
|
||||
|
||||
import (
|
||||
"github.com/kubernetes-incubator/descheduler/test"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
package utils
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -17,35 +17,49 @@ limitations under the License.
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/utils"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
corelisters "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
||||
corelisters "k8s.io/kubernetes/pkg/client/listers/core/v1"
|
||||
)
|
||||
|
||||
// ReadyNodes returns ready nodes irrespective of whether they are
|
||||
// schedulable or not.
|
||||
func ReadyNodes(client clientset.Interface, stopChannel <-chan struct{}) ([]*v1.Node, error) {
|
||||
nl := GetNodeLister(client, stopChannel)
|
||||
nodes, err := nl.List(labels.Everything())
|
||||
func ReadyNodes(client clientset.Interface, nodeSelector string, stopChannel <-chan struct{}) ([]*v1.Node, error) {
|
||||
ns, err := labels.Parse(nodeSelector)
|
||||
if err != nil {
|
||||
return []*v1.Node{}, err
|
||||
}
|
||||
|
||||
var nodes []*v1.Node
|
||||
nl := GetNodeLister(client, stopChannel)
|
||||
if nl != nil {
|
||||
// err is defined above
|
||||
if nodes, err = nl.List(ns); err != nil {
|
||||
return []*v1.Node{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(nodes) == 0 {
|
||||
var err error
|
||||
nItems, err := client.Core().Nodes().List(metav1.ListOptions{})
|
||||
glog.V(2).Infof("node lister returned empty list, now fetch directly")
|
||||
|
||||
nItems, err := client.Core().Nodes().List(metav1.ListOptions{LabelSelector: nodeSelector})
|
||||
if err != nil {
|
||||
return []*v1.Node{}, err
|
||||
}
|
||||
|
||||
for i, _ := range nItems.Items {
|
||||
if nItems == nil || len(nItems.Items) == 0 {
|
||||
return []*v1.Node{}, nil
|
||||
}
|
||||
|
||||
for i := range nItems.Items {
|
||||
node := nItems.Items[i]
|
||||
nodes = append(nodes, &node)
|
||||
}
|
||||
@@ -61,15 +75,22 @@ func ReadyNodes(client clientset.Interface, stopChannel <-chan struct{}) ([]*v1.
|
||||
}
|
||||
|
||||
func GetNodeLister(client clientset.Interface, stopChannel <-chan struct{}) corelisters.NodeLister {
|
||||
if stopChannel == nil {
|
||||
return nil
|
||||
}
|
||||
listWatcher := cache.NewListWatchFromClient(client.Core().RESTClient(), "nodes", v1.NamespaceAll, fields.Everything())
|
||||
store := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||
nodeLister := corelisters.NewNodeLister(store)
|
||||
reflector := cache.NewReflector(listWatcher, &v1.Node{}, store, time.Hour)
|
||||
reflector.RunUntil(stopChannel)
|
||||
go reflector.Run(stopChannel)
|
||||
|
||||
// To give some time so that listing works, chosen randomly
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
return nodeLister
|
||||
}
|
||||
|
||||
// IsReady checks if the descheduler could run against given node.
|
||||
func IsReady(node *v1.Node) bool {
|
||||
for i := range node.Status.Conditions {
|
||||
cond := &node.Status.Conditions[i]
|
||||
@@ -78,7 +99,7 @@ func IsReady(node *v1.Node) bool {
|
||||
// - NodeOutOfDisk condition status is ConditionFalse,
|
||||
// - NodeNetworkUnavailable condition status is ConditionFalse.
|
||||
if cond.Type == v1.NodeReady && cond.Status != v1.ConditionTrue {
|
||||
fmt.Printf("Ignoring node %v with %v condition status %v", node.Name, cond.Type, cond.Status)
|
||||
glog.V(1).Infof("Ignoring node %v with %v condition status %v", node.Name, cond.Type, cond.Status)
|
||||
return false
|
||||
} /*else if cond.Type == v1.NodeOutOfDisk && cond.Status != v1.ConditionFalse {
|
||||
glog.V(4).Infof("Ignoring node %v with %v condition status %v", node.Name, cond.Type, cond.Status)
|
||||
@@ -95,3 +116,52 @@ func IsReady(node *v1.Node) bool {
|
||||
}*/
|
||||
return true
|
||||
}
|
||||
|
||||
// IsNodeUschedulable checks if the node is unschedulable. This is helper function to check only in case of
|
||||
// underutilized node so that they won't be accounted for.
|
||||
func IsNodeUschedulable(node *v1.Node) bool {
|
||||
if node.Spec.Unschedulable {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PodFitsAnyNode checks if the given pod fits any of the given nodes, based on
|
||||
// multiple criteria, like, pod node selector matching the node label, node
|
||||
// being schedulable or not.
|
||||
func PodFitsAnyNode(pod *v1.Pod, nodes []*v1.Node) bool {
|
||||
for _, node := range nodes {
|
||||
|
||||
ok, err := utils.PodMatchNodeSelector(pod, node)
|
||||
if err != nil || !ok {
|
||||
continue
|
||||
}
|
||||
if ok {
|
||||
if !IsNodeUschedulable(node) {
|
||||
glog.V(2).Infof("Pod %v can possibly be scheduled on %v", pod.Name, node.Name)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PodFitsCurrentNode checks if the given pod fits on the given node if the pod
|
||||
// node selector matches the node label.
|
||||
func PodFitsCurrentNode(pod *v1.Pod, node *v1.Node) bool {
|
||||
ok, err := utils.PodMatchNodeSelector(pod, node)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return false
|
||||
}
|
||||
|
||||
if !ok {
|
||||
glog.V(1).Infof("Pod %v does not fit on node %v", pod.Name, node.Name)
|
||||
return false
|
||||
}
|
||||
|
||||
glog.V(3).Infof("Pod %v fits on node %v", pod.Name, node.Name)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -17,18 +17,15 @@ limitations under the License.
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/descheduler/test"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
func TestReadyNodes(t *testing.T) {
|
||||
fakeClient := &fake.Clientset{}
|
||||
node1 := test.BuildTestNode("node1", 1000, 2000, 9)
|
||||
node1.Status.Conditions = []v1.NodeCondition{{Type: v1.NodeOutOfDisk, Status: v1.ConditionTrue}}
|
||||
node2 := test.BuildTestNode("node2", 1000, 2000, 9)
|
||||
@@ -41,25 +38,6 @@ func TestReadyNodes(t *testing.T) {
|
||||
node6 := test.BuildTestNode("node6", 1000, 2000, 9)
|
||||
node6.Status.Conditions = []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}}
|
||||
|
||||
fakeClient.Fake.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) {
|
||||
getAction := action.(core.GetAction)
|
||||
switch getAction.GetName() {
|
||||
case node1.Name:
|
||||
return true, node1, nil
|
||||
case node2.Name:
|
||||
return true, node2, nil
|
||||
case node3.Name:
|
||||
return true, node3, nil
|
||||
case node4.Name:
|
||||
return true, node4, nil
|
||||
case node5.Name:
|
||||
return true, node5, nil
|
||||
case node6.Name:
|
||||
return true, node6, nil
|
||||
}
|
||||
return true, nil, fmt.Errorf("Wrong node: %v", getAction.GetName())
|
||||
})
|
||||
|
||||
if !IsReady(node1) {
|
||||
t.Errorf("Expected %v to be ready", node1.Name)
|
||||
}
|
||||
@@ -80,3 +58,290 @@ func TestReadyNodes(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestReadyNodesWithNodeSelector(t *testing.T) {
|
||||
node1 := test.BuildTestNode("node1", 1000, 2000, 9)
|
||||
node1.Labels = map[string]string{"type": "compute"}
|
||||
node2 := test.BuildTestNode("node2", 1000, 2000, 9)
|
||||
node2.Labels = map[string]string{"type": "infra"}
|
||||
|
||||
fakeClient := fake.NewSimpleClientset(node1, node2)
|
||||
nodeSelector := "type=compute"
|
||||
nodes, _ := ReadyNodes(fakeClient, nodeSelector, nil)
|
||||
|
||||
if nodes[0].Name != "node1" {
|
||||
t.Errorf("Expected node1, got %s", nodes[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsNodeUschedulable(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
node *v1.Node
|
||||
IsUnSchedulable bool
|
||||
}{
|
||||
{
|
||||
description: "Node is expected to be schedulable",
|
||||
node: &v1.Node{
|
||||
Spec: v1.NodeSpec{Unschedulable: false},
|
||||
},
|
||||
IsUnSchedulable: false,
|
||||
},
|
||||
{
|
||||
description: "Node is not expected to be schedulable because of unschedulable field",
|
||||
node: &v1.Node{
|
||||
Spec: v1.NodeSpec{Unschedulable: true},
|
||||
},
|
||||
IsUnSchedulable: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
actualUnSchedulable := IsNodeUschedulable(test.node)
|
||||
if actualUnSchedulable != test.IsUnSchedulable {
|
||||
t.Errorf("Test %#v failed", test.description)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPodFitsCurrentNode(t *testing.T) {
|
||||
|
||||
nodeLabelKey := "kubernetes.io/desiredNode"
|
||||
nodeLabelValue := "yes"
|
||||
|
||||
tests := []struct {
|
||||
description string
|
||||
pod *v1.Pod
|
||||
node *v1.Node
|
||||
success bool
|
||||
}{
|
||||
{
|
||||
description: "Pod with nodeAffinity set, expected to fit the node",
|
||||
pod: &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Affinity: &v1.Affinity{
|
||||
NodeAffinity: &v1.NodeAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: nodeLabelKey,
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
nodeLabelValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
node: &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
nodeLabelKey: nodeLabelValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
description: "Pod with nodeAffinity set, not expected to fit the node",
|
||||
pod: &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Affinity: &v1.Affinity{
|
||||
NodeAffinity: &v1.NodeAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: nodeLabelKey,
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
nodeLabelValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
node: &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
nodeLabelKey: "no",
|
||||
},
|
||||
},
|
||||
},
|
||||
success: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
actual := PodFitsCurrentNode(tc.pod, tc.node)
|
||||
if actual != tc.success {
|
||||
t.Errorf("Test %#v failed", tc.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodFitsAnyNode(t *testing.T) {
|
||||
|
||||
nodeLabelKey := "kubernetes.io/desiredNode"
|
||||
nodeLabelValue := "yes"
|
||||
|
||||
tests := []struct {
|
||||
description string
|
||||
pod *v1.Pod
|
||||
nodes []*v1.Node
|
||||
success bool
|
||||
}{
|
||||
{
|
||||
description: "Pod expected to fit one of the nodes",
|
||||
pod: &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Affinity: &v1.Affinity{
|
||||
NodeAffinity: &v1.NodeAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: nodeLabelKey,
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
nodeLabelValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
nodes: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
nodeLabelKey: nodeLabelValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
nodeLabelKey: "no",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
description: "Pod expected to fit none of the nodes",
|
||||
pod: &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Affinity: &v1.Affinity{
|
||||
NodeAffinity: &v1.NodeAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: nodeLabelKey,
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
nodeLabelValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
nodes: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
nodeLabelKey: "unfit1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
nodeLabelKey: "unfit2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
success: false,
|
||||
},
|
||||
{
|
||||
description: "Nodes are unschedulable but labels match, should fail",
|
||||
pod: &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Affinity: &v1.Affinity{
|
||||
NodeAffinity: &v1.NodeAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: nodeLabelKey,
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
nodeLabelValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
nodes: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
nodeLabelKey: nodeLabelValue,
|
||||
},
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Unschedulable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
nodeLabelKey: "no",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
success: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
actual := PodFitsAnyNode(tc.pod, tc.nodes)
|
||||
if actual != tc.success {
|
||||
t.Errorf("Test %#v failed", tc.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,19 +17,74 @@ limitations under the License.
|
||||
package pod
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/api/v1/helper/qos"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/v1/helper/qos"
|
||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||
)
|
||||
|
||||
// checkLatencySensitiveResourcesForAContainer checks if there are any latency sensitive resources like GPUs.
|
||||
func checkLatencySensitiveResourcesForAContainer(rl v1.ResourceList) bool {
|
||||
if rl == nil {
|
||||
return false
|
||||
}
|
||||
for rName := range rl {
|
||||
if rName == v1.ResourceNvidiaGPU {
|
||||
return true
|
||||
}
|
||||
// TODO: Add support for other high value resources like hugepages etc. once kube is rebased to 1.8.
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsLatencySensitivePod checks if a pod consumes high value devices like GPUs, hugepages or when cpu pinning enabled.
|
||||
func IsLatencySensitivePod(pod *v1.Pod) bool {
|
||||
for _, container := range pod.Spec.Containers {
|
||||
resourceList := container.Resources.Requests
|
||||
if checkLatencySensitiveResourcesForAContainer(resourceList) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsEvictable checks if a pod is evictable or not.
|
||||
func IsEvictable(pod *v1.Pod) bool {
|
||||
ownerRefList := OwnerRef(pod)
|
||||
if IsMirrorPod(pod) || IsPodWithLocalStorage(pod) || len(ownerRefList) == 0 || IsDaemonsetPod(ownerRefList) || IsCriticalPod(pod) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ListEvictablePodsOnNode returns the list of evictable pods on node.
|
||||
func ListEvictablePodsOnNode(client clientset.Interface, node *v1.Node) ([]*v1.Pod, error) {
|
||||
pods, err := ListPodsOnANode(client, node)
|
||||
if err != nil {
|
||||
return []*v1.Pod{}, err
|
||||
}
|
||||
evictablePods := make([]*v1.Pod, 0)
|
||||
for _, pod := range pods {
|
||||
if !IsEvictable(pod) {
|
||||
continue
|
||||
} else {
|
||||
evictablePods = append(evictablePods, pod)
|
||||
}
|
||||
}
|
||||
return evictablePods, nil
|
||||
}
|
||||
|
||||
func ListPodsOnANode(client clientset.Interface, node *v1.Node) ([]*v1.Pod, error) {
|
||||
fieldSelector, err := fields.ParseSelector("spec.nodeName=" + node.Name + ",status.phase!=" + string(api.PodSucceeded) + ",status.phase!=" + string(api.PodFailed))
|
||||
if err != nil {
|
||||
return []*v1.Pod{}, err
|
||||
}
|
||||
|
||||
podList, err := client.CoreV1().Pods(v1.NamespaceAll).List(
|
||||
metav1.ListOptions{FieldSelector: fields.SelectorFromSet(fields.Set{"spec.nodeName": node.Name}).String()})
|
||||
metav1.ListOptions{FieldSelector: fieldSelector.String()})
|
||||
if err != nil {
|
||||
return []*v1.Pod{}, err
|
||||
}
|
||||
@@ -38,7 +93,6 @@ func ListPodsOnANode(client clientset.Interface, node *v1.Node) ([]*v1.Pod, erro
|
||||
for i := range podList.Items {
|
||||
pods = append(pods, &podList.Items[i])
|
||||
}
|
||||
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
@@ -58,9 +112,11 @@ func IsGuaranteedPod(pod *v1.Pod) bool {
|
||||
return qos.GetPodQOS(pod) == v1.PodQOSGuaranteed
|
||||
}
|
||||
|
||||
func IsDaemonsetPod(sr *v1.SerializedReference) bool {
|
||||
if sr != nil {
|
||||
return sr.Reference.Kind == "DaemonSet"
|
||||
func IsDaemonsetPod(ownerRefList []metav1.OwnerReference) bool {
|
||||
for _, ownerRef := range ownerRefList {
|
||||
if ownerRef.Kind == "DaemonSet" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -81,15 +137,7 @@ func IsPodWithLocalStorage(pod *v1.Pod) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// CreatorRef returns the kind of the creator reference of the pod.
|
||||
func CreatorRef(pod *v1.Pod) (*v1.SerializedReference, error) {
|
||||
creatorRef, found := pod.ObjectMeta.Annotations[v1.CreatedByAnnotation]
|
||||
if !found {
|
||||
return nil, nil
|
||||
}
|
||||
var sr v1.SerializedReference
|
||||
if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), []byte(creatorRef), &sr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &sr, nil
|
||||
// OwnerRef returns the ownerRefList for the pod.
|
||||
func OwnerRef(pod *v1.Pod) []metav1.OwnerReference {
|
||||
return pod.ObjectMeta.GetOwnerReferences()
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/descheduler/test"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
func TestPodTypes(t *testing.T) {
|
||||
@@ -33,20 +33,25 @@ func TestPodTypes(t *testing.T) {
|
||||
p3 := test.BuildTestPod("p3", 400, 0, n1.Name)
|
||||
p4 := test.BuildTestPod("p4", 400, 0, n1.Name)
|
||||
p5 := test.BuildTestPod("p5", 400, 0, n1.Name)
|
||||
p6 := test.BuildTestPod("p6", 400, 0, n1.Name)
|
||||
p6.Spec.Containers[0].Resources.Requests[v1.ResourceNvidiaGPU] = *resource.NewMilliQuantity(3, resource.DecimalSI)
|
||||
|
||||
p1.Annotations = test.GetReplicaSetAnnotation()
|
||||
p6.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
|
||||
p1.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
// The following 4 pods won't get evicted.
|
||||
// A daemonset.
|
||||
p2.Annotations = test.GetDaemonSetAnnotation()
|
||||
//p2.Annotations = test.GetDaemonSetAnnotation()
|
||||
p2.ObjectMeta.OwnerReferences = test.GetDaemonSetOwnerRefList()
|
||||
// A pod with local storage.
|
||||
p3.Annotations = test.GetNormalPodAnnotation()
|
||||
p3.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
p3.Spec.Volumes = []v1.Volume{
|
||||
{
|
||||
Name: "sample",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{Path: "somePath"},
|
||||
EmptyDir: &v1.EmptyDirVolumeSource{
|
||||
SizeLimit: *resource.NewQuantity(int64(10), resource.BinarySI)},
|
||||
SizeLimit: resource.NewQuantity(int64(10), resource.BinarySI)},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -64,13 +69,16 @@ func TestPodTypes(t *testing.T) {
|
||||
if !IsPodWithLocalStorage(p3) {
|
||||
t.Errorf("Expected p3 to be a pod with local storage.")
|
||||
}
|
||||
sr, _ := CreatorRef(p2)
|
||||
if !IsDaemonsetPod(sr) {
|
||||
ownerRefList := OwnerRef(p2)
|
||||
if !IsDaemonsetPod(ownerRefList) {
|
||||
t.Errorf("Expected p2 to be a daemonset pod.")
|
||||
}
|
||||
sr, _ = CreatorRef(p1)
|
||||
if IsDaemonsetPod(sr) || IsPodWithLocalStorage(p1) || IsCriticalPod(p1) || IsMirrorPod(p1) {
|
||||
ownerRefList = OwnerRef(p1)
|
||||
if IsDaemonsetPod(ownerRefList) || IsPodWithLocalStorage(p1) || IsCriticalPod(p1) || IsMirrorPod(p1) {
|
||||
t.Errorf("Expected p1 to be a normal pod.")
|
||||
}
|
||||
if !IsLatencySensitivePod(p6) {
|
||||
t.Errorf("Expected p6 to be latency sensitive pod")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/api"
|
||||
_ "github.com/kubernetes-incubator/descheduler/pkg/api/install"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/api/v1alpha1"
|
||||
@@ -30,7 +31,7 @@ import (
|
||||
|
||||
func LoadPolicyConfig(policyConfigFile string) (*api.DeschedulerPolicy, error) {
|
||||
if policyConfigFile == "" {
|
||||
fmt.Printf("policy config file not specified")
|
||||
glog.V(1).Infof("policy config file not specified")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ limitations under the License.
|
||||
package strategies
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
//TODO: Change to client-go instead of generated clientset.
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
|
||||
"github.com/kubernetes-incubator/descheduler/cmd/descheduler/app/options"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/api"
|
||||
@@ -36,43 +36,45 @@ type DuplicatePodsMap map[string][]*v1.Pod
|
||||
// RemoveDuplicatePods removes the duplicate pods on node. This strategy evicts all duplicate pods on node.
|
||||
// A pod is said to be a duplicate of other if both of them are from same creator, kind and are within the same
|
||||
// namespace. As of now, this strategy won't evict daemonsets, mirror pods, critical pods and pods with local storages.
|
||||
func RemoveDuplicatePods(ds *options.DeschedulerServer, strategy api.DeschedulerStrategy, policyGroupVersion string, nodes []*v1.Node) {
|
||||
func RemoveDuplicatePods(ds *options.DeschedulerServer, strategy api.DeschedulerStrategy, policyGroupVersion string, nodes []*v1.Node, nodepodCount nodePodEvictedCount) {
|
||||
if !strategy.Enabled {
|
||||
return
|
||||
}
|
||||
deleteDuplicatePods(ds.Client, policyGroupVersion, nodes, ds.DryRun)
|
||||
deleteDuplicatePods(ds.Client, policyGroupVersion, nodes, ds.DryRun, nodepodCount, ds.MaxNoOfPodsToEvictPerNode)
|
||||
}
|
||||
|
||||
// deleteDuplicatePods evicts the pod from node and returns the count of evicted pods.
|
||||
func deleteDuplicatePods(client clientset.Interface, policyGroupVersion string, nodes []*v1.Node, dryRun bool) int {
|
||||
func deleteDuplicatePods(client clientset.Interface, policyGroupVersion string, nodes []*v1.Node, dryRun bool, nodepodCount nodePodEvictedCount, maxPodsToEvict int) int {
|
||||
podsEvicted := 0
|
||||
for _, node := range nodes {
|
||||
fmt.Printf("\nProcessing node: %#v\n", node.Name)
|
||||
glog.V(1).Infof("Processing node: %#v", node.Name)
|
||||
dpm := ListDuplicatePodsOnANode(client, node)
|
||||
for creator, pods := range dpm {
|
||||
if len(pods) > 1 {
|
||||
fmt.Printf("%#v\n", creator)
|
||||
glog.V(1).Infof("%#v", creator)
|
||||
// i = 0 does not evict the first pod
|
||||
for i := 1; i < len(pods); i++ {
|
||||
//fmt.Printf("Removing duplicate pod %#v\n", k.Name)
|
||||
if maxPodsToEvict > 0 && nodepodCount[node]+1 > maxPodsToEvict {
|
||||
break
|
||||
}
|
||||
success, err := evictions.EvictPod(client, pods[i], policyGroupVersion, dryRun)
|
||||
if !success {
|
||||
//TODO: change fmt.Printf as glogs.
|
||||
fmt.Printf("Error when evicting pod: %#v (%#v)\n", pods[i].Name, err)
|
||||
glog.Infof("Error when evicting pod: %#v (%#v)", pods[i].Name, err)
|
||||
} else {
|
||||
podsEvicted++
|
||||
fmt.Printf("Evicted pod: %#v (%#v)\n", pods[i].Name, err)
|
||||
nodepodCount[node]++
|
||||
glog.V(1).Infof("Evicted pod: %#v (%#v)", pods[i].Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
podsEvicted += nodepodCount[node]
|
||||
}
|
||||
return podsEvicted
|
||||
}
|
||||
|
||||
// ListDuplicatePodsOnANode lists duplicate pods on a given node.
|
||||
func ListDuplicatePodsOnANode(client clientset.Interface, node *v1.Node) DuplicatePodsMap {
|
||||
pods, err := podutil.ListPodsOnANode(client, node)
|
||||
pods, err := podutil.ListEvictablePodsOnNode(client, node)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -83,15 +85,14 @@ func ListDuplicatePodsOnANode(client clientset.Interface, node *v1.Node) Duplica
|
||||
func FindDuplicatePods(pods []*v1.Pod) DuplicatePodsMap {
|
||||
dpm := DuplicatePodsMap{}
|
||||
for _, pod := range pods {
|
||||
sr, err := podutil.CreatorRef(pod)
|
||||
if err != nil || sr == nil {
|
||||
continue
|
||||
// Ignoring the error here as in the ListDuplicatePodsOnNode function we call ListEvictablePodsOnNode
|
||||
// which checks for error.
|
||||
ownerRefList := podutil.OwnerRef(pod)
|
||||
for _, ownerRef := range ownerRefList {
|
||||
// ownerRef doesn't need namespace since the assumption is owner needs to be in the same namespace.
|
||||
s := strings.Join([]string{ownerRef.Kind, ownerRef.Name}, "/")
|
||||
dpm[s] = append(dpm[s], pod)
|
||||
}
|
||||
if podutil.IsMirrorPod(pod) || podutil.IsDaemonsetPod(sr) || podutil.IsPodWithLocalStorage(pod) || podutil.IsCriticalPod(pod) {
|
||||
continue
|
||||
}
|
||||
s := strings.Join([]string{sr.Reference.Kind, sr.Reference.Namespace, sr.Reference.Name}, "/")
|
||||
dpm[s] = append(dpm[s], pod)
|
||||
}
|
||||
return dpm
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/descheduler/test"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
|
||||
)
|
||||
|
||||
//TODO:@ravisantoshgudimetla This could be made table driven.
|
||||
@@ -37,24 +37,28 @@ func TestFindDuplicatePods(t *testing.T) {
|
||||
p5 := test.BuildTestPod("p5", 100, 0, node.Name)
|
||||
p6 := test.BuildTestPod("p6", 100, 0, node.Name)
|
||||
p7 := test.BuildTestPod("p7", 100, 0, node.Name)
|
||||
p8 := test.BuildTestPod("p8", 100, 0, node.Name)
|
||||
p9 := test.BuildTestPod("p9", 100, 0, node.Name)
|
||||
|
||||
// All the following pods expect for one will be evicted.
|
||||
p1.Annotations = test.GetReplicaSetAnnotation()
|
||||
p2.Annotations = test.GetReplicaSetAnnotation()
|
||||
p3.Annotations = test.GetReplicaSetAnnotation()
|
||||
p1.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
p2.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
p3.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
p8.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
p9.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
|
||||
// The following 4 pods won't get evicted.
|
||||
// A daemonset.
|
||||
p4.Annotations = test.GetDaemonSetAnnotation()
|
||||
p4.ObjectMeta.OwnerReferences = test.GetDaemonSetOwnerRefList()
|
||||
// A pod with local storage.
|
||||
p5.Annotations = test.GetNormalPodAnnotation()
|
||||
p5.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
p5.Spec.Volumes = []v1.Volume{
|
||||
{
|
||||
Name: "sample",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{Path: "somePath"},
|
||||
EmptyDir: &v1.EmptyDirVolumeSource{
|
||||
SizeLimit: *resource.NewQuantity(int64(10), resource.BinarySI)},
|
||||
SizeLimit: resource.NewQuantity(int64(10), resource.BinarySI)},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -66,12 +70,14 @@ func TestFindDuplicatePods(t *testing.T) {
|
||||
expectedEvictedPodCount := 2
|
||||
fakeClient := &fake.Clientset{}
|
||||
fakeClient.Fake.AddReactor("list", "pods", func(action core.Action) (bool, runtime.Object, error) {
|
||||
return true, &v1.PodList{Items: []v1.Pod{*p1, *p2, *p3, *p4, *p5, *p6, *p7}}, nil
|
||||
return true, &v1.PodList{Items: []v1.Pod{*p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9}}, nil
|
||||
})
|
||||
fakeClient.Fake.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) {
|
||||
return true, node, nil
|
||||
})
|
||||
podsEvicted := deleteDuplicatePods(fakeClient, "v1", []*v1.Node{node}, false)
|
||||
npe := nodePodEvictedCount{}
|
||||
npe[node] = 0
|
||||
podsEvicted := deleteDuplicatePods(fakeClient, "v1", []*v1.Node{node}, false, npe, 2)
|
||||
if podsEvicted != expectedEvictedPodCount {
|
||||
t.Errorf("Unexpected no of pods evicted")
|
||||
}
|
||||
|
||||
@@ -17,31 +17,34 @@ limitations under the License.
|
||||
package strategies
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
helper "k8s.io/kubernetes/pkg/api/v1/resource"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
||||
|
||||
"github.com/kubernetes-incubator/descheduler/cmd/descheduler/app/options"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/api"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/descheduler/evictions"
|
||||
nodeutil "github.com/kubernetes-incubator/descheduler/pkg/descheduler/node"
|
||||
podutil "github.com/kubernetes-incubator/descheduler/pkg/descheduler/pod"
|
||||
)
|
||||
|
||||
type NodeUsageMap struct {
|
||||
node *v1.Node
|
||||
usage api.ResourceThresholds
|
||||
allPods []*v1.Pod
|
||||
nonRemovablePods []*v1.Pod
|
||||
bePods []*v1.Pod
|
||||
bPods []*v1.Pod
|
||||
gPods []*v1.Pod
|
||||
}
|
||||
|
||||
type NodePodsMap map[*v1.Node][]*v1.Pod
|
||||
|
||||
func LowNodeUtilization(ds *options.DeschedulerServer, strategy api.DeschedulerStrategy, evictionPolicyGroupVersion string, nodes []*v1.Node) {
|
||||
func LowNodeUtilization(ds *options.DeschedulerServer, strategy api.DeschedulerStrategy, evictionPolicyGroupVersion string, nodes []*v1.Node, nodepodCount nodePodEvictedCount) {
|
||||
if !strategy.Enabled {
|
||||
return
|
||||
}
|
||||
@@ -57,75 +60,102 @@ func LowNodeUtilization(ds *options.DeschedulerServer, strategy api.DeschedulerS
|
||||
return
|
||||
}
|
||||
|
||||
npm := CreateNodePodsMap(ds.Client, nodes)
|
||||
lowNodes, targetNodes, _ := classifyNodes(npm, thresholds, targetThresholds)
|
||||
npm := createNodePodsMap(ds.Client, nodes)
|
||||
lowNodes, targetNodes := classifyNodes(npm, thresholds, targetThresholds)
|
||||
|
||||
glog.V(1).Infof("Criteria for a node under utilization: CPU: %v, Mem: %v, Pods: %v",
|
||||
thresholds[v1.ResourceCPU], thresholds[v1.ResourceMemory], thresholds[v1.ResourcePods])
|
||||
|
||||
if len(lowNodes) == 0 {
|
||||
fmt.Printf("No node is underutilized\n")
|
||||
return
|
||||
} else if len(lowNodes) < strategy.Params.NodeResourceUtilizationThresholds.NumberOfNodes {
|
||||
fmt.Printf("number of nodes underutilized is less than NumberOfNodes\n")
|
||||
return
|
||||
} else if len(lowNodes) == len(nodes) {
|
||||
fmt.Printf("all nodes are underutilized\n")
|
||||
return
|
||||
} else if len(targetNodes) == 0 {
|
||||
fmt.Printf("no node is above target utilization\n")
|
||||
glog.V(1).Infof("No node is underutilized, nothing to do here, you might tune your thersholds further")
|
||||
return
|
||||
}
|
||||
evictPodsFromTargetNodes(ds.Client, evictionPolicyGroupVersion, targetNodes, lowNodes, targetThresholds, ds.DryRun)
|
||||
glog.V(1).Infof("Total number of underutilized nodes: %v", len(lowNodes))
|
||||
|
||||
if len(lowNodes) < strategy.Params.NodeResourceUtilizationThresholds.NumberOfNodes {
|
||||
glog.V(1).Infof("number of nodes underutilized (%v) is less than NumberOfNodes (%v), nothing to do here", len(lowNodes), strategy.Params.NodeResourceUtilizationThresholds.NumberOfNodes)
|
||||
return
|
||||
}
|
||||
|
||||
if len(lowNodes) == len(nodes) {
|
||||
glog.V(1).Infof("all nodes are underutilized, nothing to do here")
|
||||
return
|
||||
}
|
||||
|
||||
if len(targetNodes) == 0 {
|
||||
glog.V(1).Infof("all nodes are under target utilization, nothing to do here")
|
||||
return
|
||||
}
|
||||
|
||||
glog.V(1).Infof("Criteria for a node above target utilization: CPU: %v, Mem: %v, Pods: %v",
|
||||
targetThresholds[v1.ResourceCPU], targetThresholds[v1.ResourceMemory], targetThresholds[v1.ResourcePods])
|
||||
glog.V(1).Infof("Total number of nodes above target utilization: %v", len(targetNodes))
|
||||
|
||||
totalPodsEvicted := evictPodsFromTargetNodes(ds.Client, evictionPolicyGroupVersion, targetNodes, lowNodes, targetThresholds, ds.DryRun, ds.MaxNoOfPodsToEvictPerNode, nodepodCount)
|
||||
glog.V(1).Infof("Total number of pods evicted: %v", totalPodsEvicted)
|
||||
|
||||
}
|
||||
|
||||
func validateThresholds(thresholds api.ResourceThresholds) bool {
|
||||
if thresholds == nil {
|
||||
fmt.Printf("no resource threshold is configured\n")
|
||||
if thresholds == nil || len(thresholds) == 0 {
|
||||
glog.V(1).Infof("no resource threshold is configured")
|
||||
return false
|
||||
}
|
||||
found := false
|
||||
for name, _ := range thresholds {
|
||||
if name == v1.ResourceCPU || name == v1.ResourceMemory || name == v1.ResourcePods {
|
||||
found = true
|
||||
break
|
||||
for name := range thresholds {
|
||||
switch name {
|
||||
case v1.ResourceCPU:
|
||||
continue
|
||||
case v1.ResourceMemory:
|
||||
continue
|
||||
case v1.ResourcePods:
|
||||
continue
|
||||
default:
|
||||
glog.Errorf("only cpu, memory, or pods thresholds can be specified")
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
fmt.Printf("one of cpu, memory, or pods resource threshold must be configured\n")
|
||||
return false
|
||||
}
|
||||
return found
|
||||
return true
|
||||
}
|
||||
|
||||
//This function could be merged into above once we are clear.
|
||||
func validateTargetThresholds(targetThresholds api.ResourceThresholds) bool {
|
||||
if targetThresholds == nil {
|
||||
fmt.Printf("no target resource threshold is configured\n")
|
||||
glog.V(1).Infof("no target resource threshold is configured")
|
||||
return false
|
||||
} else if _, ok := targetThresholds[v1.ResourcePods]; !ok {
|
||||
fmt.Printf("no target resource threshold for pods is configured\n")
|
||||
glog.V(1).Infof("no target resource threshold for pods is configured")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func classifyNodes(npm NodePodsMap, thresholds api.ResourceThresholds, targetThresholds api.ResourceThresholds) ([]NodeUsageMap, []NodeUsageMap, []NodeUsageMap) {
|
||||
lowNodes, targetNodes, otherNodes := []NodeUsageMap{}, []NodeUsageMap{}, []NodeUsageMap{}
|
||||
// classifyNodes classifies the nodes into low-utilization or high-utilization nodes. If a node lies between
|
||||
// low and high thresholds, it is simply ignored.
|
||||
func classifyNodes(npm NodePodsMap, thresholds api.ResourceThresholds, targetThresholds api.ResourceThresholds) ([]NodeUsageMap, []NodeUsageMap) {
|
||||
lowNodes, targetNodes := []NodeUsageMap{}, []NodeUsageMap{}
|
||||
for node, pods := range npm {
|
||||
usage, nonRemovablePods, bePods, bPods, gPods := NodeUtilization(node, pods)
|
||||
nuMap := NodeUsageMap{node, usage, nonRemovablePods, bePods, bPods, gPods}
|
||||
fmt.Printf("Node %#v usage: %#v\n", node.Name, usage)
|
||||
if IsNodeWithLowUtilization(usage, thresholds) {
|
||||
usage, allPods, nonRemovablePods, bePods, bPods, gPods := NodeUtilization(node, pods)
|
||||
nuMap := NodeUsageMap{node, usage, allPods, nonRemovablePods, bePods, bPods, gPods}
|
||||
|
||||
// Check if node is underutilized and if we can schedule pods on it.
|
||||
if !nodeutil.IsNodeUschedulable(node) && IsNodeWithLowUtilization(usage, thresholds) {
|
||||
glog.V(2).Infof("Node %#v is under utilized with usage: %#v", node.Name, usage)
|
||||
lowNodes = append(lowNodes, nuMap)
|
||||
} else if IsNodeAboveTargetUtilization(usage, targetThresholds) {
|
||||
glog.V(2).Infof("Node %#v is over utilized with usage: %#v", node.Name, usage)
|
||||
targetNodes = append(targetNodes, nuMap)
|
||||
} else {
|
||||
// Seems we don't need to collect them?
|
||||
otherNodes = append(otherNodes, nuMap)
|
||||
glog.V(2).Infof("Node %#v is appropriately utilized with usage: %#v", node.Name, usage)
|
||||
}
|
||||
glog.V(2).Infof("allPods:%v, nonRemovablePods:%v, bePods:%v, bPods:%v, gPods:%v", len(allPods), len(nonRemovablePods), len(bePods), len(bPods), len(gPods))
|
||||
}
|
||||
return lowNodes, targetNodes, otherNodes
|
||||
return lowNodes, targetNodes
|
||||
}
|
||||
|
||||
func evictPodsFromTargetNodes(client clientset.Interface, evictionPolicyGroupVersion string, targetNodes, lowNodes []NodeUsageMap, targetThresholds api.ResourceThresholds, dryRun bool) int {
|
||||
// evictPodsFromTargetNodes evicts pods based on priority, if all the pods on the node have priority, if not
|
||||
// evicts them based on QoS as fallback option.
|
||||
// TODO: @ravig Break this function into smaller functions.
|
||||
func evictPodsFromTargetNodes(client clientset.Interface, evictionPolicyGroupVersion string, targetNodes, lowNodes []NodeUsageMap, targetThresholds api.ResourceThresholds, dryRun bool, maxPodsToEvict int, nodepodCount nodePodEvictedCount) int {
|
||||
podsEvicted := 0
|
||||
|
||||
SortNodesByUsage(targetNodes)
|
||||
@@ -154,18 +184,41 @@ func evictPodsFromTargetNodes(client clientset.Interface, evictionPolicyGroupVer
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(1).Infof("Total capacity to be moved: CPU:%v, Mem:%v, Pods:%v", totalCpu, totalMem, totalPods)
|
||||
glog.V(1).Infof("********Number of pods evicted from each node:***********")
|
||||
|
||||
for _, node := range targetNodes {
|
||||
nodeCapacity := node.node.Status.Capacity
|
||||
if len(node.node.Status.Allocatable) > 0 {
|
||||
nodeCapacity = node.node.Status.Allocatable
|
||||
}
|
||||
fmt.Printf("evicting pods from node %#v with usage: %#v\n", node.node.Name, node.usage)
|
||||
// evict best effort pods
|
||||
evictPods(node.bePods, client, evictionPolicyGroupVersion, targetThresholds, nodeCapacity, node.usage, &totalPods, &totalCpu, &totalMem, &podsEvicted, dryRun)
|
||||
// evict burstable pods
|
||||
evictPods(node.bPods, client, evictionPolicyGroupVersion, targetThresholds, nodeCapacity, node.usage, &totalPods, &totalCpu, &totalMem, &podsEvicted, dryRun)
|
||||
// evict guaranteed pods
|
||||
evictPods(node.gPods, client, evictionPolicyGroupVersion, targetThresholds, nodeCapacity, node.usage, &totalPods, &totalCpu, &totalMem, &podsEvicted, dryRun)
|
||||
glog.V(3).Infof("evicting pods from node %#v with usage: %#v", node.node.Name, node.usage)
|
||||
currentPodsEvicted := nodepodCount[node.node]
|
||||
|
||||
// Check if one pod has priority, if yes, assume that all pods have priority and evict pods based on priority.
|
||||
if node.allPods[0].Spec.Priority != nil {
|
||||
glog.V(1).Infof("All pods have priority associated with them. Evicting pods based on priority")
|
||||
evictablePods := make([]*v1.Pod, 0)
|
||||
evictablePods = append(append(node.bPods, node.bePods...), node.gPods...)
|
||||
|
||||
// sort the evictable Pods based on priority. This also sorts them based on QoS. If there are multiple pods with same priority, they are sorted based on QoS tiers.
|
||||
sortPodsBasedOnPriority(evictablePods)
|
||||
evictPods(evictablePods, client, evictionPolicyGroupVersion, targetThresholds, nodeCapacity, node.usage, &totalPods, &totalCpu, &totalMem, ¤tPodsEvicted, dryRun, maxPodsToEvict)
|
||||
} else {
|
||||
// TODO: Remove this when we support only priority.
|
||||
// Falling back to evicting pods based on priority.
|
||||
glog.V(1).Infof("Evicting pods based on QoS")
|
||||
glog.V(1).Infof("There are %v non-evictable pods on the node", len(node.nonRemovablePods))
|
||||
// evict best effort pods
|
||||
evictPods(node.bePods, client, evictionPolicyGroupVersion, targetThresholds, nodeCapacity, node.usage, &totalPods, &totalCpu, &totalMem, ¤tPodsEvicted, dryRun, maxPodsToEvict)
|
||||
// evict burstable pods
|
||||
evictPods(node.bPods, client, evictionPolicyGroupVersion, targetThresholds, nodeCapacity, node.usage, &totalPods, &totalCpu, &totalMem, ¤tPodsEvicted, dryRun, maxPodsToEvict)
|
||||
// evict guaranteed pods
|
||||
evictPods(node.gPods, client, evictionPolicyGroupVersion, targetThresholds, nodeCapacity, node.usage, &totalPods, &totalCpu, &totalMem, ¤tPodsEvicted, dryRun, maxPodsToEvict)
|
||||
}
|
||||
nodepodCount[node.node] = currentPodsEvicted
|
||||
podsEvicted = podsEvicted + nodepodCount[node.node]
|
||||
glog.V(1).Infof("%v pods evicted from node %#v with usage %v", nodepodCount[node.node], node.node.Name, node.usage)
|
||||
}
|
||||
return podsEvicted
|
||||
}
|
||||
@@ -180,17 +233,20 @@ func evictPods(inputPods []*v1.Pod,
|
||||
totalCpu *float64,
|
||||
totalMem *float64,
|
||||
podsEvicted *int,
|
||||
dryRun bool) {
|
||||
dryRun bool, maxPodsToEvict int) {
|
||||
if IsNodeAboveTargetUtilization(nodeUsage, targetThresholds) && (*totalPods > 0 || *totalCpu > 0 || *totalMem > 0) {
|
||||
onePodPercentage := api.Percentage((float64(1) * 100) / float64(nodeCapacity.Pods().Value()))
|
||||
for _, pod := range inputPods {
|
||||
if maxPodsToEvict > 0 && *podsEvicted+1 > maxPodsToEvict {
|
||||
break
|
||||
}
|
||||
cUsage := helper.GetResourceRequest(pod, v1.ResourceCPU)
|
||||
mUsage := helper.GetResourceRequest(pod, v1.ResourceMemory)
|
||||
success, err := evictions.EvictPod(client, pod, evictionPolicyGroupVersion, dryRun)
|
||||
if !success {
|
||||
fmt.Printf("Error when evicting pod: %#v (%#v)\n", pod.Name, err)
|
||||
glog.Warningf("Error when evicting pod: %#v (%#v)", pod.Name, err)
|
||||
} else {
|
||||
fmt.Printf("Evicted pod: %#v (%#v)\n", pod.Name, err)
|
||||
glog.V(3).Infof("Evicted pod: %#v (%#v)", pod.Name, err)
|
||||
// update remaining pods
|
||||
*podsEvicted++
|
||||
nodeUsage[v1.ResourcePods] -= onePodPercentage
|
||||
@@ -204,7 +260,7 @@ func evictPods(inputPods []*v1.Pod,
|
||||
*totalMem -= float64(mUsage)
|
||||
nodeUsage[v1.ResourceMemory] -= api.Percentage(float64(mUsage) / float64(nodeCapacity.Memory().Value()) * 100)
|
||||
|
||||
fmt.Printf("updated node usage: %#v\n", nodeUsage)
|
||||
glog.V(3).Infof("updated node usage: %#v", nodeUsage)
|
||||
// check if node utilization drops below target threshold or required capacity (cpu, memory, pods) is moved
|
||||
if !IsNodeAboveTargetUtilization(nodeUsage, targetThresholds) || (*totalPods <= 0 && *totalCpu <= 0 && *totalMem <= 0) {
|
||||
break
|
||||
@@ -232,12 +288,35 @@ func SortNodesByUsage(nodes []NodeUsageMap) {
|
||||
})
|
||||
}
|
||||
|
||||
func CreateNodePodsMap(client clientset.Interface, nodes []*v1.Node) NodePodsMap {
|
||||
// sortPodsBasedOnPriority sorts pods based on priority and if their priorities are equal, they are sorted based on QoS tiers.
|
||||
func sortPodsBasedOnPriority(evictablePods []*v1.Pod) {
|
||||
sort.Slice(evictablePods, func(i, j int) bool {
|
||||
if evictablePods[i].Spec.Priority == nil && evictablePods[j].Spec.Priority != nil {
|
||||
return true
|
||||
}
|
||||
if evictablePods[j].Spec.Priority == nil && evictablePods[i].Spec.Priority != nil {
|
||||
return false
|
||||
}
|
||||
if (evictablePods[j].Spec.Priority == nil && evictablePods[i].Spec.Priority == nil) || (*evictablePods[i].Spec.Priority == *evictablePods[j].Spec.Priority) {
|
||||
if podutil.IsBestEffortPod(evictablePods[i]) {
|
||||
return true
|
||||
}
|
||||
if podutil.IsBurstablePod(evictablePods[i]) && podutil.IsGuaranteedPod(evictablePods[j]) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return *evictablePods[i].Spec.Priority < *evictablePods[j].Spec.Priority
|
||||
})
|
||||
}
|
||||
|
||||
// createNodePodsMap returns nodepodsmap with evictable pods on node.
|
||||
func createNodePodsMap(client clientset.Interface, nodes []*v1.Node) NodePodsMap {
|
||||
npm := NodePodsMap{}
|
||||
for _, node := range nodes {
|
||||
pods, err := podutil.ListPodsOnANode(client, node)
|
||||
if err != nil {
|
||||
fmt.Printf("node %s will not be processed, error in accessing its pods (%#v)\n", node.Name, err)
|
||||
glog.Warningf("node %s will not be processed, error in accessing its pods (%#v)", node.Name, err)
|
||||
} else {
|
||||
npm[node] = pods
|
||||
}
|
||||
@@ -271,19 +350,16 @@ func IsNodeWithLowUtilization(nodeThresholds api.ResourceThresholds, thresholds
|
||||
return true
|
||||
}
|
||||
|
||||
func NodeUtilization(node *v1.Node, pods []*v1.Pod) (api.ResourceThresholds, []*v1.Pod, []*v1.Pod, []*v1.Pod, []*v1.Pod) {
|
||||
// Nodeutilization returns the current usage of node.
|
||||
func NodeUtilization(node *v1.Node, pods []*v1.Pod) (api.ResourceThresholds, []*v1.Pod, []*v1.Pod, []*v1.Pod, []*v1.Pod, []*v1.Pod) {
|
||||
bePods := []*v1.Pod{}
|
||||
nonRemovablePods := []*v1.Pod{}
|
||||
bPods := []*v1.Pod{}
|
||||
gPods := []*v1.Pod{}
|
||||
totalReqs := map[v1.ResourceName]resource.Quantity{}
|
||||
for _, pod := range pods {
|
||||
sr, err := podutil.CreatorRef(pod)
|
||||
if err != nil {
|
||||
sr = nil
|
||||
}
|
||||
|
||||
if podutil.IsMirrorPod(pod) || podutil.IsPodWithLocalStorage(pod) || sr == nil || podutil.IsDaemonsetPod(sr) || podutil.IsCriticalPod(pod) {
|
||||
// We need to compute the usage of nonRemovablePods unless it is a best effort pod. So, cannot use podutil.ListEvictablePodsOnNode
|
||||
if !podutil.IsEvictable(pod) {
|
||||
nonRemovablePods = append(nonRemovablePods, pod)
|
||||
if podutil.IsBestEffortPod(pod) {
|
||||
continue
|
||||
@@ -297,11 +373,7 @@ func NodeUtilization(node *v1.Node, pods []*v1.Pod) (api.ResourceThresholds, []*
|
||||
gPods = append(gPods, pod)
|
||||
}
|
||||
|
||||
req, _, err := helper.PodRequestsAndLimits(pod)
|
||||
if err != nil {
|
||||
fmt.Printf("Error computing resource usage of pod, ignoring: %#v\n", pod.Name)
|
||||
continue
|
||||
}
|
||||
req, _ := helper.PodRequestsAndLimits(pod)
|
||||
for name, quantity := range req {
|
||||
if name == v1.ResourceCPU || name == v1.ResourceMemory {
|
||||
if value, ok := totalReqs[name]; !ok {
|
||||
@@ -326,5 +398,5 @@ func NodeUtilization(node *v1.Node, pods []*v1.Pod) (api.ResourceThresholds, []*
|
||||
usage[v1.ResourceCPU] = api.Percentage((float64(totalCPUReq.MilliValue()) * 100) / float64(nodeCapacity.Cpu().MilliValue()))
|
||||
usage[v1.ResourceMemory] = api.Percentage(float64(totalMemReq.Value()) / float64(nodeCapacity.Memory().Value()) * 100)
|
||||
usage[v1.ResourcePods] = api.Percentage((float64(totalPods) * 100) / float64(nodeCapacity.Pods().Value()))
|
||||
return usage, nonRemovablePods, bePods, bPods, gPods
|
||||
return usage, pods, nonRemovablePods, bePods, bPods, gPods
|
||||
}
|
||||
|
||||
@@ -18,19 +18,21 @@ package strategies
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/api"
|
||||
"github.com/kubernetes-incubator/descheduler/test"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/api"
|
||||
"github.com/kubernetes-incubator/descheduler/test"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
core "k8s.io/client-go/testing"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// TODO: Make this table driven.
|
||||
func TestLowNodeUtilization(t *testing.T) {
|
||||
func TestLowNodeUtilizationWithoutPriority(t *testing.T) {
|
||||
var thresholds = make(api.ResourceThresholds)
|
||||
var targetThresholds = make(api.ResourceThresholds)
|
||||
thresholds[v1.ResourceCPU] = 30
|
||||
@@ -40,6 +42,9 @@ func TestLowNodeUtilization(t *testing.T) {
|
||||
|
||||
n1 := test.BuildTestNode("n1", 4000, 3000, 9)
|
||||
n2 := test.BuildTestNode("n2", 4000, 3000, 10)
|
||||
n3 := test.BuildTestNode("n3", 4000, 3000, 10)
|
||||
// Making n3 node unschedulable so that it won't counted in lowUtilized nodes list.
|
||||
n3.Spec.Unschedulable = true
|
||||
p1 := test.BuildTestPod("p1", 400, 0, n1.Name)
|
||||
p2 := test.BuildTestPod("p2", 400, 0, n1.Name)
|
||||
p3 := test.BuildTestPod("p3", 400, 0, n1.Name)
|
||||
@@ -51,23 +56,23 @@ func TestLowNodeUtilization(t *testing.T) {
|
||||
p7 := test.BuildTestPod("p7", 400, 0, n1.Name)
|
||||
p8 := test.BuildTestPod("p8", 400, 0, n1.Name)
|
||||
|
||||
p1.Annotations = test.GetReplicaSetAnnotation()
|
||||
p2.Annotations = test.GetReplicaSetAnnotation()
|
||||
p3.Annotations = test.GetReplicaSetAnnotation()
|
||||
p4.Annotations = test.GetReplicaSetAnnotation()
|
||||
p5.Annotations = test.GetReplicaSetAnnotation()
|
||||
p1.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
p2.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
p3.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
p4.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
p5.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
// The following 4 pods won't get evicted.
|
||||
// A daemonset.
|
||||
p6.Annotations = test.GetDaemonSetAnnotation()
|
||||
p6.ObjectMeta.OwnerReferences = test.GetDaemonSetOwnerRefList()
|
||||
// A pod with local storage.
|
||||
p7.Annotations = test.GetNormalPodAnnotation()
|
||||
p7.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
p7.Spec.Volumes = []v1.Volume{
|
||||
{
|
||||
Name: "sample",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{Path: "somePath"},
|
||||
EmptyDir: &v1.EmptyDirVolumeSource{
|
||||
SizeLimit: *resource.NewQuantity(int64(10), resource.BinarySI)},
|
||||
SizeLimit: resource.NewQuantity(int64(10), resource.BinarySI)},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -77,7 +82,7 @@ func TestLowNodeUtilization(t *testing.T) {
|
||||
p8.Namespace = "kube-system"
|
||||
p8.Annotations = test.GetCriticalPodAnnotation()
|
||||
p9 := test.BuildTestPod("p9", 400, 0, n1.Name)
|
||||
p9.Annotations = test.GetReplicaSetAnnotation()
|
||||
p9.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
fakeClient := &fake.Clientset{}
|
||||
fakeClient.Fake.AddReactor("list", "pods", func(action core.Action) (bool, runtime.Object, error) {
|
||||
list := action.(core.ListAction)
|
||||
@@ -88,6 +93,9 @@ func TestLowNodeUtilization(t *testing.T) {
|
||||
if strings.Contains(fieldString, "n2") {
|
||||
return true, &v1.PodList{Items: []v1.Pod{*p9}}, nil
|
||||
}
|
||||
if strings.Contains(fieldString, "n3") {
|
||||
return true, &v1.PodList{Items: []v1.Pod{}}, nil
|
||||
}
|
||||
return true, nil, fmt.Errorf("Failed to list: %v", list)
|
||||
})
|
||||
fakeClient.Fake.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) {
|
||||
@@ -97,15 +105,224 @@ func TestLowNodeUtilization(t *testing.T) {
|
||||
return true, n1, nil
|
||||
case n2.Name:
|
||||
return true, n2, nil
|
||||
case n3.Name:
|
||||
return true, n3, nil
|
||||
}
|
||||
return true, nil, fmt.Errorf("Wrong node: %v", getAction.GetName())
|
||||
})
|
||||
expectedPodsEvicted := 4
|
||||
npm := CreateNodePodsMap(fakeClient, []*v1.Node{n1, n2})
|
||||
lowNodes, targetNodes, _ := classifyNodes(npm, thresholds, targetThresholds)
|
||||
podsEvicted := evictPodsFromTargetNodes(fakeClient, "v1", targetNodes, lowNodes, targetThresholds, false)
|
||||
expectedPodsEvicted := 3
|
||||
npm := createNodePodsMap(fakeClient, []*v1.Node{n1, n2, n3})
|
||||
lowNodes, targetNodes := classifyNodes(npm, thresholds, targetThresholds)
|
||||
if len(lowNodes) != 1 {
|
||||
t.Errorf("After ignoring unschedulable nodes, expected only one node to be under utilized.")
|
||||
}
|
||||
npe := nodePodEvictedCount{}
|
||||
npe[n1] = 0
|
||||
npe[n2] = 0
|
||||
npe[n3] = 0
|
||||
podsEvicted := evictPodsFromTargetNodes(fakeClient, "v1", targetNodes, lowNodes, targetThresholds, false, 3, npe)
|
||||
if expectedPodsEvicted != podsEvicted {
|
||||
t.Errorf("Expected %#v pods to be evicted but %#v got evicted", expectedPodsEvicted)
|
||||
t.Errorf("Expected %#v pods to be evicted but %#v got evicted", expectedPodsEvicted, podsEvicted)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: Make this table driven.
|
||||
func TestLowNodeUtilizationWithPriorities(t *testing.T) {
|
||||
var thresholds = make(api.ResourceThresholds)
|
||||
var targetThresholds = make(api.ResourceThresholds)
|
||||
thresholds[v1.ResourceCPU] = 30
|
||||
thresholds[v1.ResourcePods] = 30
|
||||
targetThresholds[v1.ResourceCPU] = 50
|
||||
targetThresholds[v1.ResourcePods] = 50
|
||||
lowPriority := int32(0)
|
||||
highPriority := int32(10000)
|
||||
n1 := test.BuildTestNode("n1", 4000, 3000, 9)
|
||||
n2 := test.BuildTestNode("n2", 4000, 3000, 10)
|
||||
n3 := test.BuildTestNode("n3", 4000, 3000, 10)
|
||||
// Making n3 node unschedulable so that it won't counted in lowUtilized nodes list.
|
||||
n3.Spec.Unschedulable = true
|
||||
p1 := test.BuildTestPod("p1", 400, 0, n1.Name)
|
||||
p1.Spec.Priority = &highPriority
|
||||
p2 := test.BuildTestPod("p2", 400, 0, n1.Name)
|
||||
p2.Spec.Priority = &highPriority
|
||||
p3 := test.BuildTestPod("p3", 400, 0, n1.Name)
|
||||
p3.Spec.Priority = &highPriority
|
||||
p4 := test.BuildTestPod("p4", 400, 0, n1.Name)
|
||||
p4.Spec.Priority = &highPriority
|
||||
p5 := test.BuildTestPod("p5", 400, 0, n1.Name)
|
||||
p5.Spec.Priority = &lowPriority
|
||||
|
||||
// These won't be evicted.
|
||||
p6 := test.BuildTestPod("p6", 400, 0, n1.Name)
|
||||
p6.Spec.Priority = &highPriority
|
||||
p7 := test.BuildTestPod("p7", 400, 0, n1.Name)
|
||||
p7.Spec.Priority = &lowPriority
|
||||
p8 := test.BuildTestPod("p8", 400, 0, n1.Name)
|
||||
p8.Spec.Priority = &lowPriority
|
||||
|
||||
p1.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
p2.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
p3.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
p4.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
p5.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
// The following 4 pods won't get evicted.
|
||||
// A daemonset.
|
||||
p6.ObjectMeta.OwnerReferences = test.GetDaemonSetOwnerRefList()
|
||||
// A pod with local storage.
|
||||
p7.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
p7.Spec.Volumes = []v1.Volume{
|
||||
{
|
||||
Name: "sample",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{Path: "somePath"},
|
||||
EmptyDir: &v1.EmptyDirVolumeSource{
|
||||
SizeLimit: resource.NewQuantity(int64(10), resource.BinarySI)},
|
||||
},
|
||||
},
|
||||
}
|
||||
// A Mirror Pod.
|
||||
p7.Annotations = test.GetMirrorPodAnnotation()
|
||||
// A Critical Pod.
|
||||
p8.Namespace = "kube-system"
|
||||
p8.Annotations = test.GetCriticalPodAnnotation()
|
||||
p9 := test.BuildTestPod("p9", 400, 0, n1.Name)
|
||||
p9.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
|
||||
fakeClient := &fake.Clientset{}
|
||||
fakeClient.Fake.AddReactor("list", "pods", func(action core.Action) (bool, runtime.Object, error) {
|
||||
list := action.(core.ListAction)
|
||||
fieldString := list.GetListRestrictions().Fields.String()
|
||||
if strings.Contains(fieldString, "n1") {
|
||||
return true, &v1.PodList{Items: []v1.Pod{*p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8}}, nil
|
||||
}
|
||||
if strings.Contains(fieldString, "n2") {
|
||||
return true, &v1.PodList{Items: []v1.Pod{*p9}}, nil
|
||||
}
|
||||
if strings.Contains(fieldString, "n3") {
|
||||
return true, &v1.PodList{Items: []v1.Pod{}}, nil
|
||||
}
|
||||
return true, nil, fmt.Errorf("Failed to list: %v", list)
|
||||
})
|
||||
fakeClient.Fake.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) {
|
||||
getAction := action.(core.GetAction)
|
||||
switch getAction.GetName() {
|
||||
case n1.Name:
|
||||
return true, n1, nil
|
||||
case n2.Name:
|
||||
return true, n2, nil
|
||||
case n3.Name:
|
||||
return true, n3, nil
|
||||
}
|
||||
return true, nil, fmt.Errorf("Wrong node: %v", getAction.GetName())
|
||||
})
|
||||
expectedPodsEvicted := 3
|
||||
npm := createNodePodsMap(fakeClient, []*v1.Node{n1, n2, n3})
|
||||
lowNodes, targetNodes := classifyNodes(npm, thresholds, targetThresholds)
|
||||
if len(lowNodes) != 1 {
|
||||
t.Errorf("After ignoring unschedulable nodes, expected only one node to be under utilized.")
|
||||
}
|
||||
npe := nodePodEvictedCount{}
|
||||
npe[n1] = 0
|
||||
npe[n2] = 0
|
||||
npe[n3] = 0
|
||||
podsEvicted := evictPodsFromTargetNodes(fakeClient, "v1", targetNodes, lowNodes, targetThresholds, false, 3, npe)
|
||||
if expectedPodsEvicted != podsEvicted {
|
||||
t.Errorf("Expected %#v pods to be evicted but %#v got evicted", expectedPodsEvicted, podsEvicted)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSortPodsByPriority(t *testing.T) {
|
||||
n1 := test.BuildTestNode("n1", 4000, 3000, 9)
|
||||
lowPriority := int32(0)
|
||||
highPriority := int32(10000)
|
||||
p1 := test.BuildTestPod("p1", 400, 0, n1.Name)
|
||||
p1.Spec.Priority = &lowPriority
|
||||
|
||||
// BestEffort
|
||||
p2 := test.BuildTestPod("p2", 400, 0, n1.Name)
|
||||
p2.Spec.Priority = &highPriority
|
||||
|
||||
p2.Spec.Containers[0].Resources.Requests = nil
|
||||
p2.Spec.Containers[0].Resources.Limits = nil
|
||||
|
||||
// Burstable
|
||||
p3 := test.BuildTestPod("p3", 400, 0, n1.Name)
|
||||
p3.Spec.Priority = &highPriority
|
||||
|
||||
// Guaranteed
|
||||
p4 := test.BuildTestPod("p4", 400, 100, n1.Name)
|
||||
p4.Spec.Priority = &highPriority
|
||||
p4.Spec.Containers[0].Resources.Limits[v1.ResourceCPU] = *resource.NewMilliQuantity(400, resource.DecimalSI)
|
||||
p4.Spec.Containers[0].Resources.Limits[v1.ResourceMemory] = *resource.NewQuantity(100, resource.DecimalSI)
|
||||
|
||||
// Best effort with nil priorities.
|
||||
p5 := test.BuildTestPod("p5", 400, 100, n1.Name)
|
||||
p5.Spec.Priority = nil
|
||||
p6 := test.BuildTestPod("p6", 400, 100, n1.Name)
|
||||
p6.Spec.Containers[0].Resources.Limits[v1.ResourceCPU] = *resource.NewMilliQuantity(400, resource.DecimalSI)
|
||||
p6.Spec.Containers[0].Resources.Limits[v1.ResourceMemory] = *resource.NewQuantity(100, resource.DecimalSI)
|
||||
p6.Spec.Priority = nil
|
||||
|
||||
podList := []*v1.Pod{p4, p3, p2, p1, p6, p5}
|
||||
|
||||
sortPodsBasedOnPriority(podList)
|
||||
for _, pod := range podList {
|
||||
fmt.Println(pod)
|
||||
}
|
||||
if !reflect.DeepEqual(podList[len(podList)-1], p4) {
|
||||
t.Errorf("Expected last pod in sorted list to be %v which of highest priority and guaranteed but got %v", p4, podList[len(podList)-1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateThresholds(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input api.ResourceThresholds
|
||||
succeed bool
|
||||
}{
|
||||
{
|
||||
name: "passing nil map for threshold",
|
||||
input: nil,
|
||||
succeed: false,
|
||||
},
|
||||
{
|
||||
name: "passing no threshold",
|
||||
input: api.ResourceThresholds{},
|
||||
succeed: false,
|
||||
},
|
||||
{
|
||||
name: "passing unsupported resource name",
|
||||
input: api.ResourceThresholds{
|
||||
v1.ResourceCPU: 40,
|
||||
v1.ResourceStorage: 25.5,
|
||||
},
|
||||
succeed: false,
|
||||
},
|
||||
{
|
||||
name: "passing invalid resource name",
|
||||
input: api.ResourceThresholds{
|
||||
v1.ResourceCPU: 40,
|
||||
"coolResource": 42.0,
|
||||
},
|
||||
succeed: false,
|
||||
},
|
||||
{
|
||||
name: "passing a valid threshold with cpu, memory and pods",
|
||||
input: api.ResourceThresholds{
|
||||
v1.ResourceCPU: 20,
|
||||
v1.ResourceMemory: 30,
|
||||
v1.ResourcePods: 40,
|
||||
},
|
||||
succeed: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
isValid := validateThresholds(test.input)
|
||||
|
||||
if isValid != test.succeed {
|
||||
t.Errorf("expected validity of threshold: %#v\nto be %v but got %v instead", test.input, test.succeed, isValid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
74
pkg/descheduler/strategies/node_affinity.go
Normal file
74
pkg/descheduler/strategies/node_affinity.go
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package strategies
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
"github.com/kubernetes-incubator/descheduler/cmd/descheduler/app/options"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/api"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/descheduler/evictions"
|
||||
nodeutil "github.com/kubernetes-incubator/descheduler/pkg/descheduler/node"
|
||||
podutil "github.com/kubernetes-incubator/descheduler/pkg/descheduler/pod"
|
||||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func RemovePodsViolatingNodeAffinity(ds *options.DeschedulerServer, strategy api.DeschedulerStrategy, evictionPolicyGroupVersion string, nodes []*v1.Node, nodePodCount nodePodEvictedCount) {
|
||||
removePodsViolatingNodeAffinityCount(ds, strategy, evictionPolicyGroupVersion, nodes, nodePodCount, ds.MaxNoOfPodsToEvictPerNode)
|
||||
}
|
||||
|
||||
func removePodsViolatingNodeAffinityCount(ds *options.DeschedulerServer, strategy api.DeschedulerStrategy, evictionPolicyGroupVersion string, nodes []*v1.Node, nodepodCount nodePodEvictedCount, maxPodsToEvict int) int {
|
||||
evictedPodCount := 0
|
||||
if !strategy.Enabled {
|
||||
return evictedPodCount
|
||||
}
|
||||
|
||||
for _, nodeAffinity := range strategy.Params.NodeAffinityType {
|
||||
glog.V(2).Infof("Executing for nodeAffinityType: %v", nodeAffinity)
|
||||
|
||||
switch nodeAffinity {
|
||||
case "requiredDuringSchedulingIgnoredDuringExecution":
|
||||
for _, node := range nodes {
|
||||
glog.V(1).Infof("Processing node: %#v\n", node.Name)
|
||||
|
||||
pods, err := podutil.ListEvictablePodsOnNode(ds.Client, node)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to get pods from %v: %v", node.Name, err)
|
||||
}
|
||||
|
||||
for _, pod := range pods {
|
||||
if maxPodsToEvict > 0 && nodepodCount[node]+1 > maxPodsToEvict {
|
||||
break
|
||||
}
|
||||
if pod.Spec.Affinity != nil && pod.Spec.Affinity.NodeAffinity != nil && pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
||||
|
||||
if !nodeutil.PodFitsCurrentNode(pod, node) && nodeutil.PodFitsAnyNode(pod, nodes) {
|
||||
glog.V(1).Infof("Evicting pod: %v", pod.Name)
|
||||
evictions.EvictPod(ds.Client, pod, evictionPolicyGroupVersion, ds.DryRun)
|
||||
nodepodCount[node]++
|
||||
}
|
||||
}
|
||||
}
|
||||
evictedPodCount += nodepodCount[node]
|
||||
}
|
||||
default:
|
||||
glog.Errorf("invalid nodeAffinityType: %v", nodeAffinity)
|
||||
return evictedPodCount
|
||||
}
|
||||
}
|
||||
glog.V(1).Infof("Evicted %v pods", evictedPodCount)
|
||||
return evictedPodCount
|
||||
}
|
||||
184
pkg/descheduler/strategies/node_affinity_test.go
Normal file
184
pkg/descheduler/strategies/node_affinity_test.go
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package strategies
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/descheduler/cmd/descheduler/app/options"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/api"
|
||||
"github.com/kubernetes-incubator/descheduler/test"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
core "k8s.io/client-go/testing"
|
||||
)
|
||||
|
||||
func TestRemovePodsViolatingNodeAffinity(t *testing.T) {
|
||||
|
||||
requiredDuringSchedulingIgnoredDuringExecutionStrategy := api.DeschedulerStrategy{
|
||||
Enabled: true,
|
||||
Params: api.StrategyParameters{
|
||||
NodeAffinityType: []string{
|
||||
"requiredDuringSchedulingIgnoredDuringExecution",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
nodeLabelKey := "kubernetes.io/desiredNode"
|
||||
nodeLabelValue := "yes"
|
||||
nodeWithLabels := test.BuildTestNode("nodeWithLabels", 2000, 3000, 10)
|
||||
nodeWithLabels.Labels[nodeLabelKey] = nodeLabelValue
|
||||
|
||||
nodeWithoutLabels := test.BuildTestNode("nodeWithoutLabels", 2000, 3000, 10)
|
||||
|
||||
unschedulableNodeWithLabels := test.BuildTestNode("unschedulableNodeWithLabels", 2000, 3000, 10)
|
||||
nodeWithLabels.Labels[nodeLabelKey] = nodeLabelValue
|
||||
unschedulableNodeWithLabels.Spec.Unschedulable = true
|
||||
|
||||
addPodsToNode := func(node *v1.Node) []v1.Pod {
|
||||
podWithNodeAffinity := test.BuildTestPod("podWithNodeAffinity", 100, 0, node.Name)
|
||||
podWithNodeAffinity.Spec.Affinity = &v1.Affinity{
|
||||
NodeAffinity: &v1.NodeAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: nodeLabelKey,
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
nodeLabelValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pod1 := test.BuildTestPod("pod1", 100, 0, node.Name)
|
||||
pod2 := test.BuildTestPod("pod2", 100, 0, node.Name)
|
||||
|
||||
podWithNodeAffinity.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
pod1.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
pod2.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
|
||||
return []v1.Pod{
|
||||
*podWithNodeAffinity,
|
||||
*pod1,
|
||||
*pod2,
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
description string
|
||||
nodes []*v1.Node
|
||||
pods []v1.Pod
|
||||
strategy api.DeschedulerStrategy
|
||||
expectedEvictedPodCount int
|
||||
npe nodePodEvictedCount
|
||||
maxPodsToEvict int
|
||||
}{
|
||||
{
|
||||
description: "Strategy disabled, should not evict any pods",
|
||||
strategy: api.DeschedulerStrategy{
|
||||
Enabled: false,
|
||||
Params: api.StrategyParameters{
|
||||
NodeAffinityType: []string{
|
||||
"requiredDuringSchedulingIgnoredDuringExecution",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedEvictedPodCount: 0,
|
||||
pods: addPodsToNode(nodeWithoutLabels),
|
||||
nodes: []*v1.Node{nodeWithoutLabels, nodeWithLabels},
|
||||
npe: nodePodEvictedCount{nodeWithoutLabels: 0, nodeWithLabels: 0},
|
||||
maxPodsToEvict: 0,
|
||||
},
|
||||
{
|
||||
description: "Invalid strategy type, should not evict any pods",
|
||||
strategy: api.DeschedulerStrategy{
|
||||
Enabled: true,
|
||||
Params: api.StrategyParameters{
|
||||
NodeAffinityType: []string{
|
||||
"requiredDuringSchedulingRequiredDuringExecution",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedEvictedPodCount: 0,
|
||||
pods: addPodsToNode(nodeWithoutLabels),
|
||||
nodes: []*v1.Node{nodeWithoutLabels, nodeWithLabels},
|
||||
npe: nodePodEvictedCount{nodeWithoutLabels: 0, nodeWithLabels: 0},
|
||||
maxPodsToEvict: 0,
|
||||
},
|
||||
{
|
||||
description: "Pod is correctly scheduled on node, no eviction expected",
|
||||
strategy: requiredDuringSchedulingIgnoredDuringExecutionStrategy,
|
||||
expectedEvictedPodCount: 0,
|
||||
pods: addPodsToNode(nodeWithLabels),
|
||||
nodes: []*v1.Node{nodeWithLabels},
|
||||
npe: nodePodEvictedCount{nodeWithLabels: 0},
|
||||
maxPodsToEvict: 0,
|
||||
},
|
||||
{
|
||||
description: "Pod is scheduled on node without matching labels, another schedulable node available, should be evicted",
|
||||
expectedEvictedPodCount: 1,
|
||||
strategy: requiredDuringSchedulingIgnoredDuringExecutionStrategy,
|
||||
pods: addPodsToNode(nodeWithoutLabels),
|
||||
nodes: []*v1.Node{nodeWithoutLabels, nodeWithLabels},
|
||||
npe: nodePodEvictedCount{nodeWithoutLabels: 0, nodeWithLabels: 0},
|
||||
maxPodsToEvict: 0,
|
||||
},
|
||||
{
|
||||
description: "Pod is scheduled on node without matching labels, another schedulable node available, maxPodsToEvict set to 1, should not be evicted",
|
||||
expectedEvictedPodCount: 1,
|
||||
strategy: requiredDuringSchedulingIgnoredDuringExecutionStrategy,
|
||||
pods: addPodsToNode(nodeWithoutLabels),
|
||||
nodes: []*v1.Node{nodeWithoutLabels, nodeWithLabels},
|
||||
npe: nodePodEvictedCount{nodeWithoutLabels: 0, nodeWithLabels: 0},
|
||||
maxPodsToEvict: 1,
|
||||
},
|
||||
{
|
||||
description: "Pod is scheduled on node without matching labels, but no node where pod fits is available, should not evict",
|
||||
expectedEvictedPodCount: 0,
|
||||
strategy: requiredDuringSchedulingIgnoredDuringExecutionStrategy,
|
||||
pods: addPodsToNode(nodeWithoutLabels),
|
||||
nodes: []*v1.Node{nodeWithoutLabels, unschedulableNodeWithLabels},
|
||||
npe: nodePodEvictedCount{nodeWithoutLabels: 0, unschedulableNodeWithLabels: 0},
|
||||
maxPodsToEvict: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
|
||||
fakeClient := &fake.Clientset{}
|
||||
fakeClient.Fake.AddReactor("list", "pods", func(action core.Action) (bool, runtime.Object, error) {
|
||||
return true, &v1.PodList{Items: tc.pods}, nil
|
||||
})
|
||||
|
||||
ds := options.DeschedulerServer{
|
||||
Client: fakeClient,
|
||||
}
|
||||
|
||||
actualEvictedPodCount := removePodsViolatingNodeAffinityCount(&ds, tc.strategy, "v1", tc.nodes, tc.npe, tc.maxPodsToEvict)
|
||||
if actualEvictedPodCount != tc.expectedEvictedPodCount {
|
||||
t.Errorf("Test %#v failed, expected %v pod evictions, but got %v pod evictions\n", tc.description, tc.expectedEvictedPodCount, actualEvictedPodCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
105
pkg/descheduler/strategies/pod_antiaffinity.go
Normal file
105
pkg/descheduler/strategies/pod_antiaffinity.go
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package strategies
|
||||
|
||||
import (
|
||||
"github.com/kubernetes-incubator/descheduler/cmd/descheduler/app/options"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/api"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/descheduler/evictions"
|
||||
podutil "github.com/kubernetes-incubator/descheduler/pkg/descheduler/pod"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
priorityutil "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/priorities/util"
|
||||
)
|
||||
|
||||
// RemovePodsViolatingInterPodAntiAffinity with elimination strategy
|
||||
func RemovePodsViolatingInterPodAntiAffinity(ds *options.DeschedulerServer, strategy api.DeschedulerStrategy, policyGroupVersion string, nodes []*v1.Node, nodePodCount nodePodEvictedCount) {
|
||||
if !strategy.Enabled {
|
||||
return
|
||||
}
|
||||
removePodsWithAffinityRules(ds.Client, policyGroupVersion, nodes, ds.DryRun, nodePodCount, ds.MaxNoOfPodsToEvictPerNode)
|
||||
}
|
||||
|
||||
// removePodsWithAffinityRules evicts pods on the node which are having a pod affinity rules.
|
||||
func removePodsWithAffinityRules(client clientset.Interface, policyGroupVersion string, nodes []*v1.Node, dryRun bool, nodePodCount nodePodEvictedCount, maxPodsToEvict int) int {
|
||||
podsEvicted := 0
|
||||
for _, node := range nodes {
|
||||
glog.V(1).Infof("Processing node: %#v\n", node.Name)
|
||||
pods, err := podutil.ListEvictablePodsOnNode(client, node)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
totalPods := len(pods)
|
||||
for i := 0; i < totalPods; i++ {
|
||||
if maxPodsToEvict > 0 && nodePodCount[node]+1 > maxPodsToEvict {
|
||||
break
|
||||
}
|
||||
if checkPodsWithAntiAffinityExist(pods[i], pods) {
|
||||
success, err := evictions.EvictPod(client, pods[i], policyGroupVersion, dryRun)
|
||||
if !success {
|
||||
glog.Infof("Error when evicting pod: %#v (%#v)\n", pods[i].Name, err)
|
||||
} else {
|
||||
nodePodCount[node]++
|
||||
glog.V(1).Infof("Evicted pod: %#v (%#v)\n because of existing anti-affinity", pods[i].Name, err)
|
||||
// Since the current pod is evicted all other pods which have anti-affinity with this
|
||||
// pod need not be evicted.
|
||||
// Update pods.
|
||||
pods = append(pods[:i], pods[i+1:]...)
|
||||
i--
|
||||
totalPods--
|
||||
}
|
||||
}
|
||||
}
|
||||
podsEvicted += nodePodCount[node]
|
||||
}
|
||||
return podsEvicted
|
||||
}
|
||||
|
||||
// checkPodsWithAntiAffinityExist checks if there are other pods on the node that the current pod cannot tolerate.
|
||||
func checkPodsWithAntiAffinityExist(pod *v1.Pod, pods []*v1.Pod) bool {
|
||||
affinity := pod.Spec.Affinity
|
||||
if affinity != nil && affinity.PodAntiAffinity != nil {
|
||||
for _, term := range getPodAntiAffinityTerms(affinity.PodAntiAffinity) {
|
||||
namespaces := priorityutil.GetNamespacesFromPodAffinityTerm(pod, &term)
|
||||
selector, err := metav1.LabelSelectorAsSelector(term.LabelSelector)
|
||||
if err != nil {
|
||||
glog.Infof("%v", err)
|
||||
return false
|
||||
}
|
||||
for _, existingPod := range pods {
|
||||
if existingPod.Name != pod.Name && priorityutil.PodMatchesTermsNamespaceAndSelector(existingPod, namespaces, selector) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getPodAntiAffinityTerms gets the antiaffinity terms for the given pod.
|
||||
func getPodAntiAffinityTerms(podAntiAffinity *v1.PodAntiAffinity) (terms []v1.PodAffinityTerm) {
|
||||
if podAntiAffinity != nil {
|
||||
if len(podAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution) != 0 {
|
||||
terms = podAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution
|
||||
}
|
||||
}
|
||||
return terms
|
||||
}
|
||||
89
pkg/descheduler/strategies/pod_antiaffinity_test.go
Normal file
89
pkg/descheduler/strategies/pod_antiaffinity_test.go
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package strategies
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/descheduler/test"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
core "k8s.io/client-go/testing"
|
||||
)
|
||||
|
||||
func TestPodAntiAffinity(t *testing.T) {
|
||||
node := test.BuildTestNode("n1", 2000, 3000, 10)
|
||||
p1 := test.BuildTestPod("p1", 100, 0, node.Name)
|
||||
p2 := test.BuildTestPod("p2", 100, 0, node.Name)
|
||||
p3 := test.BuildTestPod("p3", 100, 0, node.Name)
|
||||
p4 := test.BuildTestPod("p4", 100, 0, node.Name)
|
||||
p2.Labels = map[string]string{"foo": "bar"}
|
||||
p1.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
p2.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
p3.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
p4.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
|
||||
// set pod anti affinity
|
||||
setPodAntiAffinity(p1)
|
||||
setPodAntiAffinity(p3)
|
||||
setPodAntiAffinity(p4)
|
||||
|
||||
// create fake client
|
||||
fakeClient := &fake.Clientset{}
|
||||
fakeClient.Fake.AddReactor("list", "pods", func(action core.Action) (bool, runtime.Object, error) {
|
||||
return true, &v1.PodList{Items: []v1.Pod{*p1, *p2, *p3, *p4}}, nil
|
||||
})
|
||||
fakeClient.Fake.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) {
|
||||
return true, node, nil
|
||||
})
|
||||
npe := nodePodEvictedCount{}
|
||||
npe[node] = 0
|
||||
expectedEvictedPodCount := 3
|
||||
podsEvicted := removePodsWithAffinityRules(fakeClient, "v1", []*v1.Node{node}, false, npe, 0)
|
||||
if podsEvicted != expectedEvictedPodCount {
|
||||
t.Errorf("Unexpected no of pods evicted: pods evicted: %d, expected: %d", podsEvicted, expectedEvictedPodCount)
|
||||
}
|
||||
npe[node] = 0
|
||||
expectedEvictedPodCount = 1
|
||||
podsEvicted = removePodsWithAffinityRules(fakeClient, "v1", []*v1.Node{node}, false, npe, 1)
|
||||
if podsEvicted != expectedEvictedPodCount {
|
||||
t.Errorf("Unexpected no of pods evicted: pods evicted: %d, expected: %d", podsEvicted, expectedEvictedPodCount)
|
||||
}
|
||||
}
|
||||
|
||||
func setPodAntiAffinity(inputPod *v1.Pod) {
|
||||
inputPod.Spec.Affinity = &v1.Affinity{
|
||||
PodAntiAffinity: &v1.PodAntiAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
|
||||
{
|
||||
LabelSelector: &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "foo",
|
||||
Operator: metav1.LabelSelectorOpIn,
|
||||
Values: []string{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
TopologyKey: "region",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
37
pkg/descheduler/strategies/util.go
Normal file
37
pkg/descheduler/strategies/util.go
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package strategies
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// This file contains the datastructures, types & functions needed by all the strategies so that we don't have
|
||||
// to compute them again in each strategy.
|
||||
|
||||
// nodePodEvictedCount keeps count of pods evicted on node. This is used in conjunction with strategies to
|
||||
type nodePodEvictedCount map[*v1.Node]int
|
||||
|
||||
// InitializeNodePodCount initializes the nodePodCount.
|
||||
func InitializeNodePodCount(nodeList []*v1.Node) nodePodEvictedCount {
|
||||
var nodePodCount = make(nodePodEvictedCount)
|
||||
for _, node := range nodeList {
|
||||
// Initialize podsEvicted till now with 0.
|
||||
nodePodCount[node] = 0
|
||||
}
|
||||
return nodePodCount
|
||||
}
|
||||
94
pkg/utils/predicates.go
Normal file
94
pkg/utils/predicates.go
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||
)
|
||||
|
||||
// The following code has been copied from predicates package to avoid the
|
||||
// huge vendoring issues, mostly copied from
|
||||
// k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates/
|
||||
// Some minor changes have been made to ease the imports, but most of the code
|
||||
// remains untouched
|
||||
|
||||
// PodMatchNodeSelector checks if a pod node selector matches the node label.
|
||||
func PodMatchNodeSelector(pod *v1.Pod, node *v1.Node) (bool, error) {
|
||||
if node == nil {
|
||||
return false, fmt.Errorf("node not found")
|
||||
}
|
||||
if podMatchesNodeLabels(pod, node) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// The pod can only schedule onto nodes that satisfy requirements in both NodeAffinity and nodeSelector.
|
||||
func podMatchesNodeLabels(pod *v1.Pod, node *v1.Node) bool {
|
||||
// Check if node.Labels match pod.Spec.NodeSelector.
|
||||
if len(pod.Spec.NodeSelector) > 0 {
|
||||
selector := labels.SelectorFromSet(pod.Spec.NodeSelector)
|
||||
if !selector.Matches(labels.Set(node.Labels)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 1. nil NodeSelector matches all nodes (i.e. does not filter out any nodes)
|
||||
// 2. nil []NodeSelectorTerm (equivalent to non-nil empty NodeSelector) matches no nodes
|
||||
// 3. zero-length non-nil []NodeSelectorTerm matches no nodes also, just for simplicity
|
||||
// 4. nil []NodeSelectorRequirement (equivalent to non-nil empty NodeSelectorTerm) matches no nodes
|
||||
// 5. zero-length non-nil []NodeSelectorRequirement matches no nodes also, just for simplicity
|
||||
// 6. non-nil empty NodeSelectorRequirement is not allowed
|
||||
|
||||
affinity := pod.Spec.Affinity
|
||||
if affinity != nil && affinity.NodeAffinity != nil {
|
||||
nodeAffinity := affinity.NodeAffinity
|
||||
// if no required NodeAffinity requirements, will do no-op, means select all nodes.
|
||||
if nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// Match node selector for requiredDuringSchedulingIgnoredDuringExecution.
|
||||
if nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
||||
nodeSelectorTerms := nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms
|
||||
glog.V(10).Infof("Match for RequiredDuringSchedulingIgnoredDuringExecution node selector terms %+v", nodeSelectorTerms)
|
||||
return nodeMatchesNodeSelectorTerms(node, nodeSelectorTerms)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// nodeMatchesNodeSelectorTerms checks if a node's labels satisfy a list of node selector terms,
|
||||
// terms are ORed, and an empty list of terms will match nothing.
|
||||
func nodeMatchesNodeSelectorTerms(node *v1.Node, nodeSelectorTerms []v1.NodeSelectorTerm) bool {
|
||||
for _, req := range nodeSelectorTerms {
|
||||
nodeSelector, err := v1helper.NodeSelectorRequirementsAsSelector(req.MatchExpressions)
|
||||
if err != nil {
|
||||
glog.V(10).Infof("Failed to parse MatchExpressions: %+v, regarding as not match.", req.MatchExpressions)
|
||||
return false
|
||||
}
|
||||
if nodeSelector.Matches(labels.Set(node.Labels)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
156
test/e2e/e2e_test.go
Normal file
156
test/e2e/e2e_test.go
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kubernetes-incubator/descheduler/cmd/descheduler/app/options"
|
||||
deschedulerapi "github.com/kubernetes-incubator/descheduler/pkg/api"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/descheduler/client"
|
||||
eutils "github.com/kubernetes-incubator/descheduler/pkg/descheduler/evictions/utils"
|
||||
nodeutil "github.com/kubernetes-incubator/descheduler/pkg/descheduler/node"
|
||||
podutil "github.com/kubernetes-incubator/descheduler/pkg/descheduler/pod"
|
||||
"github.com/kubernetes-incubator/descheduler/pkg/descheduler/strategies"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
)
|
||||
|
||||
func MakePodSpec() v1.PodSpec {
|
||||
return v1.PodSpec{
|
||||
Containers: []v1.Container{{
|
||||
Name: "pause",
|
||||
Image: "kubernetes/pause",
|
||||
Ports: []v1.ContainerPort{{ContainerPort: 80}},
|
||||
Resources: v1.ResourceRequirements{
|
||||
Limits: v1.ResourceList{
|
||||
v1.ResourceCPU: resource.MustParse("100m"),
|
||||
v1.ResourceMemory: resource.MustParse("500Mi"),
|
||||
},
|
||||
Requests: v1.ResourceList{
|
||||
v1.ResourceCPU: resource.MustParse("100m"),
|
||||
v1.ResourceMemory: resource.MustParse("500Mi"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
// RcByNameContainer returns a ReplicationControoler with specified name and container
|
||||
func RcByNameContainer(name string, replicas int32, labels map[string]string, gracePeriod *int64) *v1.ReplicationController {
|
||||
|
||||
zeroGracePeriod := int64(0)
|
||||
|
||||
// Add "name": name to the labels, overwriting if it exists.
|
||||
labels["name"] = name
|
||||
if gracePeriod == nil {
|
||||
gracePeriod = &zeroGracePeriod
|
||||
}
|
||||
return &v1.ReplicationController{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ReplicationController",
|
||||
APIVersion: testapi.Groups[v1.GroupName].GroupVersion().String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: v1.ReplicationControllerSpec{
|
||||
Replicas: func(i int32) *int32 { return &i }(replicas),
|
||||
Selector: map[string]string{
|
||||
"name": name,
|
||||
},
|
||||
Template: &v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: MakePodSpec(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// startEndToEndForLowNodeUtilization tests the lownode utilization strategy.
|
||||
func startEndToEndForLowNodeUtilization(clientset clientset.Interface) {
|
||||
var thresholds = make(deschedulerapi.ResourceThresholds)
|
||||
var targetThresholds = make(deschedulerapi.ResourceThresholds)
|
||||
thresholds[v1.ResourceMemory] = 20
|
||||
thresholds[v1.ResourcePods] = 20
|
||||
thresholds[v1.ResourceCPU] = 85
|
||||
targetThresholds[v1.ResourceMemory] = 20
|
||||
targetThresholds[v1.ResourcePods] = 20
|
||||
targetThresholds[v1.ResourceCPU] = 90
|
||||
// Run descheduler.
|
||||
evictionPolicyGroupVersion, err := eutils.SupportEviction(clientset)
|
||||
if err != nil || len(evictionPolicyGroupVersion) == 0 {
|
||||
glog.Fatalf("%v", err)
|
||||
}
|
||||
stopChannel := make(chan struct{})
|
||||
nodes, err := nodeutil.ReadyNodes(clientset, "", stopChannel)
|
||||
if err != nil {
|
||||
glog.Fatalf("%v", err)
|
||||
}
|
||||
nodeUtilizationThresholds := deschedulerapi.NodeResourceUtilizationThresholds{Thresholds: thresholds, TargetThresholds: targetThresholds}
|
||||
nodeUtilizationStrategyParams := deschedulerapi.StrategyParameters{NodeResourceUtilizationThresholds: nodeUtilizationThresholds}
|
||||
lowNodeUtilizationStrategy := deschedulerapi.DeschedulerStrategy{Enabled: true, Params: nodeUtilizationStrategyParams}
|
||||
ds := &options.DeschedulerServer{Client: clientset}
|
||||
nodePodCount := strategies.InitializeNodePodCount(nodes)
|
||||
strategies.LowNodeUtilization(ds, lowNodeUtilizationStrategy, evictionPolicyGroupVersion, nodes, nodePodCount)
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func TestE2E(t *testing.T) {
|
||||
// If we have reached here, it means cluster would have been already setup and the kubeconfig file should
|
||||
// be in /tmp directory.
|
||||
clientSet, err := client.CreateClient("/tmp/admin.conf")
|
||||
if err != nil {
|
||||
t.Errorf("Error during client creation with %v", err)
|
||||
}
|
||||
nodeList, err := clientSet.Core().Nodes().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Error listing node with %v", err)
|
||||
}
|
||||
// Assumption: We would have 3 node cluster by now. Kubeadm brings all the master components onto master node.
|
||||
// So, the last node would have least utilization.
|
||||
leastLoadedNode := nodeList.Items[2]
|
||||
rc := RcByNameContainer("test-rc", int32(15), map[string]string{"test": "app"}, nil)
|
||||
_, err = clientSet.CoreV1().ReplicationControllers("default").Create(rc)
|
||||
if err != nil {
|
||||
t.Errorf("Error creating deployment %v", err)
|
||||
}
|
||||
podsOnleastUtilizedNode, err := podutil.ListPodsOnANode(clientSet, &leastLoadedNode)
|
||||
if err != nil {
|
||||
t.Errorf("Error listing pods on a node %v", err)
|
||||
}
|
||||
podsBefore := len(podsOnleastUtilizedNode)
|
||||
t.Log("Eviction of pods starting")
|
||||
startEndToEndForLowNodeUtilization(clientSet)
|
||||
podsOnleastUtilizedNode, err = podutil.ListPodsOnANode(clientSet, &leastLoadedNode)
|
||||
if err != nil {
|
||||
t.Errorf("Error listing pods on a node %v", err)
|
||||
}
|
||||
podsAfter := len(podsOnleastUtilizedNode)
|
||||
if podsBefore > podsAfter {
|
||||
t.Fatalf("We should have see more pods on this node as per kubeadm's way of installing %v, %v", podsBefore, podsAfter)
|
||||
}
|
||||
}
|
||||
20
test/run-e2e-tests.sh
Executable file
20
test/run-e2e-tests.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
# Copyright 2017 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
# This just run e2e tests.
|
||||
PRJ_PREFIX="github.com/${REPO_ORG:-kubernetes-incubator}/descheduler"
|
||||
go test ${PRJ_PREFIX}/test/e2e/ -v
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
# run unit tests
|
||||
go test $(go list github.com/kubernetes-incubator/descheduler/... | grep -v github.com/kubernetes-incubator/descheduler/vendor/)
|
||||
# This just run unit-tests. Ignoring the current directory so as to avoid running e2e tests.
|
||||
PRJ_PREFIX="github.com/${REPO_ORG:-kubernetes-incubator}/descheduler"
|
||||
go test $(go list ${PRJ_PREFIX}/... | grep -v ${PRJ_PREFIX}/vendor/| grep -v ${PRJ_PREFIX}/test/)
|
||||
|
||||
|
||||
@@ -16,16 +16,15 @@ limitations under the License.
|
||||
|
||||
package test
|
||||
|
||||
import ("fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
// TODO:@ravisantoshgudimetla. As of now building some test pods here. This needs to
|
||||
// move to utils after refactor.
|
||||
// buildTestPod creates a test pod with given parameters.
|
||||
// BuildTestPod creates a test pod with given parameters.
|
||||
func BuildTestPod(name string, cpu int64, memory int64, nodeName string) *v1.Pod {
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -38,6 +37,7 @@ func BuildTestPod(name string, cpu int64, memory int64, nodeName string) *v1.Pod
|
||||
{
|
||||
Resources: v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{},
|
||||
Limits: v1.ResourceList{},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -63,25 +63,25 @@ func GetMirrorPodAnnotation() map[string]string {
|
||||
}
|
||||
}
|
||||
|
||||
// GetNormalPodAnnotation returns the annotation needed for a pod.
|
||||
func GetNormalPodAnnotation() map[string]string {
|
||||
return map[string]string{
|
||||
"kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"Pod\"}}",
|
||||
}
|
||||
// GetNormalPodOwnerRefList returns the ownerRef needed for a pod.
|
||||
func GetNormalPodOwnerRefList() []metav1.OwnerReference {
|
||||
ownerRefList := make([]metav1.OwnerReference, 0)
|
||||
ownerRefList = append(ownerRefList, metav1.OwnerReference{Kind: "Pod", APIVersion: "v1"})
|
||||
return ownerRefList
|
||||
}
|
||||
|
||||
// GetReplicaSetAnnotation returns the annotation needed for replicaset pod.
|
||||
func GetReplicaSetAnnotation() map[string]string {
|
||||
return map[string]string{
|
||||
"kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicaSet\"}}",
|
||||
}
|
||||
// GetReplicaSetOwnerRefList returns the ownerRef needed for replicaset pod.
|
||||
func GetReplicaSetOwnerRefList() []metav1.OwnerReference {
|
||||
ownerRefList := make([]metav1.OwnerReference, 0)
|
||||
ownerRefList = append(ownerRefList, metav1.OwnerReference{Kind: "ReplicaSet", APIVersion: "v1"})
|
||||
return ownerRefList
|
||||
}
|
||||
|
||||
// GetDaemonSetAnnotation returns the annotation needed for daemonset pod.
|
||||
func GetDaemonSetAnnotation() map[string]string {
|
||||
return map[string]string{
|
||||
"kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"DaemonSet\"}}",
|
||||
}
|
||||
// GetDaemonSetOwnerRefList returns the ownerRef needed for daemonset pod.
|
||||
func GetDaemonSetOwnerRefList() []metav1.OwnerReference {
|
||||
ownerRefList := make([]metav1.OwnerReference, 0)
|
||||
ownerRefList = append(ownerRefList, metav1.OwnerReference{Kind: "DaemonSet", APIVersion: "v1"})
|
||||
return ownerRefList
|
||||
}
|
||||
|
||||
// GetCriticalPodAnnotation returns the annotation needed for critical pod.
|
||||
@@ -92,7 +92,6 @@ func GetCriticalPodAnnotation() map[string]string {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// BuildTestNode creates a node with specified capacity.
|
||||
func BuildTestNode(name string, millicpu int64, mem int64, pods int64) *v1.Node {
|
||||
node := &v1.Node{
|
||||
|
||||
11
vendor/cloud.google.com/go/.travis.yml
generated
vendored
Normal file
11
vendor/cloud.google.com/go/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.6
|
||||
- 1.7
|
||||
install:
|
||||
- go get -v cloud.google.com/go/...
|
||||
script:
|
||||
- openssl aes-256-cbc -K $encrypted_912ff8fa81ad_key -iv $encrypted_912ff8fa81ad_iv -in key.json.enc -out key.json -d
|
||||
- GCLOUD_TESTS_GOLANG_PROJECT_ID="dulcet-port-762" GCLOUD_TESTS_GOLANG_KEY="$(pwd)/key.json"
|
||||
go test -race -v cloud.google.com/go/...
|
||||
15
vendor/cloud.google.com/go/AUTHORS
generated
vendored
Normal file
15
vendor/cloud.google.com/go/AUTHORS
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# This is the official list of cloud authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
# Names should be added to this file as:
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
Filippo Valsorda <hi@filippo.io>
|
||||
Google Inc.
|
||||
Ingo Oeser <nightlyone@googlemail.com>
|
||||
Palm Stone Games, Inc.
|
||||
Paweł Knap <pawelknap88@gmail.com>
|
||||
Péter Szilágyi <peterke@gmail.com>
|
||||
Tyler Treat <ttreat31@gmail.com>
|
||||
126
vendor/cloud.google.com/go/CONTRIBUTING.md
generated
vendored
Normal file
126
vendor/cloud.google.com/go/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
# Contributing
|
||||
|
||||
1. Sign one of the contributor license agreements below.
|
||||
1. `go get golang.org/x/review/git-codereview` to install the code reviewing tool.
|
||||
1. Get the cloud package by running `go get -d cloud.google.com/go`.
|
||||
1. If you have already checked out the source, make sure that the remote git
|
||||
origin is https://code.googlesource.com/gocloud:
|
||||
|
||||
git remote set-url origin https://code.googlesource.com/gocloud
|
||||
1. Make sure your auth is configured correctly by visiting
|
||||
https://code.googlesource.com, clicking "Generate Password", and following
|
||||
the directions.
|
||||
1. Make changes and create a change by running `git codereview change <name>`,
|
||||
provide a commit message, and use `git codereview mail` to create a Gerrit CL.
|
||||
1. Keep amending to the change and mail as your receive feedback.
|
||||
|
||||
## Integration Tests
|
||||
|
||||
In addition to the unit tests, you may run the integration test suite.
|
||||
|
||||
To run the integrations tests, creating and configuration of a project in the
|
||||
Google Developers Console is required.
|
||||
|
||||
After creating a project, you must [create a service account](https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount).
|
||||
Ensure the project-level **Owner** [IAM role](console.cloud.google.com/iam-admin/iam/project)
|
||||
(or **Editor** and **Logs Configuration Writer** roles) are added to the
|
||||
service account.
|
||||
|
||||
Once you create a project, set the following environment variables to be able to
|
||||
run the against the actual APIs.
|
||||
|
||||
- **GCLOUD_TESTS_GOLANG_PROJECT_ID**: Developers Console project's ID (e.g. bamboo-shift-455)
|
||||
- **GCLOUD_TESTS_GOLANG_KEY**: The path to the JSON key file.
|
||||
|
||||
Install the [gcloud command-line tool][gcloudcli] to your machine and use it
|
||||
to create the indexes used in the datastore integration tests with indexes
|
||||
found in `datastore/testdata/index.yaml`:
|
||||
|
||||
From the project's root directory:
|
||||
|
||||
``` sh
|
||||
# Set the default project in your env
|
||||
$ gcloud config set project $GCLOUD_TESTS_GOLANG_PROJECT_ID
|
||||
|
||||
# Authenticate the gcloud tool with your account
|
||||
$ gcloud auth login
|
||||
|
||||
# Create the indexes
|
||||
$ gcloud preview datastore create-indexes datastore/testdata/index.yaml
|
||||
```
|
||||
|
||||
The Sink integration tests in preview/logging require a Google Cloud storage
|
||||
bucket with the same name as your test project, and with the Stackdriver Logging
|
||||
service account as owner:
|
||||
``` sh
|
||||
$ gsutil mb gs://$GCLOUD_TESTS_GOLANG_PROJECT_ID
|
||||
$ gsutil acl ch -g cloud-logs@google.com:O gs://$GCLOUD_TESTS_GOLANG_PROJECT_ID
|
||||
```
|
||||
|
||||
Once you've set the environment variables, you can run the integration tests by
|
||||
running:
|
||||
|
||||
``` sh
|
||||
$ go test -v cloud.google.com/go/...
|
||||
```
|
||||
|
||||
## Contributor License Agreements
|
||||
|
||||
Before we can accept your pull requests you'll need to sign a Contributor
|
||||
License Agreement (CLA):
|
||||
|
||||
- **If you are an individual writing original source code** and **you own the
|
||||
- intellectual property**, then you'll need to sign an [individual CLA][indvcla].
|
||||
- **If you work for a company that wants to allow you to contribute your work**,
|
||||
then you'll need to sign a [corporate CLA][corpcla].
|
||||
|
||||
You can sign these electronically (just scroll to the bottom). After that,
|
||||
we'll be able to accept your pull requests.
|
||||
|
||||
## Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project,
|
||||
and in the interest of fostering an open and welcoming community,
|
||||
we pledge to respect all people who contribute through reporting issues,
|
||||
posting feature requests, updating documentation,
|
||||
submitting pull requests or patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project
|
||||
a harassment-free experience for everyone,
|
||||
regardless of level of experience, gender, gender identity and expression,
|
||||
sexual orientation, disability, personal appearance,
|
||||
body size, race, ethnicity, age, religion, or nationality.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery
|
||||
* Personal attacks
|
||||
* Trolling or insulting/derogatory comments
|
||||
* Public or private harassment
|
||||
* Publishing other's private information,
|
||||
such as physical or electronic
|
||||
addresses, without explicit permission
|
||||
* Other unethical or unprofessional conduct.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct.
|
||||
By adopting this Code of Conduct,
|
||||
project maintainers commit themselves to fairly and consistently
|
||||
applying these principles to every aspect of managing this project.
|
||||
Project maintainers who do not follow or enforce the Code of Conduct
|
||||
may be permanently removed from the project team.
|
||||
|
||||
This code of conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior
|
||||
may be reported by opening an issue
|
||||
or contacting one or more of the project maintainers.
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0,
|
||||
available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
|
||||
|
||||
[gcloudcli]: https://developers.google.com/cloud/sdk/gcloud/
|
||||
[indvcla]: https://developers.google.com/open-source/cla/individual
|
||||
[corpcla]: https://developers.google.com/open-source/cla/corporate
|
||||
34
vendor/cloud.google.com/go/CONTRIBUTORS
generated
vendored
Normal file
34
vendor/cloud.google.com/go/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# People who have agreed to one of the CLAs and can contribute patches.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# https://developers.google.com/open-source/cla/individual
|
||||
# https://developers.google.com/open-source/cla/corporate
|
||||
#
|
||||
# Names should be added to this file as:
|
||||
# Name <email address>
|
||||
|
||||
# Keep the list alphabetically sorted.
|
||||
|
||||
Andreas Litt <andreas.litt@gmail.com>
|
||||
Andrew Gerrand <adg@golang.org>
|
||||
Brad Fitzpatrick <bradfitz@golang.org>
|
||||
Burcu Dogan <jbd@google.com>
|
||||
Dave Day <djd@golang.org>
|
||||
David Sansome <me@davidsansome.com>
|
||||
David Symonds <dsymonds@golang.org>
|
||||
Filippo Valsorda <hi@filippo.io>
|
||||
Glenn Lewis <gmlewis@google.com>
|
||||
Ingo Oeser <nightlyone@googlemail.com>
|
||||
Johan Euphrosine <proppy@google.com>
|
||||
Jonathan Amsterdam <jba@google.com>
|
||||
Luna Duclos <luna.duclos@palmstonegames.com>
|
||||
Michael McGreevy <mcgreevy@golang.org>
|
||||
Omar Jarjur <ojarjur@google.com>
|
||||
Paweł Knap <pawelknap88@gmail.com>
|
||||
Péter Szilágyi <peterke@gmail.com>
|
||||
Sarah Adams <shadams@google.com>
|
||||
Toby Burress <kurin@google.com>
|
||||
Tuo Shan <shantuo@google.com>
|
||||
Tyler Treat <ttreat31@gmail.com>
|
||||
202
vendor/cloud.google.com/go/LICENSE
generated
vendored
Normal file
202
vendor/cloud.google.com/go/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2014 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
245
vendor/cloud.google.com/go/README.md
generated
vendored
Normal file
245
vendor/cloud.google.com/go/README.md
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
# Google Cloud for Go
|
||||
|
||||
[](https://travis-ci.org/GoogleCloudPlatform/google-cloud-go)
|
||||
[](https://godoc.org/cloud.google.com/go)
|
||||
|
||||
``` go
|
||||
import "cloud.google.com/go"
|
||||
```
|
||||
|
||||
Go packages for Google Cloud Platform services.
|
||||
|
||||
**NOTE:** These packages are under development, and may occasionally make
|
||||
backwards-incompatible changes.
|
||||
|
||||
**NOTE:** Github repo is a mirror of [https://code.googlesource.com/gocloud](https://code.googlesource.com/gocloud).
|
||||
|
||||
## News
|
||||
|
||||
_September 8, 2016_
|
||||
|
||||
* New clients for some of Google's Machine Learning APIs: Vision, Speech, and
|
||||
Natural Language.
|
||||
|
||||
* Preview version of a new [Stackdriver Logging][cloud-logging] client in
|
||||
[`cloud.google.com/go/preview/logging`](https://godoc.org/cloud.google.com/go/preview/logging).
|
||||
This client uses gRPC as its transport layer, and supports log reading, sinks
|
||||
and metrics. It will replace the current client at `cloud.google.com/go/logging` shortly.
|
||||
|
||||
## Supported APIs
|
||||
|
||||
Google API | Status | Package
|
||||
-------------------------------|--------------|-----------------------------------------------------------
|
||||
[Datastore][cloud-datastore] | beta | [`cloud.google.com/go/datastore`][cloud-datastore-ref]
|
||||
[Storage][cloud-storage] | beta | [`cloud.google.com/go/storage`][cloud-storage-ref]
|
||||
[Pub/Sub][cloud-pubsub] | experimental | [`cloud.google.com/go/pubsub`][cloud-pubsub-ref]
|
||||
[Bigtable][cloud-bigtable] | beta | [`cloud.google.com/go/bigtable`][cloud-bigtable-ref]
|
||||
[BigQuery][cloud-bigquery] | experimental | [`cloud.google.com/go/bigquery`][cloud-bigquery-ref]
|
||||
[Logging][cloud-logging] | experimental | [`cloud.google.com/go/logging`][cloud-logging-ref]
|
||||
[Vision][cloud-vision] | experimental | [`cloud.google.com/go/vision`][cloud-vision-ref]
|
||||
[Language][cloud-language] | experimental | [`cloud.google.com/go/language/apiv1beta1`][cloud-language-ref]
|
||||
[Speech][cloud-speech] | experimental | [`cloud.google.com/go/speech/apiv1beta`][cloud-speech-ref]
|
||||
|
||||
|
||||
> **Experimental status**: the API is still being actively developed. As a
|
||||
> result, it might change in backward-incompatible ways and is not recommended
|
||||
> for production use.
|
||||
>
|
||||
> **Beta status**: the API is largely complete, but still has outstanding
|
||||
> features and bugs to be addressed. There may be minor backwards-incompatible
|
||||
> changes where necessary.
|
||||
>
|
||||
> **Stable status**: the API is mature and ready for production use. We will
|
||||
> continue addressing bugs and feature requests.
|
||||
|
||||
Documentation and examples are available at
|
||||
https://godoc.org/cloud.google.com/go
|
||||
|
||||
Visit or join the
|
||||
[google-api-go-announce group](https://groups.google.com/forum/#!forum/google-api-go-announce)
|
||||
for updates on these packages.
|
||||
|
||||
## Go Versions Supported
|
||||
|
||||
We support the two most recent major versions of Go. If Google App Engine uses
|
||||
an older version, we support that as well. You can see which versions are
|
||||
currently supported by looking at the lines following `go:` in
|
||||
[`.travis.yml`](.travis.yml).
|
||||
|
||||
## Authorization
|
||||
|
||||
By default, each API will use [Google Application Default Credentials][default-creds]
|
||||
for authorization credentials used in calling the API endpoints. This will allow your
|
||||
application to run in many environments without requiring explicit configuration.
|
||||
|
||||
Manually-configured authorization can be achieved using the
|
||||
[`golang.org/x/oauth2`](https://godoc.org/golang.org/x/oauth2) package to
|
||||
create an `oauth2.TokenSource`. This token source can be passed to the `NewClient`
|
||||
function for the relevant API using a
|
||||
[`option.WithTokenSource`](https://godoc.org/google.golang.org/api/option#WithTokenSource)
|
||||
option.
|
||||
|
||||
## Google Cloud Datastore [](https://godoc.org/cloud.google.com/go/datastore)
|
||||
|
||||
[Google Cloud Datastore][cloud-datastore] ([docs][cloud-datastore-docs]) is a fully-
|
||||
managed, schemaless database for storing non-relational data. Cloud Datastore
|
||||
automatically scales with your users and supports ACID transactions, high availability
|
||||
of reads and writes, strong consistency for reads and ancestor queries, and eventual
|
||||
consistency for all other queries.
|
||||
|
||||
Follow the [activation instructions][cloud-datastore-activation] to use the Google
|
||||
Cloud Datastore API with your project.
|
||||
|
||||
First create a `datastore.Client` to use throughout your application:
|
||||
|
||||
```go
|
||||
client, err := datastore.NewClient(ctx, "my-project-id")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
```
|
||||
|
||||
Then use that client to interact with the API:
|
||||
|
||||
```go
|
||||
type Post struct {
|
||||
Title string
|
||||
Body string `datastore:",noindex"`
|
||||
PublishedAt time.Time
|
||||
}
|
||||
keys := []*datastore.Key{
|
||||
datastore.NewKey(ctx, "Post", "post1", 0, nil),
|
||||
datastore.NewKey(ctx, "Post", "post2", 0, nil),
|
||||
}
|
||||
posts := []*Post{
|
||||
{Title: "Post 1", Body: "...", PublishedAt: time.Now()},
|
||||
{Title: "Post 2", Body: "...", PublishedAt: time.Now()},
|
||||
}
|
||||
if _, err := client.PutMulti(ctx, keys, posts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
## Google Cloud Storage [](https://godoc.org/cloud.google.com/go/storage)
|
||||
|
||||
[Google Cloud Storage][cloud-storage] ([docs][cloud-storage-docs]) allows you to store
|
||||
data on Google infrastructure with very high reliability, performance and availability,
|
||||
and can be used to distribute large data objects to users via direct download.
|
||||
|
||||
https://godoc.org/cloud.google.com/go/storage
|
||||
|
||||
First create a `storage.Client` to use throughout your application:
|
||||
|
||||
```go
|
||||
client, err := storage.NewClient(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// Read the object1 from bucket.
|
||||
rc, err := client.Bucket("bucket").Object("object1").NewReader(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rc.Close()
|
||||
body, err := ioutil.ReadAll(rc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
## Google Cloud Pub/Sub [](https://godoc.org/cloud.google.com/go/pubsub)
|
||||
|
||||
[Google Cloud Pub/Sub][cloud-pubsub] ([docs][cloud-pubsub-docs]) allows you to connect
|
||||
your services with reliable, many-to-many, asynchronous messaging hosted on Google's
|
||||
infrastructure. Cloud Pub/Sub automatically scales as you need it and provides a foundation
|
||||
for building your own robust, global services.
|
||||
|
||||
First create a `pubsub.Client` to use throughout your application:
|
||||
|
||||
```go
|
||||
client, err := pubsub.NewClient(ctx, "project-id")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// Publish "hello world" on topic1.
|
||||
topic := client.Topic("topic1")
|
||||
msgIDs, err := topic.Publish(ctx, &pubsub.Message{
|
||||
Data: []byte("hello world"),
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Create an iterator to pull messages via subscription1.
|
||||
it, err := client.Subscription("subscription1").Pull(ctx)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
defer it.Stop()
|
||||
|
||||
// Consume N messages from the iterator.
|
||||
for i := 0; i < N; i++ {
|
||||
msg, err := it.Next()
|
||||
if err == pubsub.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to retrieve message: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Message %d: %s\n", i, msg.Data)
|
||||
msg.Done(true) // Acknowledge that we've consumed the message.
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome. Please, see the
|
||||
[CONTRIBUTING](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/CONTRIBUTING.md)
|
||||
document for details. We're using Gerrit for our code reviews. Please don't open pull
|
||||
requests against this repo, new pull requests will be automatically closed.
|
||||
|
||||
Please note that this project is released with a Contributor Code of Conduct.
|
||||
By participating in this project you agree to abide by its terms.
|
||||
See [Contributor Code of Conduct](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/CONTRIBUTING.md#contributor-code-of-conduct)
|
||||
for more information.
|
||||
|
||||
[cloud-datastore]: https://cloud.google.com/datastore/
|
||||
[cloud-datastore-ref]: https://godoc.org/cloud.google.com/go/datastore
|
||||
[cloud-datastore-docs]: https://cloud.google.com/datastore/docs
|
||||
[cloud-datastore-activation]: https://cloud.google.com/datastore/docs/activate
|
||||
|
||||
[cloud-pubsub]: https://cloud.google.com/pubsub/
|
||||
[cloud-pubsub-ref]: https://godoc.org/cloud.google.com/go/pubsub
|
||||
[cloud-pubsub-docs]: https://cloud.google.com/pubsub/docs
|
||||
|
||||
[cloud-storage]: https://cloud.google.com/storage/
|
||||
[cloud-storage-ref]: https://godoc.org/cloud.google.com/go/storage
|
||||
[cloud-storage-docs]: https://cloud.google.com/storage/docs/overview
|
||||
[cloud-storage-create-bucket]: https://cloud.google.com/storage/docs/cloud-console#_creatingbuckets
|
||||
|
||||
[cloud-bigtable]: https://cloud.google.com/bigtable/
|
||||
[cloud-bigtable-ref]: https://godoc.org/cloud.google.com/go/bigtable
|
||||
|
||||
[cloud-bigquery]: https://cloud.google.com/bigquery/
|
||||
[cloud-bigquery-ref]: https://godoc.org/cloud.google.com/go/bigquery
|
||||
|
||||
[cloud-logging]: https://cloud.google.com/logging/
|
||||
[cloud-logging-ref]: https://godoc.org/cloud.google.com/go/logging
|
||||
|
||||
[cloud-vision]: https://cloud.google.com/vision/
|
||||
[cloud-vision-ref]: https://godoc.org/cloud.google.com/go/vision
|
||||
|
||||
[cloud-language]: https://cloud.google.com/natural-language
|
||||
[cloud-language-ref]: https://godoc.org/cloud.google.com/go/language/apiv1beta1
|
||||
|
||||
[cloud-speech]: https://cloud.google.com/speech
|
||||
[cloud-speech-ref]: https://godoc.org/cloud.google.com/go/speech/apiv1beta1
|
||||
|
||||
[default-creds]: https://developers.google.com/identity/protocols/application-default-credentials
|
||||
26
vendor/cloud.google.com/go/appveyor.yml
generated
vendored
Normal file
26
vendor/cloud.google.com/go/appveyor.yml
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# This file configures AppVeyor (http://www.appveyor.com),
|
||||
# a Windows-based CI service similar to Travis.
|
||||
|
||||
# Identifier for this run
|
||||
version: "{build}"
|
||||
|
||||
# Clone the repo into this path, which conforms to the standard
|
||||
# Go workspace structure.
|
||||
clone_folder: c:\gopath\src\cloud.google.com\go
|
||||
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
|
||||
install:
|
||||
# Info for debugging.
|
||||
- echo %PATH%
|
||||
- go version
|
||||
- go env
|
||||
- go get -v -d -t ./...
|
||||
|
||||
# Provide a build script, or AppVeyor will call msbuild.
|
||||
build_script:
|
||||
- go install -v ./...
|
||||
|
||||
test_script:
|
||||
- go test -short -v ./...
|
||||
60
vendor/cloud.google.com/go/authexample_test.go
generated
vendored
Normal file
60
vendor/cloud.google.com/go/authexample_test.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 cloud_test
|
||||
|
||||
import (
|
||||
"cloud.google.com/go/datastore"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
||||
func Example_applicationDefaultCredentials() {
|
||||
ctx := context.Background()
|
||||
// Use Google Application Default Credentials to authorize and authenticate the client.
|
||||
// More information about Application Default Credentials and how to enable is at
|
||||
// https://developers.google.com/identity/protocols/application-default-credentials.
|
||||
//
|
||||
// This is the recommended way of authorizing and authenticating.
|
||||
//
|
||||
// Note: The example uses the datastore client, but the same steps apply to
|
||||
// the other client libraries underneath this package.
|
||||
client, err := datastore.NewClient(ctx, "project-id")
|
||||
if err != nil {
|
||||
// TODO: handle error.
|
||||
}
|
||||
// Use the client.
|
||||
_ = client
|
||||
}
|
||||
|
||||
func Example_serviceAccountFile() {
|
||||
// Warning: The better way to use service accounts is to set GOOGLE_APPLICATION_CREDENTIALS
|
||||
// and use the Application Default Credentials.
|
||||
ctx := context.Background()
|
||||
// Use a JSON key file associated with a Google service account to
|
||||
// authenticate and authorize.
|
||||
// Go to https://console.developers.google.com/permissions/serviceaccounts to create
|
||||
// and download a service account key for your project.
|
||||
//
|
||||
// Note: The example uses the datastore client, but the same steps apply to
|
||||
// the other client libraries underneath this package.
|
||||
client, err := datastore.NewClient(ctx,
|
||||
"project-id",
|
||||
option.WithServiceAccountFile("/path/to/service-account-key.json"))
|
||||
if err != nil {
|
||||
// TODO: handle error.
|
||||
}
|
||||
// Use the client.
|
||||
_ = client
|
||||
}
|
||||
175
vendor/cloud.google.com/go/bigquery/bigquery.go
generated
vendored
Normal file
175
vendor/cloud.google.com/go/bigquery/bigquery.go
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
// TODO(mcgreevy): support dry-run mode when creating jobs.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/api/transport"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
const prodAddr = "https://www.googleapis.com/bigquery/v2/"
|
||||
|
||||
// A Source is a source of data for the Copy function.
|
||||
type Source interface {
|
||||
implementsSource()
|
||||
}
|
||||
|
||||
// A Destination is a destination of data for the Copy function.
|
||||
type Destination interface {
|
||||
implementsDestination()
|
||||
}
|
||||
|
||||
// An Option is an optional argument to Copy.
|
||||
type Option interface {
|
||||
implementsOption()
|
||||
}
|
||||
|
||||
// A ReadSource is a source of data for the Read function.
|
||||
type ReadSource interface {
|
||||
implementsReadSource()
|
||||
}
|
||||
|
||||
// A ReadOption is an optional argument to Read.
|
||||
type ReadOption interface {
|
||||
customizeRead(conf *pagingConf)
|
||||
}
|
||||
|
||||
const Scope = "https://www.googleapis.com/auth/bigquery"
|
||||
const userAgent = "gcloud-golang-bigquery/20160429"
|
||||
|
||||
// Client may be used to perform BigQuery operations.
|
||||
type Client struct {
|
||||
service service
|
||||
projectID string
|
||||
}
|
||||
|
||||
// NewClient constructs a new Client which can perform BigQuery operations.
|
||||
// Operations performed via the client are billed to the specified GCP project.
|
||||
func NewClient(ctx context.Context, projectID string, opts ...option.ClientOption) (*Client, error) {
|
||||
o := []option.ClientOption{
|
||||
option.WithEndpoint(prodAddr),
|
||||
option.WithScopes(Scope),
|
||||
option.WithUserAgent(userAgent),
|
||||
}
|
||||
o = append(o, opts...)
|
||||
httpClient, endpoint, err := transport.NewHTTPClient(ctx, o...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dialing: %v", err)
|
||||
}
|
||||
|
||||
s, err := newBigqueryService(httpClient, endpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("constructing bigquery client: %v", err)
|
||||
}
|
||||
|
||||
c := &Client{
|
||||
service: s,
|
||||
projectID: projectID,
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// initJobProto creates and returns a bigquery Job proto.
|
||||
// The proto is customized using any jobOptions in options.
|
||||
// The list of Options is returned with the jobOptions removed.
|
||||
func initJobProto(projectID string, options []Option) (*bq.Job, []Option) {
|
||||
job := &bq.Job{}
|
||||
|
||||
var other []Option
|
||||
for _, opt := range options {
|
||||
if o, ok := opt.(jobOption); ok {
|
||||
o.customizeJob(job, projectID)
|
||||
} else {
|
||||
other = append(other, opt)
|
||||
}
|
||||
}
|
||||
return job, other
|
||||
}
|
||||
|
||||
// Copy starts a BigQuery operation to copy data from a Source to a Destination.
|
||||
func (c *Client) Copy(ctx context.Context, dst Destination, src Source, options ...Option) (*Job, error) {
|
||||
switch dst := dst.(type) {
|
||||
case *Table:
|
||||
switch src := src.(type) {
|
||||
case *GCSReference:
|
||||
return c.load(ctx, dst, src, options)
|
||||
case *Table:
|
||||
return c.cp(ctx, dst, Tables{src}, options)
|
||||
case Tables:
|
||||
return c.cp(ctx, dst, src, options)
|
||||
case *Query:
|
||||
return c.query(ctx, dst, src, options)
|
||||
}
|
||||
case *GCSReference:
|
||||
if src, ok := src.(*Table); ok {
|
||||
return c.extract(ctx, dst, src, options)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no Copy operation matches dst/src pair: dst: %T ; src: %T", dst, src)
|
||||
}
|
||||
|
||||
// Query creates a query with string q. You may optionally set
|
||||
// DefaultProjectID and DefaultDatasetID on the returned query before using it.
|
||||
func (c *Client) Query(q string) *Query {
|
||||
return &Query{Q: q, client: c}
|
||||
}
|
||||
|
||||
// Read submits a query for execution and returns the results via an Iterator.
|
||||
//
|
||||
// Read uses a temporary table to hold the results of the query job.
|
||||
//
|
||||
// For more control over how a query is performed, don't use this method but
|
||||
// instead pass the Query as a Source to Client.Copy, and call Read on the
|
||||
// resulting Job.
|
||||
func (q *Query) Read(ctx context.Context, options ...ReadOption) (*Iterator, error) {
|
||||
dest := &Table{}
|
||||
job, err := q.client.Copy(ctx, dest, q, WriteTruncate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return job.Read(ctx, options...)
|
||||
}
|
||||
|
||||
// executeQuery submits a query for execution and returns the results via an Iterator.
|
||||
func (c *Client) executeQuery(ctx context.Context, q *Query, options ...ReadOption) (*Iterator, error) {
|
||||
dest := &Table{}
|
||||
job, err := c.Copy(ctx, dest, q, WriteTruncate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Read(ctx, job, options...)
|
||||
}
|
||||
|
||||
// Dataset creates a handle to a BigQuery dataset in the client's project.
|
||||
func (c *Client) Dataset(id string) *Dataset {
|
||||
return c.DatasetInProject(c.projectID, id)
|
||||
}
|
||||
|
||||
// DatasetInProject creates a handle to a BigQuery dataset in the specified project.
|
||||
func (c *Client) DatasetInProject(projectID, datasetID string) *Dataset {
|
||||
return &Dataset{
|
||||
projectID: projectID,
|
||||
id: datasetID,
|
||||
service: c.service,
|
||||
}
|
||||
}
|
||||
47
vendor/cloud.google.com/go/bigquery/copy_op.go
generated
vendored
Normal file
47
vendor/cloud.google.com/go/bigquery/copy_op.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
type copyOption interface {
|
||||
customizeCopy(conf *bq.JobConfigurationTableCopy)
|
||||
}
|
||||
|
||||
func (c *Client) cp(ctx context.Context, dst *Table, src Tables, options []Option) (*Job, error) {
|
||||
job, options := initJobProto(c.projectID, options)
|
||||
payload := &bq.JobConfigurationTableCopy{}
|
||||
|
||||
dst.customizeCopyDst(payload)
|
||||
src.customizeCopySrc(payload)
|
||||
|
||||
for _, opt := range options {
|
||||
o, ok := opt.(copyOption)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("option (%#v) not applicable to dst/src pair: dst: %T ; src: %T", opt, dst, src)
|
||||
}
|
||||
o.customizeCopy(payload)
|
||||
}
|
||||
|
||||
job.Configuration = &bq.JobConfiguration{
|
||||
Copy: payload,
|
||||
}
|
||||
return c.service.insertJob(ctx, job, c.projectID)
|
||||
}
|
||||
104
vendor/cloud.google.com/go/bigquery/copy_test.go
generated
vendored
Normal file
104
vendor/cloud.google.com/go/bigquery/copy_test.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
func defaultCopyJob() *bq.Job {
|
||||
return &bq.Job{
|
||||
Configuration: &bq.JobConfiguration{
|
||||
Copy: &bq.JobConfigurationTableCopy{
|
||||
DestinationTable: &bq.TableReference{
|
||||
ProjectId: "d-project-id",
|
||||
DatasetId: "d-dataset-id",
|
||||
TableId: "d-table-id",
|
||||
},
|
||||
SourceTables: []*bq.TableReference{
|
||||
{
|
||||
ProjectId: "s-project-id",
|
||||
DatasetId: "s-dataset-id",
|
||||
TableId: "s-table-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopy(t *testing.T) {
|
||||
testCases := []struct {
|
||||
dst *Table
|
||||
src Tables
|
||||
options []Option
|
||||
want *bq.Job
|
||||
}{
|
||||
{
|
||||
dst: &Table{
|
||||
ProjectID: "d-project-id",
|
||||
DatasetID: "d-dataset-id",
|
||||
TableID: "d-table-id",
|
||||
},
|
||||
src: Tables{
|
||||
{
|
||||
ProjectID: "s-project-id",
|
||||
DatasetID: "s-dataset-id",
|
||||
TableID: "s-table-id",
|
||||
},
|
||||
},
|
||||
want: defaultCopyJob(),
|
||||
},
|
||||
{
|
||||
dst: &Table{
|
||||
ProjectID: "d-project-id",
|
||||
DatasetID: "d-dataset-id",
|
||||
TableID: "d-table-id",
|
||||
},
|
||||
src: Tables{
|
||||
{
|
||||
ProjectID: "s-project-id",
|
||||
DatasetID: "s-dataset-id",
|
||||
TableID: "s-table-id",
|
||||
},
|
||||
},
|
||||
options: []Option{CreateNever, WriteTruncate},
|
||||
want: func() *bq.Job {
|
||||
j := defaultCopyJob()
|
||||
j.Configuration.Copy.CreateDisposition = "CREATE_NEVER"
|
||||
j.Configuration.Copy.WriteDisposition = "WRITE_TRUNCATE"
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s := &testService{}
|
||||
c := &Client{
|
||||
service: s,
|
||||
}
|
||||
if _, err := c.Copy(context.Background(), tc.dst, tc.src, tc.options...); err != nil {
|
||||
t.Errorf("err calling cp: %v", err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(s.Job, tc.want) {
|
||||
t.Errorf("copying: got:\n%v\nwant:\n%v", s.Job, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
79
vendor/cloud.google.com/go/bigquery/create_table_test.go
generated
vendored
Normal file
79
vendor/cloud.google.com/go/bigquery/create_table_test.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
type createTableRecorder struct {
|
||||
conf *createTableConf
|
||||
service
|
||||
}
|
||||
|
||||
func (rec *createTableRecorder) createTable(ctx context.Context, conf *createTableConf) error {
|
||||
rec.conf = conf
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCreateTableOptions(t *testing.T) {
|
||||
s := &createTableRecorder{}
|
||||
c := &Client{
|
||||
projectID: "p",
|
||||
service: s,
|
||||
}
|
||||
ds := c.Dataset("d")
|
||||
table := ds.Table("t")
|
||||
exp := time.Now()
|
||||
q := "query"
|
||||
if err := table.Create(context.Background(), TableExpiration(exp), ViewQuery(q)); err != nil {
|
||||
t.Fatalf("err calling Table.Create: %v", err)
|
||||
}
|
||||
want := createTableConf{
|
||||
projectID: "p",
|
||||
datasetID: "d",
|
||||
tableID: "t",
|
||||
expiration: exp,
|
||||
viewQuery: q,
|
||||
}
|
||||
if !reflect.DeepEqual(*s.conf, want) {
|
||||
t.Errorf("createTableConf: got:\n%v\nwant:\n%v", *s.conf, want)
|
||||
}
|
||||
|
||||
sc := Schema{fieldSchema("desc", "name", "STRING", false, true)}
|
||||
if err := table.Create(context.Background(), TableExpiration(exp), sc); err != nil {
|
||||
t.Fatalf("err calling Table.Create: %v", err)
|
||||
}
|
||||
want = createTableConf{
|
||||
projectID: "p",
|
||||
datasetID: "d",
|
||||
tableID: "t",
|
||||
expiration: exp,
|
||||
// No need for an elaborate schema, that is tested in schema_test.go.
|
||||
schema: &bq.TableSchema{
|
||||
Fields: []*bq.TableFieldSchema{
|
||||
bqTableFieldSchema("desc", "name", "STRING", "REQUIRED"),
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(*s.conf, want) {
|
||||
t.Errorf("createTableConf: got:\n%v\nwant:\n%v", *s.conf, want)
|
||||
}
|
||||
}
|
||||
55
vendor/cloud.google.com/go/bigquery/dataset.go
generated
vendored
Normal file
55
vendor/cloud.google.com/go/bigquery/dataset.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import "golang.org/x/net/context"
|
||||
|
||||
// Dataset is a reference to a BigQuery dataset.
|
||||
type Dataset struct {
|
||||
projectID string
|
||||
id string
|
||||
service service
|
||||
}
|
||||
|
||||
// ListTables returns a list of all the tables contained in the Dataset.
|
||||
func (d *Dataset) ListTables(ctx context.Context) ([]*Table, error) {
|
||||
var tables []*Table
|
||||
|
||||
err := getPages("", func(pageToken string) (string, error) {
|
||||
ts, tok, err := d.service.listTables(ctx, d.projectID, d.id, pageToken)
|
||||
if err == nil {
|
||||
tables = append(tables, ts...)
|
||||
}
|
||||
return tok, err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// Create creates a dataset in the BigQuery service. An error will be returned
|
||||
// if the dataset already exists.
|
||||
func (d *Dataset) Create(ctx context.Context) error {
|
||||
return d.service.insertDataset(ctx, d.id, d.projectID)
|
||||
}
|
||||
|
||||
// Table creates a handle to a BigQuery table in the dataset.
|
||||
// To determine if a table exists, call Table.Metadata.
|
||||
// If the table does not already exist, use Table.Create to create it.
|
||||
func (d *Dataset) Table(tableID string) *Table {
|
||||
return &Table{ProjectID: d.projectID, DatasetID: d.id, TableID: tableID, service: d.service}
|
||||
}
|
||||
105
vendor/cloud.google.com/go/bigquery/dataset_test.go
generated
vendored
Normal file
105
vendor/cloud.google.com/go/bigquery/dataset_test.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// readServiceStub services read requests by returning data from an in-memory list of values.
|
||||
type listTablesServiceStub struct {
|
||||
expectedProject, expectedDataset string
|
||||
values [][]*Table // contains pages of tables.
|
||||
pageTokens map[string]string // maps incoming page token to returned page token.
|
||||
|
||||
service
|
||||
}
|
||||
|
||||
func (s *listTablesServiceStub) listTables(ctx context.Context, projectID, datasetID, pageToken string) ([]*Table, string, error) {
|
||||
if projectID != s.expectedProject {
|
||||
return nil, "", errors.New("wrong project id")
|
||||
}
|
||||
if datasetID != s.expectedDataset {
|
||||
return nil, "", errors.New("wrong dataset id")
|
||||
}
|
||||
|
||||
tables := s.values[0]
|
||||
s.values = s.values[1:]
|
||||
return tables, s.pageTokens[pageToken], nil
|
||||
}
|
||||
|
||||
func TestListTables(t *testing.T) {
|
||||
t1 := &Table{ProjectID: "p1", DatasetID: "d1", TableID: "t1"}
|
||||
t2 := &Table{ProjectID: "p1", DatasetID: "d1", TableID: "t2"}
|
||||
t3 := &Table{ProjectID: "p1", DatasetID: "d1", TableID: "t3"}
|
||||
testCases := []struct {
|
||||
data [][]*Table
|
||||
pageTokens map[string]string
|
||||
want []*Table
|
||||
}{
|
||||
{
|
||||
data: [][]*Table{{t1, t2}, {t3}},
|
||||
pageTokens: map[string]string{"": "a", "a": ""},
|
||||
want: []*Table{t1, t2, t3},
|
||||
},
|
||||
{
|
||||
data: [][]*Table{{t1, t2}, {t3}},
|
||||
pageTokens: map[string]string{"": ""}, // no more pages after first one.
|
||||
want: []*Table{t1, t2},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
c := &Client{
|
||||
service: &listTablesServiceStub{
|
||||
expectedProject: "x",
|
||||
expectedDataset: "y",
|
||||
values: tc.data,
|
||||
pageTokens: tc.pageTokens,
|
||||
},
|
||||
projectID: "x",
|
||||
}
|
||||
got, err := c.Dataset("y").ListTables(context.Background())
|
||||
if err != nil {
|
||||
t.Errorf("err calling ListTables: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("reading: got:\n%v\nwant:\n%v", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListTablesError(t *testing.T) {
|
||||
c := &Client{
|
||||
service: &listTablesServiceStub{
|
||||
expectedProject: "x",
|
||||
expectedDataset: "y",
|
||||
},
|
||||
projectID: "x",
|
||||
}
|
||||
// Test that service read errors are propagated back to the caller.
|
||||
// Passing "not y" as the dataset id will cause the service to return an error.
|
||||
_, err := c.Dataset("not y").ListTables(context.Background())
|
||||
if err == nil {
|
||||
// Read should not return an error; only Err should.
|
||||
t.Errorf("ListTables expected: non-nil err, got: nil")
|
||||
}
|
||||
}
|
||||
18
vendor/cloud.google.com/go/bigquery/doc.go
generated
vendored
Normal file
18
vendor/cloud.google.com/go/bigquery/doc.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery provides a client for the BigQuery service.
|
||||
//
|
||||
// Note: This package is a work-in-progress. Backwards-incompatible changes should be expected.
|
||||
package bigquery // import "cloud.google.com/go/bigquery"
|
||||
82
vendor/cloud.google.com/go/bigquery/error.go
generated
vendored
Normal file
82
vendor/cloud.google.com/go/bigquery/error.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
// An Error contains detailed information about a failed bigquery operation.
|
||||
type Error struct {
|
||||
// Mirrors bq.ErrorProto, but drops DebugInfo
|
||||
Location, Message, Reason string
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("{Location: %q; Message: %q; Reason: %q}", e.Location, e.Message, e.Reason)
|
||||
}
|
||||
|
||||
func errorFromErrorProto(ep *bq.ErrorProto) *Error {
|
||||
if ep == nil {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
Location: ep.Location,
|
||||
Message: ep.Message,
|
||||
Reason: ep.Reason,
|
||||
}
|
||||
}
|
||||
|
||||
// A MultiError contains multiple related errors.
|
||||
type MultiError []error
|
||||
|
||||
func (m MultiError) Error() string {
|
||||
switch len(m) {
|
||||
case 0:
|
||||
return "(0 errors)"
|
||||
case 1:
|
||||
return m[0].Error()
|
||||
case 2:
|
||||
return m[0].Error() + " (and 1 other error)"
|
||||
}
|
||||
return fmt.Sprintf("%s (and %d other errors)", m[0].Error(), len(m)-1)
|
||||
}
|
||||
|
||||
// RowInsertionError contains all errors that occurred when attempting to insert a row.
|
||||
type RowInsertionError struct {
|
||||
InsertID string // The InsertID associated with the affected row.
|
||||
RowIndex int // The 0-based index of the affected row in the batch of rows being inserted.
|
||||
Errors MultiError
|
||||
}
|
||||
|
||||
func (e *RowInsertionError) Error() string {
|
||||
errFmt := "insertion of row [insertID: %q; insertIndex: %v] failed with error: %s"
|
||||
return fmt.Sprintf(errFmt, e.InsertID, e.RowIndex, e.Errors.Error())
|
||||
}
|
||||
|
||||
// PutMultiError contains an error for each row which was not successfully inserted
|
||||
// into a BigQuery table.
|
||||
type PutMultiError []RowInsertionError
|
||||
|
||||
func (pme PutMultiError) Error() string {
|
||||
plural := "s"
|
||||
if len(pme) == 1 {
|
||||
plural = ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v row insertion%s failed", len(pme), plural)
|
||||
}
|
||||
109
vendor/cloud.google.com/go/bigquery/error_test.go
generated
vendored
Normal file
109
vendor/cloud.google.com/go/bigquery/error_test.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
func rowInsertionError(msg string) RowInsertionError {
|
||||
return RowInsertionError{Errors: []error{errors.New(msg)}}
|
||||
}
|
||||
|
||||
func TestPutMultiErrorString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
errs PutMultiError
|
||||
want string
|
||||
}{
|
||||
{
|
||||
errs: PutMultiError{},
|
||||
want: "0 row insertions failed",
|
||||
},
|
||||
{
|
||||
errs: PutMultiError{rowInsertionError("a")},
|
||||
want: "1 row insertion failed",
|
||||
},
|
||||
{
|
||||
errs: PutMultiError{rowInsertionError("a"), rowInsertionError("b")},
|
||||
want: "2 row insertions failed",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if tc.errs.Error() != tc.want {
|
||||
t.Errorf("PutMultiError string: got:\n%v\nwant:\n%v", tc.errs.Error(), tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiErrorString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
errs MultiError
|
||||
want string
|
||||
}{
|
||||
{
|
||||
errs: MultiError{},
|
||||
want: "(0 errors)",
|
||||
},
|
||||
{
|
||||
errs: MultiError{errors.New("a")},
|
||||
want: "a",
|
||||
},
|
||||
{
|
||||
errs: MultiError{errors.New("a"), errors.New("b")},
|
||||
want: "a (and 1 other error)",
|
||||
},
|
||||
{
|
||||
errs: MultiError{errors.New("a"), errors.New("b"), errors.New("c")},
|
||||
want: "a (and 2 other errors)",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if tc.errs.Error() != tc.want {
|
||||
t.Errorf("PutMultiError string: got:\n%v\nwant:\n%v", tc.errs.Error(), tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorFromErrorProto(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
in *bq.ErrorProto
|
||||
want *Error
|
||||
}{
|
||||
{nil, nil},
|
||||
{
|
||||
in: &bq.ErrorProto{Location: "L", Message: "M", Reason: "R"},
|
||||
want: &Error{Location: "L", Message: "M", Reason: "R"},
|
||||
},
|
||||
} {
|
||||
if got := errorFromErrorProto(test.in); !reflect.DeepEqual(got, test.want) {
|
||||
t.Errorf("%v: got %v, want %v", test.in, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorString(t *testing.T) {
|
||||
e := &Error{Location: "<L>", Message: "<M>", Reason: "<R>"}
|
||||
got := e.Error()
|
||||
if !strings.Contains(got, "<L>") || !strings.Contains(got, "<M>") || !strings.Contains(got, "<R>") {
|
||||
t.Errorf(`got %q, expected to see "<L>", "<M>" and "<R>"`, got)
|
||||
}
|
||||
}
|
||||
59
vendor/cloud.google.com/go/bigquery/extract_op.go
generated
vendored
Normal file
59
vendor/cloud.google.com/go/bigquery/extract_op.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
type extractOption interface {
|
||||
customizeExtract(conf *bq.JobConfigurationExtract)
|
||||
}
|
||||
|
||||
// DisableHeader returns an Option that disables the printing of a header row in exported data.
|
||||
func DisableHeader() Option { return disableHeader{} }
|
||||
|
||||
type disableHeader struct{}
|
||||
|
||||
func (opt disableHeader) implementsOption() {}
|
||||
|
||||
func (opt disableHeader) customizeExtract(conf *bq.JobConfigurationExtract) {
|
||||
f := false
|
||||
conf.PrintHeader = &f
|
||||
}
|
||||
|
||||
func (c *Client) extract(ctx context.Context, dst *GCSReference, src *Table, options []Option) (*Job, error) {
|
||||
job, options := initJobProto(c.projectID, options)
|
||||
payload := &bq.JobConfigurationExtract{}
|
||||
|
||||
dst.customizeExtractDst(payload)
|
||||
src.customizeExtractSrc(payload)
|
||||
|
||||
for _, opt := range options {
|
||||
o, ok := opt.(extractOption)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("option (%#v) not applicable to dst/src pair: dst: %T ; src: %T", opt, dst, src)
|
||||
}
|
||||
o.customizeExtract(payload)
|
||||
}
|
||||
|
||||
job.Configuration = &bq.JobConfiguration{
|
||||
Extract: payload,
|
||||
}
|
||||
return c.service.insertJob(ctx, job, c.projectID)
|
||||
}
|
||||
97
vendor/cloud.google.com/go/bigquery/extract_test.go
generated
vendored
Normal file
97
vendor/cloud.google.com/go/bigquery/extract_test.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
func defaultExtractJob() *bq.Job {
|
||||
return &bq.Job{
|
||||
Configuration: &bq.JobConfiguration{
|
||||
Extract: &bq.JobConfigurationExtract{
|
||||
SourceTable: &bq.TableReference{
|
||||
ProjectId: "project-id",
|
||||
DatasetId: "dataset-id",
|
||||
TableId: "table-id",
|
||||
},
|
||||
DestinationUris: []string{"uri"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtract(t *testing.T) {
|
||||
testCases := []struct {
|
||||
dst *GCSReference
|
||||
src *Table
|
||||
options []Option
|
||||
want *bq.Job
|
||||
}{
|
||||
{
|
||||
dst: defaultGCS,
|
||||
src: defaultTable(nil),
|
||||
want: defaultExtractJob(),
|
||||
},
|
||||
{
|
||||
dst: defaultGCS,
|
||||
src: defaultTable(nil),
|
||||
options: []Option{
|
||||
DisableHeader(),
|
||||
},
|
||||
want: func() *bq.Job {
|
||||
j := defaultExtractJob()
|
||||
f := false
|
||||
j.Configuration.Extract.PrintHeader = &f
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: &GCSReference{
|
||||
uris: []string{"uri"},
|
||||
Compression: Gzip,
|
||||
DestinationFormat: JSON,
|
||||
FieldDelimiter: "\t",
|
||||
},
|
||||
src: defaultTable(nil),
|
||||
want: func() *bq.Job {
|
||||
j := defaultExtractJob()
|
||||
j.Configuration.Extract.Compression = "GZIP"
|
||||
j.Configuration.Extract.DestinationFormat = "NEWLINE_DELIMITED_JSON"
|
||||
j.Configuration.Extract.FieldDelimiter = "\t"
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s := &testService{}
|
||||
c := &Client{
|
||||
service: s,
|
||||
}
|
||||
if _, err := c.Copy(context.Background(), tc.dst, tc.src, tc.options...); err != nil {
|
||||
t.Errorf("err calling extract: %v", err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(s.Job, tc.want) {
|
||||
t.Errorf("extracting: got:\n%v\nwant:\n%v", s.Job, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
112
vendor/cloud.google.com/go/bigquery/gcs.go
generated
vendored
Normal file
112
vendor/cloud.google.com/go/bigquery/gcs.go
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import bq "google.golang.org/api/bigquery/v2"
|
||||
|
||||
// GCSReference is a reference to one or more Google Cloud Storage objects, which together constitute
|
||||
// an input or output to a BigQuery operation.
|
||||
type GCSReference struct {
|
||||
uris []string
|
||||
|
||||
// FieldDelimiter is the separator for fields in a CSV file, used when loading or exporting data.
|
||||
// The default is ",".
|
||||
FieldDelimiter string
|
||||
|
||||
// The number of rows at the top of a CSV file that BigQuery will skip when loading the data.
|
||||
SkipLeadingRows int64
|
||||
|
||||
// SourceFormat is the format of the GCS data to be loaded into BigQuery.
|
||||
// Allowed values are: CSV, JSON, DatastoreBackup. The default is CSV.
|
||||
SourceFormat DataFormat
|
||||
// Only used when loading data.
|
||||
Encoding Encoding
|
||||
|
||||
// Quote is the value used to quote data sections in a CSV file.
|
||||
// The default quotation character is the double quote ("), which is used if both Quote and ForceZeroQuote are unset.
|
||||
// To specify that no character should be interpreted as a quotation character, set ForceZeroQuote to true.
|
||||
// Only used when loading data.
|
||||
Quote string
|
||||
ForceZeroQuote bool
|
||||
|
||||
// DestinationFormat is the format to use when writing exported files.
|
||||
// Allowed values are: CSV, Avro, JSON. The default is CSV.
|
||||
// CSV is not supported for tables with nested or repeated fields.
|
||||
DestinationFormat DataFormat
|
||||
// Only used when writing data. Default is None.
|
||||
Compression Compression
|
||||
}
|
||||
|
||||
func (gcs *GCSReference) implementsSource() {}
|
||||
func (gcs *GCSReference) implementsDestination() {}
|
||||
|
||||
// NewGCSReference constructs a reference to one or more Google Cloud Storage objects, which together constitute a data source or destination.
|
||||
// In the simple case, a single URI in the form gs://bucket/object may refer to a single GCS object.
|
||||
// Data may also be split into mutiple files, if multiple URIs or URIs containing wildcards are provided.
|
||||
// Each URI may contain one '*' wildcard character, which (if present) must come after the bucket name.
|
||||
// For more information about the treatment of wildcards and multiple URIs,
|
||||
// see https://cloud.google.com/bigquery/exporting-data-from-bigquery#exportingmultiple
|
||||
func (c *Client) NewGCSReference(uri ...string) *GCSReference {
|
||||
return &GCSReference{uris: uri}
|
||||
}
|
||||
|
||||
type DataFormat string
|
||||
|
||||
const (
|
||||
CSV DataFormat = "CSV"
|
||||
Avro DataFormat = "AVRO"
|
||||
JSON DataFormat = "NEWLINE_DELIMITED_JSON"
|
||||
DatastoreBackup DataFormat = "DATASTORE_BACKUP"
|
||||
)
|
||||
|
||||
// Encoding specifies the character encoding of data to be loaded into BigQuery.
|
||||
// See https://cloud.google.com/bigquery/docs/reference/v2/jobs#configuration.load.encoding
|
||||
// for more details about how this is used.
|
||||
type Encoding string
|
||||
|
||||
const (
|
||||
UTF_8 Encoding = "UTF-8"
|
||||
ISO_8859_1 Encoding = "ISO-8859-1"
|
||||
)
|
||||
|
||||
// Compression is the type of compression to apply when writing data to Google Cloud Storage.
|
||||
type Compression string
|
||||
|
||||
const (
|
||||
None Compression = "NONE"
|
||||
Gzip Compression = "GZIP"
|
||||
)
|
||||
|
||||
func (gcs *GCSReference) customizeLoadSrc(conf *bq.JobConfigurationLoad) {
|
||||
conf.SourceUris = gcs.uris
|
||||
conf.SkipLeadingRows = gcs.SkipLeadingRows
|
||||
conf.SourceFormat = string(gcs.SourceFormat)
|
||||
conf.Encoding = string(gcs.Encoding)
|
||||
conf.FieldDelimiter = gcs.FieldDelimiter
|
||||
|
||||
if gcs.ForceZeroQuote {
|
||||
quote := ""
|
||||
conf.Quote = "e
|
||||
} else if gcs.Quote != "" {
|
||||
conf.Quote = &gcs.Quote
|
||||
}
|
||||
}
|
||||
|
||||
func (gcs *GCSReference) customizeExtractDst(conf *bq.JobConfigurationExtract) {
|
||||
conf.DestinationUris = gcs.uris
|
||||
conf.Compression = string(gcs.Compression)
|
||||
conf.DestinationFormat = string(gcs.DestinationFormat)
|
||||
conf.FieldDelimiter = gcs.FieldDelimiter
|
||||
}
|
||||
154
vendor/cloud.google.com/go/bigquery/integration_test.go
generated
vendored
Normal file
154
vendor/cloud.google.com/go/bigquery/integration_test.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/internal/testutil"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/api/googleapi"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
||||
func TestIntegration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Integration tests skipped in short mode")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ts := testutil.TokenSource(ctx, Scope)
|
||||
if ts == nil {
|
||||
t.Skip("Integration tests skipped. See CONTRIBUTING.md for details")
|
||||
}
|
||||
|
||||
projID := testutil.ProjID()
|
||||
c, err := NewClient(ctx, projID, option.WithTokenSource(ts))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ds := c.Dataset("bigquery_integration_test")
|
||||
if err := ds.Create(ctx); err != nil && !hasStatusCode(err, http.StatusConflict) { // AlreadyExists is 409
|
||||
t.Fatal(err)
|
||||
}
|
||||
schema := Schema([]*FieldSchema{
|
||||
{Name: "name", Type: StringFieldType},
|
||||
{Name: "num", Type: IntegerFieldType},
|
||||
})
|
||||
table := ds.Table("t1")
|
||||
// Delete the table in case it already exists. (Ignore errors.)
|
||||
table.Delete(ctx)
|
||||
// Create the table.
|
||||
err = table.Create(ctx, schema, TableExpiration(time.Now().Add(5*time.Minute)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Check table metadata.
|
||||
md, err := table.Metadata(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// TODO(jba): check md more thorougly.
|
||||
if got, want := md.ID, fmt.Sprintf("%s:%s.%s", projID, ds.id, table.TableID); got != want {
|
||||
t.Errorf("metadata.ID: got %q, want %q", got, want)
|
||||
}
|
||||
if got, want := md.Type, RegularTable; got != want {
|
||||
t.Errorf("metadata.Type: got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
// List tables in the dataset.
|
||||
tables, err := ds.ListTables(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := len(tables), 1; got != want {
|
||||
t.Fatalf("ListTables: got %d, want %d", got, want)
|
||||
}
|
||||
want := *table
|
||||
if got := tables[0]; !reflect.DeepEqual(got, &want) {
|
||||
t.Errorf("ListTables: got %v, want %v", got, &want)
|
||||
}
|
||||
|
||||
// Populate the table.
|
||||
upl := table.NewUploader()
|
||||
var rows []*ValuesSaver
|
||||
for i, name := range []string{"a", "b", "c"} {
|
||||
rows = append(rows, &ValuesSaver{
|
||||
Schema: schema,
|
||||
InsertID: name,
|
||||
Row: []Value{name, i},
|
||||
})
|
||||
}
|
||||
if err := upl.Put(ctx, rows); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkRead := func(src ReadSource) {
|
||||
it, err := c.Read(ctx, src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; it.Next(ctx); i++ {
|
||||
var vals ValueList
|
||||
if err := it.Get(&vals); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := vals, rows[i].Row; !reflect.DeepEqual([]Value(got), want) {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Read the table.
|
||||
checkRead(table)
|
||||
|
||||
// Query the table.
|
||||
q := &Query{
|
||||
Q: "select name, num from t1",
|
||||
DefaultProjectID: projID,
|
||||
DefaultDatasetID: ds.id,
|
||||
}
|
||||
checkRead(q)
|
||||
|
||||
// Query the long way.
|
||||
dest := &Table{}
|
||||
job1, err := c.Copy(ctx, dest, q, WriteTruncate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
job2, err := c.JobFromID(ctx, job1.ID())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// TODO(jba): poll status until job is done
|
||||
_, err = job2.Status(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkRead(job2)
|
||||
|
||||
// TODO(jba): patch the table
|
||||
}
|
||||
|
||||
func hasStatusCode(err error, code int) bool {
|
||||
if e, ok := err.(*googleapi.Error); ok && e.Code == code {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
186
vendor/cloud.google.com/go/bigquery/iterator.go
generated
vendored
Normal file
186
vendor/cloud.google.com/go/bigquery/iterator.go
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// A pageFetcher returns a page of rows, starting from the row specified by token.
|
||||
type pageFetcher interface {
|
||||
fetch(ctx context.Context, s service, token string) (*readDataResult, error)
|
||||
}
|
||||
|
||||
// Iterator provides access to the result of a BigQuery lookup.
|
||||
// Next must be called before the first call to Get.
|
||||
type Iterator struct {
|
||||
service service
|
||||
|
||||
err error // contains any error encountered during calls to Next.
|
||||
|
||||
// Once Next has been called at least once, schema has the result schema, rs contains the current
|
||||
// page of data, and nextToken contains the token for fetching the next
|
||||
// page (empty if there is no more data to be fetched).
|
||||
schema Schema
|
||||
rs [][]Value
|
||||
nextToken string
|
||||
|
||||
// The remaining fields contain enough information to fetch the current
|
||||
// page of data, and determine which row of data from this page is the
|
||||
// current row.
|
||||
|
||||
pf pageFetcher
|
||||
pageToken string
|
||||
|
||||
// The offset from the start of the current page to the current row.
|
||||
// For a new iterator, this is -1.
|
||||
offset int64
|
||||
}
|
||||
|
||||
func newIterator(s service, pf pageFetcher) *Iterator {
|
||||
return &Iterator{
|
||||
service: s,
|
||||
pf: pf,
|
||||
offset: -1,
|
||||
}
|
||||
}
|
||||
|
||||
// fetchPage loads the current page of data from the server.
|
||||
// The contents of rs and nextToken are replaced with the loaded data.
|
||||
// If there is an error while fetching, the error is stored in it.err and false is returned.
|
||||
func (it *Iterator) fetchPage(ctx context.Context) bool {
|
||||
var res *readDataResult
|
||||
var err error
|
||||
for {
|
||||
res, err = it.pf.fetch(ctx, it.service, it.pageToken)
|
||||
if err != errIncompleteJob {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
it.err = err
|
||||
return false
|
||||
}
|
||||
|
||||
it.schema = res.schema
|
||||
it.rs = res.rows
|
||||
it.nextToken = res.pageToken
|
||||
return true
|
||||
}
|
||||
|
||||
// getEnoughData loads new data into rs until offset no longer points beyond the end of rs.
|
||||
func (it *Iterator) getEnoughData(ctx context.Context) bool {
|
||||
if len(it.rs) == 0 {
|
||||
// Either we have not yet fetched any pages, or we are iterating over an empty dataset.
|
||||
// In the former case, we should fetch a page of data, so that we can depend on the resultant nextToken.
|
||||
// In the latter case, it is harmless to fetch a page of data.
|
||||
if !it.fetchPage(ctx) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for it.offset >= int64(len(it.rs)) {
|
||||
// If offset is still outside the bounds of the loaded data,
|
||||
// but there are no more pages of data to fetch, then we have
|
||||
// failed to satisfy the offset.
|
||||
if it.nextToken == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// offset cannot be satisfied with the currently loaded data,
|
||||
// so we fetch the next page. We no longer need the existing
|
||||
// cached rows, so we remove them and update the offset to be
|
||||
// relative to the new page that we're about to fetch.
|
||||
// NOTE: we can't just set offset to 0, because after
|
||||
// marshalling/unmarshalling, it's possible for the offset to
|
||||
// point arbitrarily far beyond the end of rs.
|
||||
// This can happen if the server returns a different size
|
||||
// results page before and after marshalling.
|
||||
it.offset -= int64(len(it.rs))
|
||||
it.pageToken = it.nextToken
|
||||
if !it.fetchPage(ctx) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Next advances the Iterator to the next row, making that row available
|
||||
// via the Get method.
|
||||
// Next must be called before the first call to Get or Schema, and blocks until data is available.
|
||||
// Next returns false when there are no more rows available, either because
|
||||
// the end of the output was reached, or because there was an error (consult
|
||||
// the Err method to determine which).
|
||||
func (it *Iterator) Next(ctx context.Context) bool {
|
||||
if it.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Advance offset to where we want it to be for the next call to Get.
|
||||
it.offset++
|
||||
|
||||
// offset may now point beyond the end of rs, so we fetch data
|
||||
// until offset is within its bounds again. If there are no more
|
||||
// results available, offset will be left pointing beyond the bounds
|
||||
// of rs.
|
||||
// At the end of this method, rs will contain at least one element
|
||||
// unless the dataset we are iterating over is empty.
|
||||
return it.getEnoughData(ctx)
|
||||
}
|
||||
|
||||
// Err returns the last error encountered by Next, or nil for no error.
|
||||
func (it *Iterator) Err() error {
|
||||
return it.err
|
||||
}
|
||||
|
||||
// verifyState checks that the iterator is pointing to a valid row.
|
||||
func (it *Iterator) verifyState() error {
|
||||
if it.err != nil {
|
||||
return fmt.Errorf("called on iterator in error state: %v", it.err)
|
||||
}
|
||||
|
||||
// If Next has been called, then offset should always index into a
|
||||
// valid row in rs, as long as there is still data available.
|
||||
if it.offset >= int64(len(it.rs)) || it.offset < 0 {
|
||||
return errors.New("called without preceding successful call to Next")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get loads the current row into dst, which must implement ValueLoader.
|
||||
func (it *Iterator) Get(dst interface{}) error {
|
||||
if err := it.verifyState(); err != nil {
|
||||
return fmt.Errorf("Get %v", err)
|
||||
}
|
||||
|
||||
if dst, ok := dst.(ValueLoader); ok {
|
||||
return dst.Load(it.rs[it.offset])
|
||||
}
|
||||
return errors.New("Get called with unsupported argument type")
|
||||
}
|
||||
|
||||
// Schema returns the schema of the result rows.
|
||||
func (it *Iterator) Schema() (Schema, error) {
|
||||
if err := it.verifyState(); err != nil {
|
||||
return nil, fmt.Errorf("Schema %v", err)
|
||||
}
|
||||
|
||||
return it.schema, nil
|
||||
}
|
||||
538
vendor/cloud.google.com/go/bigquery/iterator_test.go
generated
vendored
Normal file
538
vendor/cloud.google.com/go/bigquery/iterator_test.go
generated
vendored
Normal file
@@ -0,0 +1,538 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type fetchResponse struct {
|
||||
result *readDataResult // The result to return.
|
||||
err error // The error to return.
|
||||
}
|
||||
|
||||
// pageFetcherStub services fetch requests by returning data from an in-memory list of values.
|
||||
type pageFetcherStub struct {
|
||||
fetchResponses map[string]fetchResponse
|
||||
|
||||
err error
|
||||
}
|
||||
|
||||
func (pf *pageFetcherStub) fetch(ctx context.Context, s service, token string) (*readDataResult, error) {
|
||||
call, ok := pf.fetchResponses[token]
|
||||
if !ok {
|
||||
pf.err = fmt.Errorf("Unexpected page token: %q", token)
|
||||
}
|
||||
return call.result, call.err
|
||||
}
|
||||
|
||||
func TestIterator(t *testing.T) {
|
||||
fetchFailure := errors.New("fetch failure")
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
alreadyConsumed int64 // amount to advance offset before commencing reading.
|
||||
fetchResponses map[string]fetchResponse
|
||||
want []ValueList
|
||||
wantErr error
|
||||
wantSchema Schema
|
||||
}{
|
||||
{
|
||||
desc: "Iteration over single empty page",
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{},
|
||||
schema: Schema{},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []ValueList{},
|
||||
wantSchema: Schema{},
|
||||
},
|
||||
{
|
||||
desc: "Iteration over single page",
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{{1, 2}, {11, 12}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []ValueList{{1, 2}, {11, 12}},
|
||||
wantSchema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Iteration over single page with different schema",
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{{"1", 2}, {"11", 12}},
|
||||
schema: Schema{
|
||||
{Type: StringFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []ValueList{{"1", 2}, {"11", 12}},
|
||||
wantSchema: Schema{
|
||||
{Type: StringFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Iteration over two pages",
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "a",
|
||||
rows: [][]Value{{1, 2}, {11, 12}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
"a": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{{101, 102}, {111, 112}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []ValueList{{1, 2}, {11, 12}, {101, 102}, {111, 112}},
|
||||
wantSchema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Server response includes empty page",
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "a",
|
||||
rows: [][]Value{{1, 2}, {11, 12}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
"a": {
|
||||
result: &readDataResult{
|
||||
pageToken: "b",
|
||||
rows: [][]Value{},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
"b": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{{101, 102}, {111, 112}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []ValueList{{1, 2}, {11, 12}, {101, 102}, {111, 112}},
|
||||
wantSchema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Fetch error",
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "a",
|
||||
rows: [][]Value{{1, 2}, {11, 12}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
"a": {
|
||||
// We returns some data from this fetch, but also an error.
|
||||
// So the end result should include only data from the previous fetch.
|
||||
err: fetchFailure,
|
||||
result: &readDataResult{
|
||||
pageToken: "b",
|
||||
rows: [][]Value{{101, 102}, {111, 112}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []ValueList{{1, 2}, {11, 12}},
|
||||
wantErr: fetchFailure,
|
||||
wantSchema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Skip over a single element",
|
||||
alreadyConsumed: 1,
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "a",
|
||||
rows: [][]Value{{1, 2}, {11, 12}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
"a": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{{101, 102}, {111, 112}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []ValueList{{11, 12}, {101, 102}, {111, 112}},
|
||||
wantSchema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Skip over an entire page",
|
||||
alreadyConsumed: 2,
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "a",
|
||||
rows: [][]Value{{1, 2}, {11, 12}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
"a": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{{101, 102}, {111, 112}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []ValueList{{101, 102}, {111, 112}},
|
||||
wantSchema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Skip beyond start of second page",
|
||||
alreadyConsumed: 3,
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "a",
|
||||
rows: [][]Value{{1, 2}, {11, 12}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
"a": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{{101, 102}, {111, 112}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []ValueList{{111, 112}},
|
||||
wantSchema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Skip beyond all data",
|
||||
alreadyConsumed: 4,
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "a",
|
||||
rows: [][]Value{{1, 2}, {11, 12}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
"a": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{{101, 102}, {111, 112}},
|
||||
schema: Schema{
|
||||
{Type: IntegerFieldType},
|
||||
{Type: IntegerFieldType},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// In this test case, Next will return false on its first call,
|
||||
// so we won't even attempt to call Get.
|
||||
want: []ValueList{},
|
||||
wantSchema: Schema{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
pf := &pageFetcherStub{
|
||||
fetchResponses: tc.fetchResponses,
|
||||
}
|
||||
it := newIterator(nil, pf)
|
||||
it.offset += tc.alreadyConsumed
|
||||
|
||||
values, schema, err := consumeIterator(it)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %v", tc.desc, err)
|
||||
}
|
||||
|
||||
if (len(values) != 0 || len(tc.want) != 0) && !reflect.DeepEqual(values, tc.want) {
|
||||
t.Errorf("%s: values:\ngot: %v\nwant:%v", tc.desc, values, tc.want)
|
||||
}
|
||||
if it.Err() != tc.wantErr {
|
||||
t.Errorf("%s: iterator.Err:\ngot: %v\nwant: %v", tc.desc, it.Err(), tc.wantErr)
|
||||
}
|
||||
if (len(schema) != 0 || len(tc.wantSchema) != 0) && !reflect.DeepEqual(schema, tc.wantSchema) {
|
||||
t.Errorf("%s: iterator.Schema:\ngot: %v\nwant: %v", tc.desc, schema, tc.wantSchema)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// consumeIterator reads the schema and all values from an iterator and returns them.
|
||||
func consumeIterator(it *Iterator) ([]ValueList, Schema, error) {
|
||||
var got []ValueList
|
||||
var schema Schema
|
||||
for it.Next(context.Background()) {
|
||||
var vals ValueList
|
||||
var err error
|
||||
if err = it.Get(&vals); err != nil {
|
||||
return nil, Schema{}, fmt.Errorf("err calling Get: %v", err)
|
||||
}
|
||||
got = append(got, vals)
|
||||
if schema, err = it.Schema(); err != nil {
|
||||
return nil, Schema{}, fmt.Errorf("err calling Schema: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return got, schema, nil
|
||||
}
|
||||
|
||||
func TestGetBeforeNext(t *testing.T) {
|
||||
// TODO: once mashalling/unmarshalling of iterators is implemented, do a similar test for unmarshalled iterators.
|
||||
pf := &pageFetcherStub{
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{{1, 2}, {11, 12}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
it := newIterator(nil, pf)
|
||||
var vals ValueList
|
||||
if err := it.Get(&vals); err == nil {
|
||||
t.Errorf("Expected error calling Get before Next")
|
||||
}
|
||||
}
|
||||
|
||||
type delayedPageFetcher struct {
|
||||
pageFetcherStub
|
||||
delayCount int
|
||||
}
|
||||
|
||||
func (pf *delayedPageFetcher) fetch(ctx context.Context, s service, token string) (*readDataResult, error) {
|
||||
if pf.delayCount > 0 {
|
||||
pf.delayCount--
|
||||
return nil, errIncompleteJob
|
||||
}
|
||||
return pf.pageFetcherStub.fetch(ctx, s, token)
|
||||
}
|
||||
|
||||
func TestIterateIncompleteJob(t *testing.T) {
|
||||
want := []ValueList{{1, 2}, {11, 12}, {101, 102}, {111, 112}}
|
||||
pf := pageFetcherStub{
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "a",
|
||||
rows: [][]Value{{1, 2}, {11, 12}},
|
||||
},
|
||||
},
|
||||
"a": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{{101, 102}, {111, 112}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
dpf := &delayedPageFetcher{
|
||||
pageFetcherStub: pf,
|
||||
delayCount: 1,
|
||||
}
|
||||
it := newIterator(nil, dpf)
|
||||
|
||||
values, _, err := consumeIterator(it)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if (len(values) != 0 || len(want) != 0) && !reflect.DeepEqual(values, want) {
|
||||
t.Errorf("values: got:\n%v\nwant:\n%v", values, want)
|
||||
}
|
||||
if it.Err() != nil {
|
||||
t.Fatalf("iterator.Err: got:\n%v", it.Err())
|
||||
}
|
||||
if dpf.delayCount != 0 {
|
||||
t.Errorf("delayCount: got: %v, want: 0", dpf.delayCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDuringErrorState(t *testing.T) {
|
||||
pf := &pageFetcherStub{
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {err: errors.New("bang")},
|
||||
},
|
||||
}
|
||||
it := newIterator(nil, pf)
|
||||
var vals ValueList
|
||||
it.Next(context.Background())
|
||||
if it.Err() == nil {
|
||||
t.Errorf("Expected error after calling Next")
|
||||
}
|
||||
if err := it.Get(&vals); err == nil {
|
||||
t.Errorf("Expected error calling Get when iterator has a non-nil error.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAfterFinished(t *testing.T) {
|
||||
testCases := []struct {
|
||||
alreadyConsumed int64 // amount to advance offset before commencing reading.
|
||||
fetchResponses map[string]fetchResponse
|
||||
want []ValueList
|
||||
}{
|
||||
{
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{{1, 2}, {11, 12}},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []ValueList{{1, 2}, {11, 12}},
|
||||
},
|
||||
{
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []ValueList{},
|
||||
},
|
||||
{
|
||||
alreadyConsumed: 100,
|
||||
fetchResponses: map[string]fetchResponse{
|
||||
"": {
|
||||
result: &readDataResult{
|
||||
pageToken: "",
|
||||
rows: [][]Value{{1, 2}, {11, 12}},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []ValueList{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
pf := &pageFetcherStub{
|
||||
fetchResponses: tc.fetchResponses,
|
||||
}
|
||||
it := newIterator(nil, pf)
|
||||
it.offset += tc.alreadyConsumed
|
||||
|
||||
values, _, err := consumeIterator(it)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if (len(values) != 0 || len(tc.want) != 0) && !reflect.DeepEqual(values, tc.want) {
|
||||
t.Errorf("values: got:\n%v\nwant:\n%v", values, tc.want)
|
||||
}
|
||||
if it.Err() != nil {
|
||||
t.Fatalf("iterator.Err: got:\n%v\nwant:\n:nil", it.Err())
|
||||
}
|
||||
// Try calling Get again.
|
||||
var vals ValueList
|
||||
if err := it.Get(&vals); err == nil {
|
||||
t.Errorf("Expected error calling Get when there are no more values")
|
||||
}
|
||||
}
|
||||
}
|
||||
131
vendor/cloud.google.com/go/bigquery/job.go
generated
vendored
Normal file
131
vendor/cloud.google.com/go/bigquery/job.go
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
// A Job represents an operation which has been submitted to BigQuery for processing.
|
||||
type Job struct {
|
||||
service service
|
||||
projectID string
|
||||
jobID string
|
||||
|
||||
isQuery bool
|
||||
}
|
||||
|
||||
// JobFromID creates a Job which refers to an existing BigQuery job. The job
|
||||
// need not have been created by this package. For example, the job may have
|
||||
// been created in the BigQuery console.
|
||||
func (c *Client) JobFromID(ctx context.Context, id string) (*Job, error) {
|
||||
jobType, err := c.service.getJobType(ctx, c.projectID, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Job{
|
||||
service: c.service,
|
||||
projectID: c.projectID,
|
||||
jobID: id,
|
||||
isQuery: jobType == queryJobType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (j *Job) ID() string {
|
||||
return j.jobID
|
||||
}
|
||||
|
||||
// State is one of a sequence of states that a Job progresses through as it is processed.
|
||||
type State int
|
||||
|
||||
const (
|
||||
Pending State = iota
|
||||
Running
|
||||
Done
|
||||
)
|
||||
|
||||
// JobStatus contains the current State of a job, and errors encountered while processing that job.
|
||||
type JobStatus struct {
|
||||
State State
|
||||
|
||||
err error
|
||||
|
||||
// All errors encountered during the running of the job.
|
||||
// Not all Errors are fatal, so errors here do not necessarily mean that the job has completed or was unsuccessful.
|
||||
Errors []*Error
|
||||
}
|
||||
|
||||
// jobOption is an Option which modifies a bq.Job proto.
|
||||
// This is used for configuring values that apply to all operations, such as setting a jobReference.
|
||||
type jobOption interface {
|
||||
customizeJob(job *bq.Job, projectID string)
|
||||
}
|
||||
|
||||
type jobID string
|
||||
|
||||
// JobID returns an Option that sets the job ID of a BigQuery job.
|
||||
// If this Option is not used, a job ID is generated automatically.
|
||||
func JobID(ID string) Option {
|
||||
return jobID(ID)
|
||||
}
|
||||
|
||||
func (opt jobID) implementsOption() {}
|
||||
|
||||
func (opt jobID) customizeJob(job *bq.Job, projectID string) {
|
||||
job.JobReference = &bq.JobReference{
|
||||
JobId: string(opt),
|
||||
ProjectId: projectID,
|
||||
}
|
||||
}
|
||||
|
||||
// Done reports whether the job has completed.
|
||||
// After Done returns true, the Err method will return an error if the job completed unsuccesfully.
|
||||
func (s *JobStatus) Done() bool {
|
||||
return s.State == Done
|
||||
}
|
||||
|
||||
// Err returns the error that caused the job to complete unsuccesfully (if any).
|
||||
func (s *JobStatus) Err() error {
|
||||
return s.err
|
||||
}
|
||||
|
||||
// Status returns the current status of the job. It fails if the Status could not be determined.
|
||||
func (j *Job) Status(ctx context.Context) (*JobStatus, error) {
|
||||
return j.service.jobStatus(ctx, j.projectID, j.jobID)
|
||||
}
|
||||
|
||||
// Cancel requests that a job be cancelled. This method returns without waiting for
|
||||
// cancellation to take effect. To check whether the job has terminated, use Job.Status.
|
||||
// Cancelled jobs may still incur costs.
|
||||
func (j *Job) Cancel(ctx context.Context) error {
|
||||
return j.service.jobCancel(ctx, j.projectID, j.jobID)
|
||||
}
|
||||
|
||||
func (j *Job) implementsReadSource() {}
|
||||
|
||||
func (j *Job) customizeReadQuery(cursor *readQueryConf) error {
|
||||
// There are mulitple kinds of jobs, but only a query job is suitable for reading.
|
||||
if !j.isQuery {
|
||||
return errors.New("Cannot read from a non-query job")
|
||||
}
|
||||
|
||||
cursor.projectID = j.projectID
|
||||
cursor.jobID = j.jobID
|
||||
return nil
|
||||
}
|
||||
70
vendor/cloud.google.com/go/bigquery/legacy.go
generated
vendored
Normal file
70
vendor/cloud.google.com/go/bigquery/legacy.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// OpenTable creates a handle to an existing BigQuery table. If the table does
|
||||
// not already exist, subsequent uses of the *Table will fail.
|
||||
//
|
||||
// Deprecated: use Client.DatasetInProject.Table instead.
|
||||
func (c *Client) OpenTable(projectID, datasetID, tableID string) *Table {
|
||||
return c.Table(projectID, datasetID, tableID)
|
||||
}
|
||||
|
||||
// Table creates a handle to a BigQuery table.
|
||||
//
|
||||
// Use this method to reference a table in a project other than that of the
|
||||
// Client.
|
||||
//
|
||||
// Deprecated: use Client.DatasetInProject.Table instead.
|
||||
func (c *Client) Table(projectID, datasetID, tableID string) *Table {
|
||||
return &Table{ProjectID: projectID, DatasetID: datasetID, TableID: tableID, service: c.service}
|
||||
}
|
||||
|
||||
// CreateTable creates a table in the BigQuery service and returns a handle to it.
|
||||
//
|
||||
// Deprecated: use Table.Create instead.
|
||||
func (c *Client) CreateTable(ctx context.Context, projectID, datasetID, tableID string, options ...CreateTableOption) (*Table, error) {
|
||||
t := c.Table(projectID, datasetID, tableID)
|
||||
if err := t.Create(ctx, options...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// Read fetches data from a ReadSource and returns the data via an Iterator.
|
||||
//
|
||||
// Deprecated: use Query.Read, Job.Read or Table.Read instead.
|
||||
func (c *Client) Read(ctx context.Context, src ReadSource, options ...ReadOption) (*Iterator, error) {
|
||||
switch src := src.(type) {
|
||||
case *Job:
|
||||
return src.Read(ctx, options...)
|
||||
case *Query:
|
||||
// For compatibility, support Query values created by literal, rather
|
||||
// than Client.Query.
|
||||
if src.client == nil {
|
||||
src.client = c
|
||||
}
|
||||
return src.Read(ctx, options...)
|
||||
case *Table:
|
||||
return src.Read(ctx, options...)
|
||||
}
|
||||
return nil, fmt.Errorf("src (%T) does not support the Read operation", src)
|
||||
}
|
||||
112
vendor/cloud.google.com/go/bigquery/load_op.go
generated
vendored
Normal file
112
vendor/cloud.google.com/go/bigquery/load_op.go
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
type loadOption interface {
|
||||
customizeLoad(conf *bq.JobConfigurationLoad)
|
||||
}
|
||||
|
||||
// DestinationSchema returns an Option that specifies the schema to use when loading data into a new table.
|
||||
// A DestinationSchema Option must be supplied when loading data from Google Cloud Storage into a non-existent table.
|
||||
// Caveat: DestinationSchema is not required if the data being loaded is a datastore backup.
|
||||
// schema must not be nil.
|
||||
func DestinationSchema(schema Schema) Option { return destSchema{Schema: schema} }
|
||||
|
||||
type destSchema struct {
|
||||
Schema
|
||||
}
|
||||
|
||||
func (opt destSchema) implementsOption() {}
|
||||
|
||||
func (opt destSchema) customizeLoad(conf *bq.JobConfigurationLoad) {
|
||||
conf.Schema = opt.asTableSchema()
|
||||
}
|
||||
|
||||
// MaxBadRecords returns an Option that sets the maximum number of bad records that will be ignored.
|
||||
// If this maximum is exceeded, the operation will be unsuccessful.
|
||||
func MaxBadRecords(n int64) Option { return maxBadRecords(n) }
|
||||
|
||||
type maxBadRecords int64
|
||||
|
||||
func (opt maxBadRecords) implementsOption() {}
|
||||
|
||||
func (opt maxBadRecords) customizeLoad(conf *bq.JobConfigurationLoad) {
|
||||
conf.MaxBadRecords = int64(opt)
|
||||
}
|
||||
|
||||
// AllowJaggedRows returns an Option that causes missing trailing optional columns to be tolerated in CSV data. Missing values are treated as nulls.
|
||||
func AllowJaggedRows() Option { return allowJaggedRows{} }
|
||||
|
||||
type allowJaggedRows struct{}
|
||||
|
||||
func (opt allowJaggedRows) implementsOption() {}
|
||||
|
||||
func (opt allowJaggedRows) customizeLoad(conf *bq.JobConfigurationLoad) {
|
||||
conf.AllowJaggedRows = true
|
||||
}
|
||||
|
||||
// AllowQuotedNewlines returns an Option that allows quoted data sections containing newlines in CSV data.
|
||||
func AllowQuotedNewlines() Option { return allowQuotedNewlines{} }
|
||||
|
||||
type allowQuotedNewlines struct{}
|
||||
|
||||
func (opt allowQuotedNewlines) implementsOption() {}
|
||||
|
||||
func (opt allowQuotedNewlines) customizeLoad(conf *bq.JobConfigurationLoad) {
|
||||
conf.AllowQuotedNewlines = true
|
||||
}
|
||||
|
||||
// IgnoreUnknownValues returns an Option that causes values not matching the schema to be tolerated.
|
||||
// Unknown values are ignored. For CSV this ignores extra values at the end of a line.
|
||||
// For JSON this ignores named values that do not match any column name.
|
||||
// If this Option is not used, records containing unknown values are treated as bad records.
|
||||
// The MaxBadRecords Option can be used to customize how bad records are handled.
|
||||
func IgnoreUnknownValues() Option { return ignoreUnknownValues{} }
|
||||
|
||||
type ignoreUnknownValues struct{}
|
||||
|
||||
func (opt ignoreUnknownValues) implementsOption() {}
|
||||
|
||||
func (opt ignoreUnknownValues) customizeLoad(conf *bq.JobConfigurationLoad) {
|
||||
conf.IgnoreUnknownValues = true
|
||||
}
|
||||
|
||||
func (c *Client) load(ctx context.Context, dst *Table, src *GCSReference, options []Option) (*Job, error) {
|
||||
job, options := initJobProto(c.projectID, options)
|
||||
payload := &bq.JobConfigurationLoad{}
|
||||
|
||||
dst.customizeLoadDst(payload)
|
||||
src.customizeLoadSrc(payload)
|
||||
|
||||
for _, opt := range options {
|
||||
o, ok := opt.(loadOption)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("option (%#v) not applicable to dst/src pair: dst: %T ; src: %T", opt, dst, src)
|
||||
}
|
||||
o.customizeLoad(payload)
|
||||
}
|
||||
|
||||
job.Configuration = &bq.JobConfiguration{
|
||||
Load: payload,
|
||||
}
|
||||
return c.service.insertJob(ctx, job, c.projectID)
|
||||
}
|
||||
198
vendor/cloud.google.com/go/bigquery/load_test.go
generated
vendored
Normal file
198
vendor/cloud.google.com/go/bigquery/load_test.go
generated
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
func defaultLoadJob() *bq.Job {
|
||||
return &bq.Job{
|
||||
Configuration: &bq.JobConfiguration{
|
||||
Load: &bq.JobConfigurationLoad{
|
||||
DestinationTable: &bq.TableReference{
|
||||
ProjectId: "project-id",
|
||||
DatasetId: "dataset-id",
|
||||
TableId: "table-id",
|
||||
},
|
||||
SourceUris: []string{"uri"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func stringFieldSchema() *FieldSchema {
|
||||
return &FieldSchema{Name: "fieldname", Type: StringFieldType}
|
||||
}
|
||||
|
||||
func nestedFieldSchema() *FieldSchema {
|
||||
return &FieldSchema{
|
||||
Name: "nested",
|
||||
Type: RecordFieldType,
|
||||
Schema: Schema{stringFieldSchema()},
|
||||
}
|
||||
}
|
||||
|
||||
func bqStringFieldSchema() *bq.TableFieldSchema {
|
||||
return &bq.TableFieldSchema{
|
||||
Name: "fieldname",
|
||||
Type: "STRING",
|
||||
}
|
||||
}
|
||||
|
||||
func bqNestedFieldSchema() *bq.TableFieldSchema {
|
||||
return &bq.TableFieldSchema{
|
||||
Name: "nested",
|
||||
Type: "RECORD",
|
||||
Fields: []*bq.TableFieldSchema{bqStringFieldSchema()},
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
testCases := []struct {
|
||||
dst *Table
|
||||
src *GCSReference
|
||||
options []Option
|
||||
want *bq.Job
|
||||
}{
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: defaultGCS,
|
||||
want: defaultLoadJob(),
|
||||
},
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: defaultGCS,
|
||||
options: []Option{
|
||||
MaxBadRecords(1),
|
||||
AllowJaggedRows(),
|
||||
AllowQuotedNewlines(),
|
||||
IgnoreUnknownValues(),
|
||||
},
|
||||
want: func() *bq.Job {
|
||||
j := defaultLoadJob()
|
||||
j.Configuration.Load.MaxBadRecords = 1
|
||||
j.Configuration.Load.AllowJaggedRows = true
|
||||
j.Configuration.Load.AllowQuotedNewlines = true
|
||||
j.Configuration.Load.IgnoreUnknownValues = true
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: &Table{
|
||||
ProjectID: "project-id",
|
||||
DatasetID: "dataset-id",
|
||||
TableID: "table-id",
|
||||
},
|
||||
options: []Option{CreateNever, WriteTruncate},
|
||||
src: defaultGCS,
|
||||
want: func() *bq.Job {
|
||||
j := defaultLoadJob()
|
||||
j.Configuration.Load.CreateDisposition = "CREATE_NEVER"
|
||||
j.Configuration.Load.WriteDisposition = "WRITE_TRUNCATE"
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: &Table{
|
||||
ProjectID: "project-id",
|
||||
DatasetID: "dataset-id",
|
||||
TableID: "table-id",
|
||||
},
|
||||
src: defaultGCS,
|
||||
options: []Option{
|
||||
DestinationSchema(Schema{
|
||||
stringFieldSchema(),
|
||||
nestedFieldSchema(),
|
||||
}),
|
||||
},
|
||||
want: func() *bq.Job {
|
||||
j := defaultLoadJob()
|
||||
j.Configuration.Load.Schema = &bq.TableSchema{
|
||||
Fields: []*bq.TableFieldSchema{
|
||||
bqStringFieldSchema(),
|
||||
bqNestedFieldSchema(),
|
||||
}}
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: &GCSReference{
|
||||
uris: []string{"uri"},
|
||||
SkipLeadingRows: 1,
|
||||
SourceFormat: JSON,
|
||||
Encoding: UTF_8,
|
||||
FieldDelimiter: "\t",
|
||||
Quote: "-",
|
||||
},
|
||||
want: func() *bq.Job {
|
||||
j := defaultLoadJob()
|
||||
j.Configuration.Load.SkipLeadingRows = 1
|
||||
j.Configuration.Load.SourceFormat = "NEWLINE_DELIMITED_JSON"
|
||||
j.Configuration.Load.Encoding = "UTF-8"
|
||||
j.Configuration.Load.FieldDelimiter = "\t"
|
||||
hyphen := "-"
|
||||
j.Configuration.Load.Quote = &hyphen
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: &GCSReference{
|
||||
uris: []string{"uri"},
|
||||
Quote: "",
|
||||
},
|
||||
want: func() *bq.Job {
|
||||
j := defaultLoadJob()
|
||||
j.Configuration.Load.Quote = nil
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: &GCSReference{
|
||||
uris: []string{"uri"},
|
||||
Quote: "",
|
||||
ForceZeroQuote: true,
|
||||
},
|
||||
want: func() *bq.Job {
|
||||
j := defaultLoadJob()
|
||||
empty := ""
|
||||
j.Configuration.Load.Quote = &empty
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s := &testService{}
|
||||
c := &Client{
|
||||
service: s,
|
||||
}
|
||||
if _, err := c.Copy(context.Background(), tc.dst, tc.src, tc.options...); err != nil {
|
||||
t.Errorf("err calling load: %v", err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(s.Job, tc.want) {
|
||||
t.Errorf("loading: got:\n%v\nwant:\n%v", s.Job, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
44
vendor/cloud.google.com/go/bigquery/query.go
generated
vendored
Normal file
44
vendor/cloud.google.com/go/bigquery/query.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import bq "google.golang.org/api/bigquery/v2"
|
||||
|
||||
// Query represents a query to be executed. Use Client.Query to create a query.
|
||||
type Query struct {
|
||||
// The query to execute. See https://cloud.google.com/bigquery/query-reference for details.
|
||||
Q string
|
||||
|
||||
// DefaultProjectID and DefaultDatasetID specify the dataset to use for unqualified table names in the query.
|
||||
// If DefaultProjectID is set, DefaultDatasetID must also be set.
|
||||
DefaultProjectID string
|
||||
DefaultDatasetID string
|
||||
|
||||
client *Client
|
||||
}
|
||||
|
||||
func (q *Query) implementsSource() {}
|
||||
|
||||
func (q *Query) implementsReadSource() {}
|
||||
|
||||
func (q *Query) customizeQuerySrc(conf *bq.JobConfigurationQuery) {
|
||||
conf.Query = q.Q
|
||||
if q.DefaultProjectID != "" || q.DefaultDatasetID != "" {
|
||||
conf.DefaultDataset = &bq.DatasetReference{
|
||||
DatasetId: q.DefaultDatasetID,
|
||||
ProjectId: q.DefaultProjectID,
|
||||
}
|
||||
}
|
||||
}
|
||||
148
vendor/cloud.google.com/go/bigquery/query_op.go
generated
vendored
Normal file
148
vendor/cloud.google.com/go/bigquery/query_op.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
type queryOption interface {
|
||||
customizeQuery(conf *bq.JobConfigurationQuery)
|
||||
}
|
||||
|
||||
// DisableQueryCache returns an Option that prevents results being fetched from the query cache.
|
||||
// If this Option is not used, results are fetched from the cache if they are available.
|
||||
// The query cache is a best-effort cache that is flushed whenever tables in the query are modified.
|
||||
// Cached results are only available when TableID is unspecified in the query's destination Table.
|
||||
// For more information, see https://cloud.google.com/bigquery/querying-data#querycaching
|
||||
func DisableQueryCache() Option { return disableQueryCache{} }
|
||||
|
||||
type disableQueryCache struct{}
|
||||
|
||||
func (opt disableQueryCache) implementsOption() {}
|
||||
|
||||
func (opt disableQueryCache) customizeQuery(conf *bq.JobConfigurationQuery) {
|
||||
f := false
|
||||
conf.UseQueryCache = &f
|
||||
}
|
||||
|
||||
// DisableFlattenedResults returns an Option that prevents results being flattened.
|
||||
// If this Option is not used, results from nested and repeated fields are flattened.
|
||||
// DisableFlattenedResults implies AllowLargeResults
|
||||
// For more information, see https://cloud.google.com/bigquery/docs/data#nested
|
||||
func DisableFlattenedResults() Option { return disableFlattenedResults{} }
|
||||
|
||||
type disableFlattenedResults struct{}
|
||||
|
||||
func (opt disableFlattenedResults) implementsOption() {}
|
||||
|
||||
func (opt disableFlattenedResults) customizeQuery(conf *bq.JobConfigurationQuery) {
|
||||
f := false
|
||||
conf.FlattenResults = &f
|
||||
// DisableFlattenedResults implies AllowLargeResults
|
||||
allowLargeResults{}.customizeQuery(conf)
|
||||
}
|
||||
|
||||
// AllowLargeResults returns an Option that allows the query to produce arbitrarily large result tables.
|
||||
// The destination must be a table.
|
||||
// When using this option, queries will take longer to execute, even if the result set is small.
|
||||
// For additional limitations, see https://cloud.google.com/bigquery/querying-data#largequeryresults
|
||||
func AllowLargeResults() Option { return allowLargeResults{} }
|
||||
|
||||
type allowLargeResults struct{}
|
||||
|
||||
func (opt allowLargeResults) implementsOption() {}
|
||||
|
||||
func (opt allowLargeResults) customizeQuery(conf *bq.JobConfigurationQuery) {
|
||||
conf.AllowLargeResults = true
|
||||
}
|
||||
|
||||
// JobPriority returns an Option that causes a query to be scheduled with the specified priority.
|
||||
// The default priority is InteractivePriority.
|
||||
// For more information, see https://cloud.google.com/bigquery/querying-data#batchqueries
|
||||
func JobPriority(priority string) Option { return jobPriority(priority) }
|
||||
|
||||
type jobPriority string
|
||||
|
||||
func (opt jobPriority) implementsOption() {}
|
||||
|
||||
func (opt jobPriority) customizeQuery(conf *bq.JobConfigurationQuery) {
|
||||
conf.Priority = string(opt)
|
||||
}
|
||||
|
||||
const (
|
||||
BatchPriority = "BATCH"
|
||||
InteractivePriority = "INTERACTIVE"
|
||||
)
|
||||
|
||||
// MaxBillingTier returns an Option that sets the maximum billing tier for a Query.
|
||||
// Queries that have resource usage beyond this tier will fail (without
|
||||
// incurring a charge). If this Option is not used, the project default will be used.
|
||||
func MaxBillingTier(tier int) Option { return maxBillingTier(tier) }
|
||||
|
||||
type maxBillingTier int
|
||||
|
||||
func (opt maxBillingTier) implementsOption() {}
|
||||
|
||||
func (opt maxBillingTier) customizeQuery(conf *bq.JobConfigurationQuery) {
|
||||
tier := int64(opt)
|
||||
conf.MaximumBillingTier = &tier
|
||||
}
|
||||
|
||||
// MaxBytesBilled returns an Option that limits the number of bytes billed for
|
||||
// this job. Queries that would exceed this limit will fail (without incurring
|
||||
// a charge).
|
||||
// If this Option is not used, or bytes is < 1, the project default will be
|
||||
// used.
|
||||
func MaxBytesBilled(bytes int64) Option { return maxBytesBilled(bytes) }
|
||||
|
||||
type maxBytesBilled int64
|
||||
|
||||
func (opt maxBytesBilled) implementsOption() {}
|
||||
|
||||
func (opt maxBytesBilled) customizeQuery(conf *bq.JobConfigurationQuery) {
|
||||
if opt >= 1 {
|
||||
conf.MaximumBytesBilled = int64(opt)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) query(ctx context.Context, dst *Table, src *Query, options []Option) (*Job, error) {
|
||||
job, options := initJobProto(c.projectID, options)
|
||||
payload := &bq.JobConfigurationQuery{}
|
||||
|
||||
dst.customizeQueryDst(payload)
|
||||
src.customizeQuerySrc(payload)
|
||||
|
||||
for _, opt := range options {
|
||||
o, ok := opt.(queryOption)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("option (%#v) not applicable to dst/src pair: dst: %T ; src: %T", opt, dst, src)
|
||||
}
|
||||
o.customizeQuery(payload)
|
||||
}
|
||||
|
||||
job.Configuration = &bq.JobConfiguration{
|
||||
Query: payload,
|
||||
}
|
||||
j, err := c.service.insertJob(ctx, job, c.projectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
j.isQuery = true
|
||||
return j, nil
|
||||
}
|
||||
168
vendor/cloud.google.com/go/bigquery/query_test.go
generated
vendored
Normal file
168
vendor/cloud.google.com/go/bigquery/query_test.go
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
bq "google.golang.org/api/bigquery/v2"
|
||||
)
|
||||
|
||||
func defaultQueryJob() *bq.Job {
|
||||
return &bq.Job{
|
||||
Configuration: &bq.JobConfiguration{
|
||||
Query: &bq.JobConfigurationQuery{
|
||||
DestinationTable: &bq.TableReference{
|
||||
ProjectId: "project-id",
|
||||
DatasetId: "dataset-id",
|
||||
TableId: "table-id",
|
||||
},
|
||||
Query: "query string",
|
||||
DefaultDataset: &bq.DatasetReference{
|
||||
ProjectId: "def-project-id",
|
||||
DatasetId: "def-dataset-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuery(t *testing.T) {
|
||||
testCases := []struct {
|
||||
dst *Table
|
||||
src *Query
|
||||
options []Option
|
||||
want *bq.Job
|
||||
}{
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: defaultQuery,
|
||||
want: defaultQueryJob(),
|
||||
},
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: &Query{
|
||||
Q: "query string",
|
||||
},
|
||||
want: func() *bq.Job {
|
||||
j := defaultQueryJob()
|
||||
j.Configuration.Query.DefaultDataset = nil
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: &Table{},
|
||||
src: defaultQuery,
|
||||
want: func() *bq.Job {
|
||||
j := defaultQueryJob()
|
||||
j.Configuration.Query.DestinationTable = nil
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: &Table{
|
||||
ProjectID: "project-id",
|
||||
DatasetID: "dataset-id",
|
||||
TableID: "table-id",
|
||||
},
|
||||
src: defaultQuery,
|
||||
options: []Option{CreateNever, WriteTruncate},
|
||||
want: func() *bq.Job {
|
||||
j := defaultQueryJob()
|
||||
j.Configuration.Query.WriteDisposition = "WRITE_TRUNCATE"
|
||||
j.Configuration.Query.CreateDisposition = "CREATE_NEVER"
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: defaultQuery,
|
||||
options: []Option{DisableQueryCache()},
|
||||
want: func() *bq.Job {
|
||||
j := defaultQueryJob()
|
||||
f := false
|
||||
j.Configuration.Query.UseQueryCache = &f
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: defaultQuery,
|
||||
options: []Option{AllowLargeResults()},
|
||||
want: func() *bq.Job {
|
||||
j := defaultQueryJob()
|
||||
j.Configuration.Query.AllowLargeResults = true
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: defaultQuery,
|
||||
options: []Option{DisableFlattenedResults()},
|
||||
want: func() *bq.Job {
|
||||
j := defaultQueryJob()
|
||||
f := false
|
||||
j.Configuration.Query.FlattenResults = &f
|
||||
j.Configuration.Query.AllowLargeResults = true
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: defaultQuery,
|
||||
options: []Option{JobPriority("low")},
|
||||
want: func() *bq.Job {
|
||||
j := defaultQueryJob()
|
||||
j.Configuration.Query.Priority = "low"
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: defaultQuery,
|
||||
options: []Option{MaxBillingTier(3), MaxBytesBilled(5)},
|
||||
want: func() *bq.Job {
|
||||
j := defaultQueryJob()
|
||||
tier := int64(3)
|
||||
j.Configuration.Query.MaximumBillingTier = &tier
|
||||
j.Configuration.Query.MaximumBytesBilled = 5
|
||||
return j
|
||||
}(),
|
||||
},
|
||||
{
|
||||
dst: defaultTable(nil),
|
||||
src: defaultQuery,
|
||||
options: []Option{MaxBytesBilled(-1)},
|
||||
want: defaultQueryJob(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s := &testService{}
|
||||
c := &Client{
|
||||
service: s,
|
||||
}
|
||||
if _, err := c.Copy(context.Background(), tc.dst, tc.src, tc.options...); err != nil {
|
||||
t.Errorf("err calling query: %v", err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(s.Job, tc.want) {
|
||||
t.Errorf("querying: got:\n%v\nwant:\n%v", s.Job, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
70
vendor/cloud.google.com/go/bigquery/read_op.go
generated
vendored
Normal file
70
vendor/cloud.google.com/go/bigquery/read_op.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 bigquery
|
||||
|
||||
import "golang.org/x/net/context"
|
||||
|
||||
// RecordsPerRequest returns a ReadOption that sets the number of records to fetch per request when streaming data from BigQuery.
|
||||
func RecordsPerRequest(n int64) ReadOption { return recordsPerRequest(n) }
|
||||
|
||||
type recordsPerRequest int64
|
||||
|
||||
func (opt recordsPerRequest) customizeRead(conf *pagingConf) {
|
||||
conf.recordsPerRequest = int64(opt)
|
||||
conf.setRecordsPerRequest = true
|
||||
}
|
||||
|
||||
// StartIndex returns a ReadOption that sets the zero-based index of the row to start reading from.
|
||||
func StartIndex(i uint64) ReadOption { return startIndex(i) }
|
||||
|
||||
type startIndex uint64
|
||||
|
||||
func (opt startIndex) customizeRead(conf *pagingConf) {
|
||||
conf.startIndex = uint64(opt)
|
||||
}
|
||||
|
||||
func (conf *readTableConf) fetch(ctx context.Context, s service, token string) (*readDataResult, error) {
|
||||
return s.readTabledata(ctx, conf, token)
|
||||
}
|
||||
|
||||
// Read fetches the contents of the table.
|
||||
func (t *Table) Read(_ context.Context, options ...ReadOption) (*Iterator, error) {
|
||||
conf := &readTableConf{}
|
||||
t.customizeReadSrc(conf)
|
||||
|
||||
for _, o := range options {
|
||||
o.customizeRead(&conf.paging)
|
||||
}
|
||||
|
||||
return newIterator(t.service, conf), nil
|
||||
}
|
||||
|
||||
func (conf *readQueryConf) fetch(ctx context.Context, s service, token string) (*readDataResult, error) {
|
||||
return s.readQuery(ctx, conf, token)
|
||||
}
|
||||
|
||||
// Read fetches the results of a query job.
|
||||
func (j *Job) Read(_ context.Context, options ...ReadOption) (*Iterator, error) {
|
||||
conf := &readQueryConf{}
|
||||
if err := j.customizeReadQuery(conf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, o := range options {
|
||||
o.customizeRead(&conf.paging)
|
||||
}
|
||||
|
||||
return newIterator(j.service, conf), nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user