1
0
mirror of https://github.com/kubernetes-sigs/descheduler.git synced 2026-01-26 05:14:13 +01:00

Compare commits

...

94 Commits

Author SHA1 Message Date
Kubernetes Prow Robot
fc1688057a Merge pull request #142 from adyada/master
Added support to evict pods that use local storage.
2019-04-12 07:42:46 -07:00
Adi Yadav
e6e200b93c Added auto-generated changes. 2019-04-11 23:24:56 -04:00
Adi Yadav
5d843d1f08 Added manual changes. 2019-04-11 23:24:20 -04:00
Kubernetes Prow Robot
6c981cc067 Merge pull request #137 from pickledrick/fix/update-installation-readme
update readme to fix unused path
2019-03-04 06:04:39 -08:00
Peter Grant
22a3a6ea1d update readme to fix unused path 2019-03-04 15:16:37 +11:00
Kubernetes Prow Robot
294bddb5e2 Merge pull request #135 from russiancook/patch-1
Update configmap.yaml
2019-02-28 13:34:37 -08:00
russiancook
0a9d1959e2 Update configmap.yaml
Put configmap in kube-system namespace
2019-02-14 12:12:47 +02:00
RaviSantosh Gudimetla
19ee5d80b5 Merge pull request #124 from ravisantoshgudimetla/fix-kubeadm
Run apiserver on private ip and bump kube version installed to 1.13
2019-01-14 22:41:26 -05:00
ravisantoshgudimetla
14d9e175c2 Run apiserver on private ip and bump kube version installed to 1.13 2018-12-14 14:54:42 -05:00
RaviSantosh Gudimetla
468e138070 Merge pull request #114 from nikhita/contributing.md
Add CONTRIBUTING.md
2018-09-03 09:15:37 -04:00
Nikhita Raghunath
db13b2ac73 Add CONTRIBUTING.md 2018-09-01 19:29:24 +05:30
Avesh Agarwal
40ca53e0a5 Merge pull request #113 from sanity-io/master
Yaml files for kubernetes
2018-08-24 08:50:41 -04:00
nicholas.klem
35d8367fe5 rbac.authorization.k8s.io/v1 - not v1beta1 2018-08-24 14:44:19 +02:00
nicholas.klem
345dd9cf27 add kubernetes yaml files 2018-08-24 14:34:38 +02:00
RaviSantosh Gudimetla
81f471fe05 Merge pull request #111 from kubernetes-incubator/ravisantoshgudimetla-patch-2
Remove production usage warning
2018-08-23 15:57:09 -04:00
RaviSantosh Gudimetla
aa5e8770f5 Remove production usage warning
Removing production usage warning to encourage more users to try using descheduler and since descheduler has been stable so far.
2018-08-23 15:44:17 -04:00
RaviSantosh Gudimetla
2690d139c5 Merge pull request #110 from kubernetes-incubator/ravisantoshgudimetla-patch-1
Update the compatibility matrix
2018-08-22 16:31:09 -04:00
RaviSantosh Gudimetla
cd192ce5fc Update the compatibility matrix
Descheduler 0.4+ should work with kube 1.9+.
2018-08-22 16:09:18 -04:00
RaviSantosh Gudimetla
048f3fd1e5 Merge pull request #109 from ravisantoshgudimetla/test-cases-cleanup
Remove the unnecessary print statements in test file
2018-08-22 12:20:27 -04:00
ravisantoshgudimetla
a079fd2757 Remove the unnecessary print statements 2018-08-22 11:27:40 -04:00
RaviSantosh Gudimetla
ae0a9ed525 Merge pull request #108 from ravisantoshgudimetla/fix-warnings-ci
Fix deprecated warning in CI
2018-08-21 15:10:40 -04:00
ravisantoshgudimetla
0a815e8786 Fix deprecated warning in CI 2018-08-21 14:45:14 -04:00
RaviSantosh Gudimetla
0115748fe8 Merge pull request #105 from ravisantoshgudimetla/priority-low-node
Low node utilization to respect priority while evicting pods
2018-08-21 14:33:50 -04:00
ravisantoshgudimetla
d0305dac3f Low node utilization to respect priority while evicting pods 2018-08-21 14:14:26 -04:00
RaviSantosh Gudimetla
72d6a8aa33 Merge pull request #106 from ravisantoshgudimetla/fix-e2e
Fix broken e2e tests
2018-08-06 10:53:13 -04:00
ravisantoshgudimetla
654fdbba94 Fix broken e2e tests 2018-08-05 13:16:47 -04:00
Avesh Agarwal
d73471327b Merge pull request #99 from aveshagarwal/master-fix-1
Add SECURITY_CONTACTS file.
2018-05-31 12:05:58 -04:00
Avesh Agarwal
1294c8a2c2 Add SECURITY_CONTACTS file. 2018-05-31 11:46:54 -04:00
Avesh Agarwal
ddd3dd6f19 Merge pull request #95 from betabrand/auth-providers
Allow other auth providers (GCP, OIDC, Azure, OpenStack)
2018-05-23 13:01:27 -04:00
Hugues Alary
fad9e8dc39 Running glide update -v to pulldown all the required depedencies 2018-05-22 19:14:00 -07:00
Hugues Alary
450a5c290b Allow other auth providers (GCP, OIDC, Azure, OpenStack) 2018-05-22 19:13:47 -07:00
RaviSantosh Gudimetla
af2198428e Merge pull request #94 from ingvagabund/travis-bump-go-to-1.10
Bump Travis go version to 1.9.1
2018-05-22 09:39:29 -04:00
Jan Chaloupka
bc7be54e2e Bump Travis go version to 1.9.1 2018-05-22 14:50:20 +02:00
Avesh Agarwal
ccb61fc800 Merge pull request #91 from aveshagarwal/master-critical-pod
Fix readme to have empty value for critical pod annotation as required.
2018-05-10 17:22:53 -04:00
Avesh Agarwal
61819e3fec Fix readme to have empty value for critical pod annotation as required. 2018-05-10 16:44:40 -04:00
Avesh Agarwal
a7ceb67109 Merge pull request #88 from aveshagarwal/master-issues
Fix dryRun issue and avoid unnecessary log msg when node affinity strategy is disabled.
2018-04-24 09:48:05 -04:00
Avesh Agarwal
57a28e9a8f Fix dryRun issue and avoid unnecessary log
msg when node affinity strategy is disabled.
2018-04-23 17:13:19 -04:00
Avesh Agarwal
c1d87dd93c Merge pull request #87 from aveshagarwal/master-pod-max-issue
Fix max pods to evict per node.
2018-04-13 14:38:02 -04:00
Avesh Agarwal
34fb602101 Fix max pods to evict per node. 2018-04-12 15:28:24 -04:00
Jan Chaloupka
a6af54ab30 Merge pull request #85 from ingvagabund/allow-to-set-repo-org
Allow to override repository organization
2018-03-29 16:54:56 +02:00
Jan Chaloupka
e41ef8cca3 Allow to override repository organization
Given the repository is build and about-to-be tested under openshift organization
we need to change the kubernetes-incubator into openshift. Yet, keeping both repos still in sync.
2018-03-29 16:34:02 +02:00
Avesh Agarwal
d26cd4b317 Merge pull request #84 from ingvagabund/max-pods-per-node
Max pods per node
2018-03-22 09:04:01 -04:00
Jan Chaloupka
f7d0acb731 Regen 2018-03-22 12:40:23 +01:00
ravisantoshgudimetla
f1f8b2eaa7 Limit maximum number of pods to be evicted per node 2018-03-22 12:40:19 +01:00
Avesh Agarwal
0a7f14d75e Merge pull request #31 from ravisantoshgudimetla/e2e_with_travis_gcloud
E2E using gce first pass
2018-03-19 12:02:50 -04:00
ravisantoshgudimetla
de76f9b14c E2E using gce first pass 2018-03-19 11:34:17 -04:00
RaviSantosh Gudimetla
8b84bb26ff Merge pull request #56 from containscafeine/add-RemovePodsViolatingNodeAffinity-required
add requiredDuringSchedulingIgnoredDuringExecution for RemovePodsViolatingNodeAffinity strategy
2018-02-19 12:11:21 -05:00
Shubham Minglani
bb25192163 add generated files 2018-02-19 16:21:04 +05:30
Shubham Minglani
40bb490f4c add RemovePodsViolatingNodeAffinity strategy
This commit adds requiredDuringSchedulingIgnoredDuringExecution
for RemovePodsViolatingNodeAffinity strategy.

Also adds unit tests and documentation.
2018-02-19 16:20:59 +05:30
Avesh Agarwal
08729f6ef9 Merge pull request #79 from jelmersnoeck/multi-stage-builds
Scratch image + multistage builds.
2018-02-16 12:40:25 -05:00
Jelmer Snoeck
3dd7de8132 Use docker target in README. 2018-02-16 18:30:49 +01:00
Jelmer Snoeck
471aeb5ea4 Set up multistage Docker build.
By setting up a multistage Docker build, we can create the container
image in a single command. This eliminates external setup and allows us
to build this automatically on registries.
2018-02-16 18:30:48 +01:00
Jelmer Snoeck
65e7093ee7 Use scratch image for container.
By disabling CGO, we can use the `scratch` image instead of the `fedora`
image, allowing a lighter weight image.
2018-02-16 08:50:22 +01:00
RaviSantosh Gudimetla
fc0cd4ba30 Merge pull request #78 from ravisantoshgudimetla/schedulable-nodes
Descheduler should not run when cluster size is 1
2018-02-13 17:40:18 -05:00
ravisantoshgudimetla
ba3eac6c57 Descheduler should not run when cluster size is 1 2018-02-08 21:15:02 -05:00
Avesh Agarwal
11a95ce8fb Merge pull request #71 from ravisantoshgudimetla/release-table
Compatibility matrix for kube version
2018-01-29 08:52:59 -05:00
ravisantoshgudimetla
29a9fc6b56 Compatibility matrix for kube versions 2018-01-13 22:25:40 +05:30
Avesh Agarwal
d3c2f25685 Merge pull request #73 from aveshagarwal/master-rebase-kube-1-9
Rebase to kube 1 9
2018-01-09 13:00:42 -05:00
Avesh Agarwal
e858c9ee80 Fix glog issue: ERROR: logging before flag.Parse. 2018-01-09 12:54:52 -05:00
Avesh Agarwal
44752e5e83 Update code and hack dir for kube 1.9 rebase. 2018-01-09 12:41:01 -05:00
Avesh Agarwal
7123f30783 Update auto-generated code. 2018-01-09 11:04:26 -05:00
Avesh Agarwal
a82cf7cea4 Update verify-gofmt for go1.9. 2018-01-09 11:04:26 -05:00
Avesh Agarwal
72318868b0 Update vendor dir for kube 1.9 rebase. 2018-01-09 00:58:09 -05:00
Avesh Agarwal
589fb95236 Update glide files for kube 1.9 rebase. 2018-01-09 00:57:56 -05:00
Avesh Agarwal
7df543d137 Merge pull request #70 from ravisantoshgudimetla/pod-antiaffinity-readme-update
Read me updated with changes for pod anti-affinity
2018-01-08 15:04:59 -05:00
Avesh Agarwal
1d7f429ba1 Merge pull request #72 from ravisantoshgudimetla/owner-ref-switch
Owner ref switch
2018-01-08 15:03:10 -05:00
ravisantoshgudimetla
bf29a6073f Owner ref switch
Signed-off-by: ravisantoshgudimetla <ravisantoshgudimetla@gmail.com>
2018-01-05 00:22:12 +05:30
ravisantoshgudimetla
65635bdb2e Read me updated with changes for pod anti-affinity 2018-01-03 12:08:15 +05:30
Avesh Agarwal
fd961557d0 Merge pull request #61 from ravisantoshgudimetla/latency-sensitive-pods
Make GPU requested pods not evictable by descheduler
2018-01-02 16:43:27 -05:00
Avesh Agarwal
c29bdc1dbe Merge pull request #64 from ravisantoshgudimetla/fix-serviceaccount
Fixes the serviceaccount issue by adding new eviction resources
2018-01-02 16:40:36 -05:00
RaviSantosh Gudimetla
ffecc54bf5 Merge pull request #68 from ravisantoshgudimetla/go_report
Go report included
2017-12-31 19:40:09 +05:30
ravisantoshgudimetla
f6f6fbab10 Go report included 2017-12-31 19:34:06 +05:30
RaviSantosh Gudimetla
12c217477c Merge pull request #67 from ravisantoshgudimetla/build-badge
Build status badge
2017-12-29 19:11:47 +05:30
ravisantoshgudimetla
20a4798465 Build status badge 2017-12-29 19:09:17 +05:30
RaviSantosh Gudimetla
a201f222e5 Merge pull request #66 from spiffxp/update-code-of-conduct
Update code-of-conduct.md /cc @aveshagarwal
2017-12-26 11:54:32 +05:30
Aaron Crickenberger
6e705fde85 Update code-of-conduct.md
Refer to kubernetes/community as authoritative source for code of conduct
2017-12-20 14:11:52 -05:00
ravisantoshgudimetla
5763554be4 Fixes the serviceaccount issue by adding new eviction resources 2017-12-14 15:13:33 +05:30
ravisantoshgudimetla
6f873d5e69 Make GPU requested pods not evictable by descheduler 2017-12-05 03:49:47 -05:00
Avesh Agarwal
445ae92caa Merge pull request #59 from aveshagarwal/master-fix-errors
Update logging and a few fixes.
2017-12-01 13:19:36 -05:00
Avesh Agarwal
8f3c0cf4b8 Merge pull request #58 from aveshagarwal/master-fix-node-lister
Fix node lister by allowing its reflector to have enough time so that listing works.
2017-12-01 13:02:17 -05:00
Avesh Agarwal
2a280f9a20 Update logging and a few fixes. 2017-12-01 13:02:05 -05:00
Avesh Agarwal
0503f53904 Merge pull request #57 from aveshagarwal/master-fix-pod-listing
Fix field selector to only list non-terminated pods.
2017-12-01 12:58:50 -05:00
Avesh Agarwal
e0a9dfcb76 Fix field selector to only list non-terminated pods. 2017-12-01 12:50:39 -05:00
Avesh Agarwal
5db49f2ce1 Fix node lister by allowing its reflector to have enough time so that listing works.
Currently time duration is chosen randomly.
2017-11-30 12:28:33 -05:00
Avesh Agarwal
582bd67681 Merge pull request #49 from ravisantoshgudimetla/podlist-refactor
Small refactor to accomodate descheduling only evictable pods
2017-11-29 15:48:04 -05:00
ravisantoshgudimetla
a63f815116 Small refactor to accomodate descheduling only evictable pods
Signed-off-by: ravisantoshgudimetla <ravisantoshgudimetla@gmail.com>
2017-11-29 15:34:28 -05:00
Avesh Agarwal
afc17a62ea Merge pull request #45 from ravisantoshgudimetla/lownodeutilization-fix#43
Changes to fix low node utilization strategy
2017-11-27 18:47:42 -05:00
ravisantoshgudimetla
6dbc8a1fcc Changes to fix low node utilization strategy 2017-11-27 18:40:53 -05:00
RaviSantosh Gudimetla
d2bd16a12d Merge pull request #41 from containscafeine/error-out-on-unsupported-resource-names
Validate resource names to pass to thresholds
2017-11-27 12:35:30 -05:00
Avesh Agarwal
955d0eb228 Merge pull request #36 from ravisantoshgudimetla/flag-fix
kubeconfig file correction
2017-11-27 11:15:27 -05:00
Shubham Minglani
a490726245 Validate resource names to pass to thresholds
This commit adds checks to only allow valid resource names for the
thresholds field, which are "cpu", "memory", and "pods" right now.

For the other valid or invalid resource names, descheduler will now
throw an error.

Also, tests have been added to test the added behavior.

fix #40
2017-11-27 19:54:53 +05:30
RaviSantosh Gudimetla
16a504fb87 Merge pull request #44 from containscafeine/log-underutilized-and-target-nodes
Add log messages for target, underutilized nodes
2017-11-26 11:55:26 -05:00
Shubham Minglani
d54b73a6ba Add log messages for target, underutilized nodes
This commit adds log messages to be printed whenever a node is
identified as a target node from which the pods can be evicted or
as an underutilized node to which the evicted pods can be
scheduled, if at all.

This should help with debugging as well as with letting the user
know the identified nodes.
2017-11-25 19:49:05 +05:30
ravisantoshgudimetla
344dc0f3c2 kubeconfig file correction 2017-11-20 22:40:15 -05:00
18136 changed files with 2094630 additions and 1562242 deletions

View File

@@ -1,7 +1,7 @@
language: go language: go
go: go:
- 1.8.3 - 1.9.1
script: script:
- hack/verify-gofmt.sh - hack/verify-gofmt.sh
- make build - make build
- make test - make test-unit

23
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,23 @@
# Contributing Guidelines
Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt:
_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._
## Getting Started
We have full documentation on how to get started contributing here:
- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests
- [Kubernetes Contributor Guide](http://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](http://git.k8s.io/community/contributors/guide#contributing)
- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet.md) - Common resources for existing developers
## Mentorship
- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers!
## Contact Information
- [Slack channel](https://kubernetes.slack.com/messages/sig-scheduling)
- [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-scheduling)

View File

@@ -11,10 +11,16 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # 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> MAINTAINER Avesh Agarwal <avagarwa@redhat.com>
COPY _output/bin/descheduler /bin/descheduler COPY --from=0 /go/src/github.com/kubernetes-incubator/descheduler/_output/bin/descheduler /bin/descheduler
CMD ["/bin/descheduler --help"]
CMD ["/bin/descheduler", "--help"]

20
Dockerfile.dev Normal file
View 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"]

View File

@@ -30,13 +30,25 @@ IMAGE:=descheduler:$(VERSION)
all: build all: build
build: build:
go build ${LDFLAGS} -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) . docker build -t $(IMAGE) .
clean: clean:
rm -rf _output rm -rf _output
test: test-unit:
./test/run-unit-tests.sh ./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

View File

@@ -1,3 +1,6 @@
[![Build Status](https://travis-ci.org/kubernetes-incubator/descheduler.svg?branch=master)](https://travis-ci.org/kubernetes-incubator/descheduler)
[![Go Report Card](https://goreportcard.com/badge/github.com/kubernetes-incubator/descheduler)](https://goreportcard.com/report/github.com/kubernetes-incubator/descheduler)
# Descheduler for Kubernetes # Descheduler for Kubernetes
## Introduction ## Introduction
@@ -53,10 +56,20 @@ in `kube-system` namespace.
First we create a simple Docker image utilizing the Dockerfile found in the root directory: 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 $ make image
``` ```
This eliminates the need to have Go installed locally and builds the binary
within it's own container.
### Create a cluster role ### Create a cluster role
To give necessary permissions for the descheduler to work in a pod, create a cluster role: To give necessary permissions for the descheduler to work in a pod, create a cluster role:
@@ -74,6 +87,9 @@ rules:
- apiGroups: [""] - apiGroups: [""]
resources: ["pods"] resources: ["pods"]
verbs: ["get", "watch", "list", "delete"] verbs: ["get", "watch", "list", "delete"]
- apiGroups: [""]
resources: ["pods/eviction"]
verbs: ["create"]
EOF EOF
``` ```
@@ -114,7 +130,7 @@ spec:
metadata: metadata:
name: descheduler-pod name: descheduler-pod
annotations: annotations:
scheduler.alpha.kubernetes.io/critical-pod: "true" scheduler.alpha.kubernetes.io/critical-pod: ""
spec: spec:
containers: containers:
- name: descheduler - name: descheduler
@@ -122,11 +138,7 @@ spec:
volumeMounts: volumeMounts:
- mountPath: /policy-dir - mountPath: /policy-dir
name: policy-volume name: policy-volume
command: command: ["/bin/descheduler", "--policy-config-file", "/policy-dir/policy.yaml"]
- "/bin/sh"
- "-ec"
- |
/bin/descheduler --policy-config-file /policy-dir/policy.yaml
restartPolicy: "Never" restartPolicy: "Never"
serviceAccountName: descheduler-sa serviceAccountName: descheduler-sa
volumes: volumes:
@@ -146,7 +158,7 @@ $ kubectl create -f descheduler-job.yaml
## Policy and Strategies ## Policy and Strategies
Descheduler's policy is configurable and includes strategies to be enabled or disabled. 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. As part of the policy, the parameters associated with the strategies can be configured too.
By default, all strategies are enabled. By default, all strategies are enabled.
@@ -158,7 +170,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 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 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 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" apiVersion: "descheduler/v1alpha1"
@@ -210,6 +222,33 @@ 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 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. 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 ## Pod Evictions
When the descheduler decides to evict pods from a node, it employs following general mechanism: When the descheduler decides to evict pods from a node, it employs following general mechanism:
@@ -229,10 +268,8 @@ disruption budget (PDB). The pods are evicted by using eviction subresource to h
This roadmap is not in any particular order. 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 * 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 pod life time
* Strategy to consider number of pending pods * Strategy to consider number of pending pods
* Integration with cluster autoscaler * Integration with cluster autoscaler
@@ -240,8 +277,22 @@ This roadmap is not in any particular order.
* Consideration of Kubernetes's scheduler's predicates * Consideration of Kubernetes's scheduler's predicates
## Note ## Compatibility matrix
This project is under active development, and is not intended for production use. Descheduler | supported Kubernetes version
Any api could be changed any time with out any notice. That said, your feedback is -------------|-----------------------------
very important and appreciated to make this project more stable and useful. 0.4+ | 1.9+
0.1-0.3 | 1.7-1.8
## Community, discussion, contribution, and support
Learn how to engage with the Kubernetes community on the [community page](http://kubernetes.io/community/).
You can reach the maintainers of this project at:
- [Slack channel](https://kubernetes.slack.com/messages/sig-scheduling)
- [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-scheduling)
### Code of conduct
Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).

14
SECURITY_CONTACTS Normal file
View 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

View File

@@ -18,7 +18,7 @@ limitations under the License.
package options package options
import ( 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 // install the componentconfig api so we get its defaulting and conversion functions
"github.com/kubernetes-incubator/descheduler/pkg/apis/componentconfig" "github.com/kubernetes-incubator/descheduler/pkg/apis/componentconfig"
@@ -50,9 +50,13 @@ func NewDeschedulerServer() *DeschedulerServer {
// AddFlags adds flags for a specific SchedulerServer to the specified FlagSet // AddFlags adds flags for a specific SchedulerServer to the specified FlagSet
func (rs *DeschedulerServer) AddFlags(fs *pflag.FlagSet) { func (rs *DeschedulerServer) AddFlags(fs *pflag.FlagSet) {
fs.DurationVar(&rs.DeschedulingInterval, "descheduling-interval", rs.DeschedulingInterval, "time interval between two consecutive descheduler executions") 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.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.") 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 // 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)") 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")
// evict-local-storage-pods allows eviction of pods that are using local storage. This is false by default.
fs.BoolVar(&rs.EvictLocalStoragePods, "evict-local-storage-pods", rs.EvictLocalStoragePods, "Enables evicting pods using local storage by descheduler")
} }

View File

@@ -17,15 +17,18 @@ limitations under the License.
package main package main
import ( import (
"flag"
"fmt" "fmt"
"github.com/kubernetes-incubator/descheduler/cmd/descheduler/app"
"os" "os"
"github.com/kubernetes-incubator/descheduler/cmd/descheduler/app"
) )
func main() { func main() {
out := os.Stdout out := os.Stdout
cmd := app.NewDeschedulerCommand(out) cmd := app.NewDeschedulerCommand(out)
cmd.AddCommand(app.NewVersionCommand()) cmd.AddCommand(app.NewVersionCommand())
flag.CommandLine.Parse([]string{})
if err := cmd.Execute(); err != nil { if err := cmd.Execute(); err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@@ -1,58 +1,3 @@
## Kubernetes Community Code of Conduct # Kubernetes Community Code of Conduct
### Contributor Code of Conduct Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)
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
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/code-of-conduct.md?pixel)]()

View File

@@ -0,0 +1,8 @@
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"RemovePodsViolatingNodeAffinity":
enabled: true
params:
nodeAffinityType:
- "requiredDuringSchedulingIgnoredDuringExecution"

376
glide.lock generated
View File

@@ -1,44 +1,83 @@
hash: 6ccf8e8213eb31f9dd31b46c3aa3c2c01929c6230fb049cfabcabd498ade9c30 hash: ed51a8e643db6e9996ef0ffca671fb31ab5b7fe0d61ecdda828192871f9da366
updated: 2017-10-16T14:31:20.353977552-04:00 updated: 2018-05-22T18:05:00.26435-07:00
imports: 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 - name: github.com/davecgh/go-spew
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d version: 782f4967f2dc4564575ca782fe2d04090b5faca8
subpackages: subpackages:
- spew - spew
- name: github.com/dgrijalva/jwt-go
version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20
- name: github.com/docker/distribution - name: github.com/docker/distribution
version: cd27f179f2c10c5d300e6d09025b538c475b0d51 version: edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c
subpackages: subpackages:
- digest - digestset
- reference - reference
- name: github.com/emicklei/go-restful - name: github.com/emicklei/go-restful
version: ff4f55a206334ef123e4f79bbf348980da81ca46 version: ff4f55a206334ef123e4f79bbf348980da81ca46
subpackages: subpackages:
- log - log
- name: github.com/emicklei/go-restful-swagger12
version: dcef7f55730566d41eae5db10e7d6981829720f6
- name: github.com/ghodss/yaml - name: github.com/ghodss/yaml
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
- name: github.com/go-openapi/analysis
version: b44dc874b601d9e4e2f6e19140e794ba24bead3b
- name: github.com/go-openapi/jsonpointer - name: github.com/go-openapi/jsonpointer
version: 46af16f9f7b149af66e5d1bd010e3574dc06de98 version: 46af16f9f7b149af66e5d1bd010e3574dc06de98
- name: github.com/go-openapi/jsonreference - name: github.com/go-openapi/jsonreference
version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272 version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272
- name: github.com/go-openapi/loads
version: 18441dfa706d924a39a030ee2c3b1d8d81917b38
- name: github.com/go-openapi/spec - name: github.com/go-openapi/spec
version: 6aced65f8501fe1217321abf0749d354824ba2ff version: 7abd5745472fff5eb3685386d5fb8bf38683154d
- name: github.com/go-openapi/swag - name: github.com/go-openapi/swag
version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72 version: f3f9494671f93fcff853e3c6e9e948b3eb71e590
- name: github.com/gogo/protobuf - name: github.com/gogo/protobuf
version: c0656edd0d9eab7c66d1eb0c568f9039345796f7 version: c0656edd0d9eab7c66d1eb0c568f9039345796f7
subpackages: subpackages:
- proto - proto
- sortkeys - sortkeys
- name: github.com/golang/glog - 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 - name: github.com/google/gofuzz
version: 44d81051d367757e1c7c6a5a86423ece9afcf63c 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 - name: github.com/hashicorp/golang-lru
version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4 version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
subpackages: subpackages:
@@ -49,16 +88,22 @@ imports:
version: 6633656539c1639d9d78127b7d47c622b5d7b6dc version: 6633656539c1639d9d78127b7d47c622b5d7b6dc
- name: github.com/inconshreveable/mousetrap - name: github.com/inconshreveable/mousetrap
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
- name: github.com/json-iterator/go
version: 36b14963da70d11297d313183d7e6388c8510e1e
- name: github.com/juju/ratelimit - name: github.com/juju/ratelimit
version: 5b9ff866471762aa2ab2dced63c9fb6f53921342 version: 5b9ff866471762aa2ab2dced63c9fb6f53921342
- name: github.com/kubernetes/repo-infra - name: github.com/kubernetes/repo-infra
version: f521b5d472e00e05da5394994942064510a6e8bf version: dbcbd7624d5e4eb29f33c48edf1b1651809827a3
- name: github.com/mailru/easyjson - name: github.com/mailru/easyjson
version: d5b7844b561a7bc640052f1b935f7b800330d7e0 version: 2f5df55504ebc322e4d52d34df6a1f5b503bf26d
subpackages: subpackages:
- buffer - buffer
- jlexer - jlexer
- jwriter - jwriter
- name: github.com/opencontainers/go-digest
version: a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
- name: github.com/peterbourgon/diskv
version: 5f041e8faa004a95c88a202771f4cc3e991971e6
- name: github.com/PuerkitoBio/purell - name: github.com/PuerkitoBio/purell
version: 8a290539e2e8629dbc4e6bad948158f790ec31f4 version: 8a290539e2e8629dbc4e6bad948158f790ec31f4
- name: github.com/PuerkitoBio/urlesc - name: github.com/PuerkitoBio/urlesc
@@ -66,13 +111,13 @@ imports:
- name: github.com/spf13/cobra - name: github.com/spf13/cobra
version: f62e98d28ab7ad31d707ba837a966378465c7b57 version: f62e98d28ab7ad31d707ba837a966378465c7b57
- name: github.com/spf13/pflag - name: github.com/spf13/pflag
version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7 version: 4c012f6dcd9546820e378d0bdda4d8fc772cdfea
- name: github.com/ugorji/go - name: github.com/ugorji/go
version: ded73eae5db7e7a0ef6f55aace87a2873c5d2b74 version: f57d8945648dbfe4c332cff9c50fb57548958e3f
subpackages: subpackages:
- codec - codec
- name: golang.org/x/crypto - name: golang.org/x/crypto
version: d172538b2cfce0c13cee31e647d0367aa8cd2486 version: 81e90905daefcd6fd217b62423c0908922eadb30
subpackages: subpackages:
- bcrypt - bcrypt
- blowfish - blowfish
@@ -81,9 +126,10 @@ imports:
- salsa20/salsa - salsa20/salsa
- ssh/terminal - ssh/terminal
- name: golang.org/x/net - name: golang.org/x/net
version: f2499483f923065a842d38eb4c7f1927e6fc6e6d version: 1c05540f6879653db88113bc4a2b70aec4bd491f
subpackages: subpackages:
- context - context
- context/ctxhttp
- html - html
- html/atom - html/atom
- http2 - http2
@@ -93,14 +139,23 @@ imports:
- lex/httplex - lex/httplex
- trace - trace
- websocket - websocket
- name: golang.org/x/oauth2
version: a6bd8cefa1811bd24b86f8902872e4e8225f74c4
subpackages:
- google
- internal
- jws
- jwt
- name: golang.org/x/sys - name: golang.org/x/sys
version: 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9 version: 95c6576299259db960f6c5b9b69ea52422860fce
subpackages: subpackages:
- unix - unix
- windows
- name: golang.org/x/text - name: golang.org/x/text
version: 2910a502d2bf9e43193af9d68ca516529614eed3 version: b19bf474d317b857955b12035d2c5acb57ce8b01
subpackages: subpackages:
- cases - cases
- internal
- internal/tag - internal/tag
- language - language
- runes - runes
@@ -110,29 +165,80 @@ imports:
- unicode/bidi - unicode/bidi
- unicode/norm - unicode/norm
- width - 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 - name: gopkg.in/inf.v0
version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
- name: gopkg.in/yaml.v2 - name: gopkg.in/yaml.v2
version: 53feefa2559fb8dfa8d81baad31be332c97d6c77 version: 53feefa2559fb8dfa8d81baad31be332c97d6c77
- name: k8s.io/apimachinery - name: k8s.io/api
version: 917740426ad66ff818da4809990480bcc0786a77 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: subpackages:
- pkg/api/equality
- pkg/api/errors - pkg/api/errors
- pkg/api/meta - pkg/api/meta
- pkg/api/resource - pkg/api/resource
- pkg/apimachinery - pkg/apimachinery
- pkg/apimachinery/announced - pkg/apimachinery/announced
- pkg/apimachinery/registered - pkg/apimachinery/registered
- pkg/apis/meta/internalversion
- pkg/apis/meta/v1 - pkg/apis/meta/v1
- pkg/apis/meta/v1/unstructured - pkg/apis/meta/v1/unstructured
- pkg/apis/meta/v1alpha1 - pkg/apis/meta/v1alpha1
- pkg/conversion - pkg/conversion
- pkg/conversion/queryparams - pkg/conversion/queryparams
- pkg/conversion/unstructured
- pkg/fields - pkg/fields
- pkg/labels - pkg/labels
- pkg/openapi
- pkg/runtime - pkg/runtime
- pkg/runtime/schema - pkg/runtime/schema
- pkg/runtime/serializer - pkg/runtime/serializer
@@ -151,7 +257,6 @@ imports:
- pkg/util/intstr - pkg/util/intstr
- pkg/util/json - pkg/util/json
- pkg/util/net - pkg/util/net
- pkg/util/rand
- pkg/util/runtime - pkg/util/runtime
- pkg/util/sets - pkg/util/sets
- pkg/util/validation - pkg/util/validation
@@ -162,56 +267,87 @@ imports:
- pkg/watch - pkg/watch
- third_party/forked/golang/reflect - third_party/forked/golang/reflect
- name: k8s.io/apiserver - name: k8s.io/apiserver
version: a7f02eb8e3920e446965036c9610ec52a7ede92f version: 91e14f394e4796abf5a994a349a222e7081d86b6
subpackages: subpackages:
- pkg/features
- pkg/util/feature
- pkg/util/flag - pkg/util/flag
- pkg/util/logs - pkg/util/logs
- name: k8s.io/client-go - name: k8s.io/client-go
version: ec52d278b25c8fef82a965d93afdc74771ea6963 version: 78700dec6369ba22221b72770783300f143df150
subpackages: subpackages:
- discovery - discovery
- discovery/fake - discovery/fake
- kubernetes
- kubernetes/fake
- kubernetes/scheme - kubernetes/scheme
- pkg/api - kubernetes/typed/admissionregistration/v1alpha1
- pkg/api/v1 - kubernetes/typed/admissionregistration/v1alpha1/fake
- pkg/apis/admissionregistration - kubernetes/typed/admissionregistration/v1beta1
- pkg/apis/admissionregistration/v1alpha1 - kubernetes/typed/admissionregistration/v1beta1/fake
- pkg/apis/apps - kubernetes/typed/apps/v1
- pkg/apis/apps/v1beta1 - kubernetes/typed/apps/v1/fake
- pkg/apis/authentication - kubernetes/typed/apps/v1beta1
- pkg/apis/authentication/v1 - kubernetes/typed/apps/v1beta1/fake
- pkg/apis/authentication/v1beta1 - kubernetes/typed/apps/v1beta2
- pkg/apis/authorization - kubernetes/typed/apps/v1beta2/fake
- pkg/apis/authorization/v1 - kubernetes/typed/authentication/v1
- pkg/apis/authorization/v1beta1 - kubernetes/typed/authentication/v1/fake
- pkg/apis/autoscaling - kubernetes/typed/authentication/v1beta1
- pkg/apis/autoscaling/v1 - kubernetes/typed/authentication/v1beta1/fake
- pkg/apis/autoscaling/v2alpha1 - kubernetes/typed/authorization/v1
- pkg/apis/batch - kubernetes/typed/authorization/v1/fake
- pkg/apis/batch/v1 - kubernetes/typed/authorization/v1beta1
- pkg/apis/batch/v2alpha1 - kubernetes/typed/authorization/v1beta1/fake
- pkg/apis/certificates - kubernetes/typed/autoscaling/v1
- pkg/apis/certificates/v1beta1 - kubernetes/typed/autoscaling/v1/fake
- pkg/apis/extensions - kubernetes/typed/autoscaling/v2beta1
- pkg/apis/extensions/v1beta1 - kubernetes/typed/autoscaling/v2beta1/fake
- pkg/apis/networking - kubernetes/typed/batch/v1
- pkg/apis/networking/v1 - kubernetes/typed/batch/v1/fake
- pkg/apis/policy - kubernetes/typed/batch/v1beta1
- pkg/apis/policy/v1beta1 - kubernetes/typed/batch/v1beta1/fake
- pkg/apis/rbac - kubernetes/typed/batch/v2alpha1
- pkg/apis/rbac/v1alpha1 - kubernetes/typed/batch/v2alpha1/fake
- pkg/apis/rbac/v1beta1 - kubernetes/typed/certificates/v1beta1
- pkg/apis/settings - kubernetes/typed/certificates/v1beta1/fake
- pkg/apis/settings/v1alpha1 - kubernetes/typed/core/v1
- pkg/apis/storage - kubernetes/typed/core/v1/fake
- pkg/apis/storage/v1 - kubernetes/typed/events/v1beta1
- pkg/apis/storage/v1beta1 - kubernetes/typed/events/v1beta1/fake
- pkg/util - kubernetes/typed/extensions/v1beta1
- pkg/util/parsers - 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 - 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
- rest/watch - rest/watch
- testing - testing
- third_party/forked/golang/template
- tools/auth - tools/auth
- tools/cache - tools/cache
- tools/clientcmd - tools/clientcmd
@@ -219,27 +355,45 @@ imports:
- tools/clientcmd/api/latest - tools/clientcmd/api/latest
- tools/clientcmd/api/v1 - tools/clientcmd/api/v1
- tools/metrics - tools/metrics
- tools/pager
- tools/reference
- transport - transport
- util/buffer
- util/cert - util/cert
- util/flowcontrol - util/flowcontrol
- util/homedir - util/homedir
- util/integer - util/integer
- util/jsonpath
- name: k8s.io/code-generator
version: fef8bcdbaf36ac6a1a18c9ef7d85200b249fad30
- name: k8s.io/gengo - name: k8s.io/gengo
version: c79c13d131b0a8f42d05faa6491c12e94ccc6f30 version: 1ef560bbde5195c01629039ad3b337ce63e7b321
- name: k8s.io/kubernetes - name: k8s.io/kube-openapi
version: 4bc5e7f9a6c25dc4c03d4d656f2cefd21540e28c version: 39a7bf85c140f972372c2a0d1ee40adbf0c8bfe1
subpackages: subpackages:
- pkg/api - pkg/builder
- pkg/api/install - pkg/common
- pkg/api/v1 - pkg/handler
- pkg/api/v1/helper/qos - pkg/util
- pkg/api/v1/ref - pkg/util/proto
- name: k8s.io/kubernetes
version: 925c127ec6b946659ad0fd596fa959be43f0cc05
subpackages:
- pkg/api/legacyscheme
- pkg/api/testapi
- pkg/api/v1/resource - pkg/api/v1/resource
- pkg/apis/admission
- pkg/apis/admission/install
- pkg/apis/admission/v1beta1
- pkg/apis/admissionregistration - pkg/apis/admissionregistration
- pkg/apis/admissionregistration/install
- pkg/apis/admissionregistration/v1alpha1 - pkg/apis/admissionregistration/v1alpha1
- pkg/apis/admissionregistration/v1beta1
- pkg/apis/apps - pkg/apis/apps
- pkg/apis/apps/install - pkg/apis/apps/install
- pkg/apis/apps/v1
- pkg/apis/apps/v1beta1 - pkg/apis/apps/v1beta1
- pkg/apis/apps/v1beta2
- pkg/apis/authentication - pkg/apis/authentication
- pkg/apis/authentication/install - pkg/apis/authentication/install
- pkg/apis/authentication/v1 - pkg/apis/authentication/v1
@@ -251,78 +405,60 @@ imports:
- pkg/apis/autoscaling - pkg/apis/autoscaling
- pkg/apis/autoscaling/install - pkg/apis/autoscaling/install
- pkg/apis/autoscaling/v1 - pkg/apis/autoscaling/v1
- pkg/apis/autoscaling/v2alpha1 - pkg/apis/autoscaling/v2beta1
- pkg/apis/batch - pkg/apis/batch
- pkg/apis/batch/install - pkg/apis/batch/install
- pkg/apis/batch/v1 - pkg/apis/batch/v1
- pkg/apis/batch/v1beta1
- pkg/apis/batch/v2alpha1 - pkg/apis/batch/v2alpha1
- pkg/apis/certificates - pkg/apis/certificates
- pkg/apis/certificates/install - pkg/apis/certificates/install
- pkg/apis/certificates/v1beta1 - 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
- pkg/apis/extensions/install - pkg/apis/extensions/install
- pkg/apis/extensions/v1beta1 - pkg/apis/extensions/v1beta1
- pkg/apis/imagepolicy
- pkg/apis/imagepolicy/install
- pkg/apis/imagepolicy/v1alpha1
- pkg/apis/networking - pkg/apis/networking
- pkg/apis/networking/install
- pkg/apis/networking/v1 - pkg/apis/networking/v1
- pkg/apis/policy - pkg/apis/policy
- pkg/apis/policy/install - pkg/apis/policy/install
- pkg/apis/policy/v1beta1 - pkg/apis/policy/v1beta1
- pkg/apis/rbac - pkg/apis/rbac
- pkg/apis/rbac/install - pkg/apis/rbac/install
- pkg/apis/rbac/v1
- pkg/apis/rbac/v1alpha1 - pkg/apis/rbac/v1alpha1
- pkg/apis/rbac/v1beta1 - pkg/apis/rbac/v1beta1
- pkg/apis/scheduling
- pkg/apis/scheduling/install
- pkg/apis/scheduling/v1alpha1
- pkg/apis/settings - pkg/apis/settings
- pkg/apis/settings/install - pkg/apis/settings/install
- pkg/apis/settings/v1alpha1 - pkg/apis/settings/v1alpha1
- pkg/apis/storage - pkg/apis/storage
- pkg/apis/storage/install - pkg/apis/storage/install
- pkg/apis/storage/v1 - pkg/apis/storage/v1
- pkg/apis/storage/v1alpha1
- pkg/apis/storage/v1beta1 - pkg/apis/storage/v1beta1
- pkg/client/clientset_generated/clientset - pkg/features
- pkg/client/clientset_generated/clientset/fake - pkg/kubelet/apis
- pkg/client/clientset_generated/clientset/scheme
- pkg/client/clientset_generated/clientset/typed/admissionregistration/v1alpha1
- pkg/client/clientset_generated/clientset/typed/admissionregistration/v1alpha1/fake
- pkg/client/clientset_generated/clientset/typed/apps/v1beta1
- pkg/client/clientset_generated/clientset/typed/apps/v1beta1/fake
- pkg/client/clientset_generated/clientset/typed/authentication/v1
- pkg/client/clientset_generated/clientset/typed/authentication/v1/fake
- pkg/client/clientset_generated/clientset/typed/authentication/v1beta1
- pkg/client/clientset_generated/clientset/typed/authentication/v1beta1/fake
- pkg/client/clientset_generated/clientset/typed/authorization/v1
- pkg/client/clientset_generated/clientset/typed/authorization/v1/fake
- pkg/client/clientset_generated/clientset/typed/authorization/v1beta1
- pkg/client/clientset_generated/clientset/typed/authorization/v1beta1/fake
- pkg/client/clientset_generated/clientset/typed/autoscaling/v1
- pkg/client/clientset_generated/clientset/typed/autoscaling/v1/fake
- pkg/client/clientset_generated/clientset/typed/autoscaling/v2alpha1
- pkg/client/clientset_generated/clientset/typed/autoscaling/v2alpha1/fake
- pkg/client/clientset_generated/clientset/typed/batch/v1
- pkg/client/clientset_generated/clientset/typed/batch/v1/fake
- pkg/client/clientset_generated/clientset/typed/batch/v2alpha1
- pkg/client/clientset_generated/clientset/typed/batch/v2alpha1/fake
- pkg/client/clientset_generated/clientset/typed/certificates/v1beta1
- pkg/client/clientset_generated/clientset/typed/certificates/v1beta1/fake
- pkg/client/clientset_generated/clientset/typed/core/v1
- pkg/client/clientset_generated/clientset/typed/core/v1/fake
- pkg/client/clientset_generated/clientset/typed/extensions/v1beta1
- pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/fake
- pkg/client/clientset_generated/clientset/typed/networking/v1
- pkg/client/clientset_generated/clientset/typed/networking/v1/fake
- pkg/client/clientset_generated/clientset/typed/policy/v1beta1
- pkg/client/clientset_generated/clientset/typed/policy/v1beta1/fake
- pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1
- pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1/fake
- pkg/client/clientset_generated/clientset/typed/rbac/v1beta1
- pkg/client/clientset_generated/clientset/typed/rbac/v1beta1/fake
- pkg/client/clientset_generated/clientset/typed/settings/v1alpha1
- pkg/client/clientset_generated/clientset/typed/settings/v1alpha1/fake
- pkg/client/clientset_generated/clientset/typed/storage/v1
- pkg/client/clientset_generated/clientset/typed/storage/v1/fake
- pkg/client/clientset_generated/clientset/typed/storage/v1beta1
- pkg/client/clientset_generated/clientset/typed/storage/v1beta1/fake
- pkg/client/listers/core/v1
- pkg/kubelet/types - pkg/kubelet/types
- pkg/util - pkg/master/ports
- pkg/util/parsers - pkg/util/parsers
- pkg/util/pointer
- plugin/pkg/scheduler/algorithm/priorities/util
testImports: [] testImports: []

View File

@@ -1,15 +1,25 @@
package: github.com/kubernetes-incubator/descheduler package: github.com/kubernetes-incubator/descheduler
import: import:
- package: k8s.io/client-go - package: k8s.io/client-go
version: ec52d278b25c8fef82a965d93afdc74771ea6963 version: 78700dec6369ba22221b72770783300f143df150
- package: k8s.io/api
version: af4bc157c3a209798fc897f6d4aaaaeb6c2e0d6a
- package: k8s.io/apiserver - package: k8s.io/apiserver
version: release-1.7 version: 91e14f394e4796abf5a994a349a222e7081d86b6
- package: k8s.io/apimachinery - package: k8s.io/apimachinery
version: 917740426ad66ff818da4809990480bcc0786a77 version: 180eddb345a5be3a157cea1c624700ad5bd27b8f
- package: k8s.io/kubernetes - package: k8s.io/kubernetes
version: 1.7.6 version: v1.9.0
- package: k8s.io/code-generator
version: kubernetes-1.9.0
- package: github.com/kubernetes/repo-infra - package: github.com/kubernetes/repo-infra
- package: github.com/spf13/cobra - package: github.com/spf13/cobra
version: f62e98d28ab7ad31d707ba837a966378465c7b57 version: f62e98d28ab7ad31d707ba837a966378465c7b57
- package: k8s.io/gengo - 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

View 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.13.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_private_ip=$(gcloud compute instances list | grep $master_uuid|awk '{print $4}')
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_private_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

View 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
View 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

View 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

View File

@@ -43,5 +43,5 @@ OS_ROOT="$( os::util::absolute_path "${init_source}" )"
export OS_ROOT export OS_ROOT
cd "${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" OS_OUTPUT_BINPATH="${OS_ROOT}/_output/bin"

View File

@@ -40,7 +40,7 @@ generated_files=($(
# We only work for deps within this prefix. # We only work for deps within this prefix.
#my_prefix="k8s.io/kubernetes" #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 # Register function to be called on EXIT to remove codecgen
# binary and also to touch the files that should be regenerated # binary and also to touch the files that should be regenerated

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
source "$(dirname "${BASH_SOURCE}")/lib/init.sh" 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 \ ${OS_OUTPUT_BINPATH}/conversion-gen \
--go-header-file "hack/boilerplate/boilerplate.go.txt" \ --go-header-file "hack/boilerplate/boilerplate.go.txt" \

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
source "$(dirname "${BASH_SOURCE}")/lib/init.sh" 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 \ ${OS_OUTPUT_BINPATH}/deepcopy-gen \
--go-header-file "hack/boilerplate/boilerplate.go.txt" \ --go-header-file "hack/boilerplate/boilerplate.go.txt" \

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
source "$(dirname "${BASH_SOURCE}")/lib/init.sh" 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 \ ${OS_OUTPUT_BINPATH}/defaulter-gen \
--go-header-file "hack/boilerplate/boilerplate.go.txt" \ --go-header-file "hack/boilerplate/boilerplate.go.txt" \

View File

@@ -23,7 +23,7 @@ DESCHEDULER_ROOT=$(dirname "${BASH_SOURCE}")/..
GO_VERSION=($(go version)) 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') ]]; then 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." echo "Unknown go version '${GO_VERSION}', skipping gofmt."
exit 1 exit 1
fi fi

27
kubernetes/configmap.yaml Normal file
View File

@@ -0,0 +1,27 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: descheduler-policy-configmap
namespace: kube-system
data:
policy.yaml: |
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"RemoveDuplicates":
enabled: true
"RemovePodsViolatingInterPodAntiAffinity":
enabled: true
"LowNodeUtilization":
enabled: true
params:
nodeResourceUtilizationThresholds:
thresholds:
"cpu" : 20
"memory": 20
"pods": 20
targetThresholds:
"cpu" : 50
"memory": 50
"pods": 50

33
kubernetes/job.yaml Normal file
View File

@@ -0,0 +1,33 @@
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:latest
volumeMounts:
- mountPath: /policy-dir
name: policy-volume
command:
- "/bin/descheduler"
args:
- "--policy-config-file"
- "/policy-dir/policy.yaml"
- "--v"
- "3"
restartPolicy: "Never"
serviceAccountName: descheduler-sa
volumes:
- name: policy-volume
configMap:
name: descheduler-policy-configmap

37
kubernetes/rbac.yaml Normal file
View File

@@ -0,0 +1,37 @@
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: descheduler-cluster-role
namespace: kube-system
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "watch", "list"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list", "delete"]
- apiGroups: [""]
resources: ["pods/eviction"]
verbs: ["create"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: descheduler-sa
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: descehduler-cluster-role-binding
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: descheduler-cluster-role
subjects:
- name: descheduler-sa
kind: ServiceAccount
namespace: kube-system

View File

@@ -37,7 +37,6 @@ func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *r
&announced.GroupMetaFactoryArgs{ &announced.GroupMetaFactoryArgs{
GroupName: deschedulerapi.GroupName, GroupName: deschedulerapi.GroupName,
VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version}, VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version},
ImportPrefix: "github.com/kubernetes-incubator/descheduler/pkg/api",
AddInternalObjectsToScheme: deschedulerapi.AddToScheme, AddInternalObjectsToScheme: deschedulerapi.AddToScheme,
}, },
announced.VersionToSchemeFunc{ announced.VersionToSchemeFunc{

File diff suppressed because it is too large Load Diff

View File

@@ -17,10 +17,12 @@ limitations under the License.
package api package api
import ( import (
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/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 { type DeschedulerPolicy struct {
metav1.TypeMeta metav1.TypeMeta
@@ -45,6 +47,7 @@ type DeschedulerStrategy struct {
// Only one of its members may be specified // Only one of its members may be specified
type StrategyParameters struct { type StrategyParameters struct {
NodeResourceUtilizationThresholds NodeResourceUtilizationThresholds NodeResourceUtilizationThresholds NodeResourceUtilizationThresholds
NodeAffinityType []string
} }
type Percentage float64 type Percentage float64

File diff suppressed because it is too large Load Diff

View File

@@ -17,10 +17,12 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/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 { type DeschedulerPolicy struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
@@ -45,6 +47,7 @@ type DeschedulerStrategy struct {
// Only one of its members may be specified // Only one of its members may be specified
type StrategyParameters struct { type StrategyParameters struct {
NodeResourceUtilizationThresholds NodeResourceUtilizationThresholds `json:"nodeResourceUtilizationThresholds,omitempty"` NodeResourceUtilizationThresholds NodeResourceUtilizationThresholds `json:"nodeResourceUtilizationThresholds,omitempty"`
NodeAffinityType []string `json:"nodeAffinityType,omitempty"`
} }
type Percentage float64 type Percentage float64

View File

@@ -1,7 +1,7 @@
// +build !ignore_autogenerated // +build !ignore_autogenerated
/* /*
Copyright 2017 The Kubernetes Authors. Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -21,14 +21,15 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
unsafe "unsafe"
api "github.com/kubernetes-incubator/descheduler/pkg/api" api "github.com/kubernetes-incubator/descheduler/pkg/api"
conversion "k8s.io/apimachinery/pkg/conversion" conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
unsafe "unsafe"
) )
func init() { func init() {
SchemeBuilder.Register(RegisterConversions) localSchemeBuilder.Register(RegisterConversions)
} }
// RegisterConversions adds conversion functions to the given scheme. // 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 { if err := Convert_v1alpha1_NodeResourceUtilizationThresholds_To_api_NodeResourceUtilizationThresholds(&in.NodeResourceUtilizationThresholds, &out.NodeResourceUtilizationThresholds, s); err != nil {
return err return err
} }
out.NodeAffinityType = *(*[]string)(unsafe.Pointer(&in.NodeAffinityType))
return nil 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 { if err := Convert_api_NodeResourceUtilizationThresholds_To_v1alpha1_NodeResourceUtilizationThresholds(&in.NodeResourceUtilizationThresholds, &out.NodeResourceUtilizationThresholds, s); err != nil {
return err return err
} }
out.NodeAffinityType = *(*[]string)(unsafe.Pointer(&in.NodeAffinityType))
return nil return nil
} }

View File

@@ -1,7 +1,7 @@
// +build !ignore_autogenerated // +build !ignore_autogenerated
/* /*
Copyright 2017 The Kubernetes Authors. Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -21,93 +21,109 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
reflect "reflect"
) )
func init() { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
SchemeBuilder.Register(RegisterDeepCopies) func (in *DeschedulerPolicy) DeepCopyInto(out *DeschedulerPolicy) {
} *out = *in
out.TypeMeta = in.TypeMeta
// RegisterDeepCopies adds deep-copy functions to the given scheme. Public if in.Strategies != nil {
// to allow building arbitrary schemes. in, out := &in.Strategies, &out.Strategies
func RegisterDeepCopies(scheme *runtime.Scheme) error { *out = make(StrategyList, len(*in))
return scheme.AddGeneratedDeepCopyFuncs( for key, val := range *in {
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_DeschedulerPolicy, InType: reflect.TypeOf(&DeschedulerPolicy{})}, newVal := new(DeschedulerStrategy)
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_DeschedulerStrategy, InType: reflect.TypeOf(&DeschedulerStrategy{})}, val.DeepCopyInto(newVal)
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_NodeResourceUtilizationThresholds, InType: reflect.TypeOf(&NodeResourceUtilizationThresholds{})}, (*out)[key] = *newVal
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
}
} }
}
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 return nil
} }
} }
// DeepCopy_v1alpha1_DeschedulerStrategy is an autogenerated deepcopy function. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func DeepCopy_v1alpha1_DeschedulerStrategy(in interface{}, out interface{}, c *conversion.Cloner) error { func (in *DeschedulerStrategy) DeepCopyInto(out *DeschedulerStrategy) {
{ *out = *in
in := in.(*DeschedulerStrategy) in.Params.DeepCopyInto(&out.Params)
out := out.(*DeschedulerStrategy) return
*out = *in
if err := DeepCopy_v1alpha1_StrategyParameters(&in.Params, &out.Params, c); err != nil {
return err
}
return nil
}
} }
// DeepCopy_v1alpha1_NodeResourceUtilizationThresholds is an autogenerated deepcopy function. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeschedulerStrategy.
func DeepCopy_v1alpha1_NodeResourceUtilizationThresholds(in interface{}, out interface{}, c *conversion.Cloner) error { func (in *DeschedulerStrategy) DeepCopy() *DeschedulerStrategy {
{ if in == nil {
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
}
}
return nil return nil
} }
out := new(DeschedulerStrategy)
in.DeepCopyInto(out)
return out
} }
// DeepCopy_v1alpha1_StrategyParameters is an autogenerated deepcopy function. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func DeepCopy_v1alpha1_StrategyParameters(in interface{}, out interface{}, c *conversion.Cloner) error { func (in *NodeResourceUtilizationThresholds) DeepCopyInto(out *NodeResourceUtilizationThresholds) {
{ *out = *in
in := in.(*StrategyParameters) if in.Thresholds != nil {
out := out.(*StrategyParameters) in, out := &in.Thresholds, &out.Thresholds
*out = *in *out = make(ResourceThresholds, len(*in))
if err := DeepCopy_v1alpha1_NodeResourceUtilizationThresholds(&in.NodeResourceUtilizationThresholds, &out.NodeResourceUtilizationThresholds, c); err != nil { for key, val := range *in {
return err (*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 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
} }

View File

@@ -1,7 +1,7 @@
// +build !ignore_autogenerated // +build !ignore_autogenerated
/* /*
Copyright 2017 The Kubernetes Authors. Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,7 +1,7 @@
// +build !ignore_autogenerated // +build !ignore_autogenerated
/* /*
Copyright 2017 The Kubernetes Authors. Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -21,93 +21,109 @@ limitations under the License.
package api package api
import ( import (
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
reflect "reflect"
) )
func init() { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
SchemeBuilder.Register(RegisterDeepCopies) func (in *DeschedulerPolicy) DeepCopyInto(out *DeschedulerPolicy) {
} *out = *in
out.TypeMeta = in.TypeMeta
// RegisterDeepCopies adds deep-copy functions to the given scheme. Public if in.Strategies != nil {
// to allow building arbitrary schemes. in, out := &in.Strategies, &out.Strategies
func RegisterDeepCopies(scheme *runtime.Scheme) error { *out = make(StrategyList, len(*in))
return scheme.AddGeneratedDeepCopyFuncs( for key, val := range *in {
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeschedulerPolicy, InType: reflect.TypeOf(&DeschedulerPolicy{})}, newVal := new(DeschedulerStrategy)
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeschedulerStrategy, InType: reflect.TypeOf(&DeschedulerStrategy{})}, val.DeepCopyInto(newVal)
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_NodeResourceUtilizationThresholds, InType: reflect.TypeOf(&NodeResourceUtilizationThresholds{})}, (*out)[key] = *newVal
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
}
} }
}
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 return nil
} }
} }
// DeepCopy_api_DeschedulerStrategy is an autogenerated deepcopy function. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func DeepCopy_api_DeschedulerStrategy(in interface{}, out interface{}, c *conversion.Cloner) error { func (in *DeschedulerStrategy) DeepCopyInto(out *DeschedulerStrategy) {
{ *out = *in
in := in.(*DeschedulerStrategy) in.Params.DeepCopyInto(&out.Params)
out := out.(*DeschedulerStrategy) return
*out = *in
if err := DeepCopy_api_StrategyParameters(&in.Params, &out.Params, c); err != nil {
return err
}
return nil
}
} }
// DeepCopy_api_NodeResourceUtilizationThresholds is an autogenerated deepcopy function. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeschedulerStrategy.
func DeepCopy_api_NodeResourceUtilizationThresholds(in interface{}, out interface{}, c *conversion.Cloner) error { func (in *DeschedulerStrategy) DeepCopy() *DeschedulerStrategy {
{ if in == nil {
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
}
}
return nil return nil
} }
out := new(DeschedulerStrategy)
in.DeepCopyInto(out)
return out
} }
// DeepCopy_api_StrategyParameters is an autogenerated deepcopy function. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func DeepCopy_api_StrategyParameters(in interface{}, out interface{}, c *conversion.Cloner) error { func (in *NodeResourceUtilizationThresholds) DeepCopyInto(out *NodeResourceUtilizationThresholds) {
{ *out = *in
in := in.(*StrategyParameters) if in.Thresholds != nil {
out := out.(*StrategyParameters) in, out := &in.Thresholds, &out.Thresholds
*out = *in *out = make(ResourceThresholds, len(*in))
if err := DeepCopy_api_NodeResourceUtilizationThresholds(&in.NodeResourceUtilizationThresholds, &out.NodeResourceUtilizationThresholds, c); err != nil { for key, val := range *in {
return err (*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 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
} }

View File

@@ -37,7 +37,6 @@ func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *r
&announced.GroupMetaFactoryArgs{ &announced.GroupMetaFactoryArgs{
GroupName: componentconfig.GroupName, GroupName: componentconfig.GroupName,
VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version}, VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version},
ImportPrefix: "github.com/kubernetes-incubator/descheduler/pkg/apis/componentconfig",
AddInternalObjectsToScheme: componentconfig.AddToScheme, AddInternalObjectsToScheme: componentconfig.AddToScheme,
}, },
announced.VersionToSchemeFunc{ announced.VersionToSchemeFunc{

View File

@@ -14,10 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// ************************************************************ // Code generated by codecgen - DO NOT EDIT.
// DO NOT EDIT.
// THIS FILE IS AUTO-GENERATED BY codecgen.
// ************************************************************
package componentconfig package componentconfig
@@ -33,31 +30,29 @@ import (
const ( const (
// ----- content types ---- // ----- content types ----
codecSelferC_UTF81234 = 1 codecSelferCcUTF81234 = 1
codecSelferC_RAW1234 = 0 codecSelferCcRAW1234 = 0
// ----- value types used ---- // ----- value types used ----
codecSelferValueTypeArray1234 = 10 codecSelferValueTypeArray1234 = 10
codecSelferValueTypeMap1234 = 9 codecSelferValueTypeMap1234 = 9
// ----- containerStateValues ---- codecSelferValueTypeString1234 = 6
codecSelfer_containerMapKey1234 = 2 codecSelferValueTypeInt1234 = 2
codecSelfer_containerMapValue1234 = 3 codecSelferValueTypeUint1234 = 3
codecSelfer_containerMapEnd1234 = 4 codecSelferValueTypeFloat1234 = 4
codecSelfer_containerArrayElem1234 = 6
codecSelfer_containerArrayEnd1234 = 7
) )
var ( var (
codecSelferBitsize1234 = uint8(reflect.TypeOf(uint(0)).Bits()) 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{} type codecSelfer1234 struct{}
func init() { func init() {
if codec1978.GenVersion != 5 { if codec1978.GenVersion != 8 {
_, file, _, _ := runtime.Caller(0) _, file, _, _ := runtime.Caller(0)
err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v", err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v",
5, codec1978.GenVersion, file) 8, codec1978.GenVersion, file)
panic(err) panic(err)
} }
if false { // reference the types, but skip this branch at build/run time 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 := z.EncBinary()
_ = yym1 _ = yym1
if false { if false {
} else if z.HasExtensions() && z.EncExt(x) { } else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
z.EncExtension(x, yyxt1)
} else { } else {
yysep2 := !z.EncBinary() yysep2 := !z.EncBinary()
yy2arr2 := z.EncBasicHandle().StructToArray yy2arr2 := z.EncBasicHandle().StructToArray
var yyq2 [7]bool var yyq2 [9]bool
_, _, _ = yysep2, yyq2, yy2arr2 _ = yyq2
_, _ = yysep2, yy2arr2
const yyr2 bool = false const yyr2 bool = false
yyq2[0] = x.Kind != "" yyq2[0] = x.Kind != ""
yyq2[1] = x.APIVersion != "" yyq2[1] = x.APIVersion != ""
var yynn2 int
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
r.EncodeArrayStart(7) r.WriteArrayStart(9)
} else { } else {
yynn2 = 5 var yynn2 = 7
for _, b := range yyq2 { for _, b := range yyq2 {
if b { if b {
yynn2++ yynn2++
} }
} }
r.EncodeMapStart(yynn2) r.WriteMapStart(yynn2)
yynn2 = 0 yynn2 = 0
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
if yyq2[0] { if yyq2[0] {
yym4 := z.EncBinary() yym4 := z.EncBinary()
_ = yym4 _ = yym4
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) r.EncodeString(codecSelferCcUTF81234, string(x.Kind))
} }
} else { } else {
r.EncodeString(codecSelferC_UTF81234, "") r.EncodeString(codecSelferCcUTF81234, "")
} }
} else { } else {
if yyq2[0] { if yyq2[0] {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("kind")) r.EncStructFieldKey(codecSelferValueTypeString1234, `kind`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym5 := z.EncBinary() yym5 := z.EncBinary()
_ = yym5 _ = yym5
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) r.EncodeString(codecSelferCcUTF81234, string(x.Kind))
} }
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
if yyq2[1] { if yyq2[1] {
yym7 := z.EncBinary() yym7 := z.EncBinary()
_ = yym7 _ = yym7
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) r.EncodeString(codecSelferCcUTF81234, string(x.APIVersion))
} }
} else { } else {
r.EncodeString(codecSelferC_UTF81234, "") r.EncodeString(codecSelferCcUTF81234, "")
} }
} else { } else {
if yyq2[1] { if yyq2[1] {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) r.EncStructFieldKey(codecSelferValueTypeString1234, `apiVersion`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym8 := z.EncBinary() yym8 := z.EncBinary()
_ = yym8 _ = yym8
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) r.EncodeString(codecSelferCcUTF81234, string(x.APIVersion))
} }
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
yym10 := z.EncBinary() yym10 := z.EncBinary()
_ = yym10 _ = yym10
if false { 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 { } else {
r.EncodeInt(int64(x.DeschedulingInterval)) r.EncodeInt(int64(x.DeschedulingInterval))
} }
} else { } else {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("DeschedulingInterval")) r.EncStructFieldKey(codecSelferValueTypeString1234, `DeschedulingInterval`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym11 := z.EncBinary() yym11 := z.EncBinary()
_ = yym11 _ = yym11
if false { 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 { } else {
r.EncodeInt(int64(x.DeschedulingInterval)) r.EncodeInt(int64(x.DeschedulingInterval))
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
yym13 := z.EncBinary() yym13 := z.EncBinary()
_ = yym13 _ = yym13
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.KubeconfigFile)) r.EncodeString(codecSelferCcUTF81234, string(x.KubeconfigFile))
} }
} else { } else {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("KubeconfigFile")) r.EncStructFieldKey(codecSelferValueTypeString1234, `KubeconfigFile`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym14 := z.EncBinary() yym14 := z.EncBinary()
_ = yym14 _ = yym14
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.KubeconfigFile)) r.EncodeString(codecSelferCcUTF81234, string(x.KubeconfigFile))
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
yym16 := z.EncBinary() yym16 := z.EncBinary()
_ = yym16 _ = yym16
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.PolicyConfigFile)) r.EncodeString(codecSelferCcUTF81234, string(x.PolicyConfigFile))
} }
} else { } else {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("PolicyConfigFile")) r.EncStructFieldKey(codecSelferValueTypeString1234, `PolicyConfigFile`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym17 := z.EncBinary() yym17 := z.EncBinary()
_ = yym17 _ = yym17
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.PolicyConfigFile)) r.EncodeString(codecSelferCcUTF81234, string(x.PolicyConfigFile))
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
yym19 := z.EncBinary() yym19 := z.EncBinary()
_ = yym19 _ = yym19
if false { if false {
@@ -217,9 +215,9 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
r.EncodeBool(bool(x.DryRun)) r.EncodeBool(bool(x.DryRun))
} }
} else { } else {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("DryRun")) r.EncStructFieldKey(codecSelferValueTypeString1234, `DryRun`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym20 := z.EncBinary() yym20 := z.EncBinary()
_ = yym20 _ = yym20
if false { if false {
@@ -228,28 +226,66 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
yym22 := z.EncBinary() yym22 := z.EncBinary()
_ = yym22 _ = yym22
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.NodeSelector)) r.EncodeString(codecSelferCcUTF81234, string(x.NodeSelector))
} }
} else { } else {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("NodeSelector")) r.EncStructFieldKey(codecSelferValueTypeString1234, `NodeSelector`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym23 := z.EncBinary() yym23 := z.EncBinary()
_ = yym23 _ = yym23
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.NodeSelector)) r.EncodeString(codecSelferCcUTF81234, string(x.NodeSelector))
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayEnd1234) r.WriteArrayElem()
yym25 := z.EncBinary()
_ = yym25
if false {
} else {
r.EncodeInt(int64(x.MaxNoOfPodsToEvictPerNode))
}
} else { } else {
z.EncSendContainerState(codecSelfer_containerMapEnd1234) r.WriteMapElemKey()
r.EncStructFieldKey(codecSelferValueTypeString1234, `MaxNoOfPodsToEvictPerNode`)
r.WriteMapElemValue()
yym26 := z.EncBinary()
_ = yym26
if false {
} else {
r.EncodeInt(int64(x.MaxNoOfPodsToEvictPerNode))
}
}
if yyr2 || yy2arr2 {
r.WriteArrayElem()
yym28 := z.EncBinary()
_ = yym28
if false {
} else {
r.EncodeBool(bool(x.EvictLocalStoragePods))
}
} else {
r.WriteMapElemKey()
r.EncStructFieldKey(codecSelferValueTypeString1234, `EvictLocalStoragePods`)
r.WriteMapElemValue()
yym29 := z.EncBinary()
_ = yym29
if false {
} else {
r.EncodeBool(bool(x.EvictLocalStoragePods))
}
}
if yyr2 || yy2arr2 {
r.WriteArrayEnd()
} else {
r.WriteMapEnd()
} }
} }
} }
@@ -262,25 +298,26 @@ func (x *DeschedulerConfiguration) CodecDecodeSelf(d *codec1978.Decoder) {
yym1 := z.DecBinary() yym1 := z.DecBinary()
_ = yym1 _ = yym1
if false { if false {
} else if z.HasExtensions() && z.DecExt(x) { } else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
z.DecExtension(x, yyxt1)
} else { } else {
yyct2 := r.ContainerType() yyct2 := r.ContainerType()
if yyct2 == codecSelferValueTypeMap1234 { if yyct2 == codecSelferValueTypeMap1234 {
yyl2 := r.ReadMapStart() yyl2 := r.ReadMapStart()
if yyl2 == 0 { if yyl2 == 0 {
z.DecSendContainerState(codecSelfer_containerMapEnd1234) r.ReadMapEnd()
} else { } else {
x.codecDecodeSelfFromMap(yyl2, d) x.codecDecodeSelfFromMap(yyl2, d)
} }
} else if yyct2 == codecSelferValueTypeArray1234 { } else if yyct2 == codecSelferValueTypeArray1234 {
yyl2 := r.ReadArrayStart() yyl2 := r.ReadArrayStart()
if yyl2 == 0 { if yyl2 == 0 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
} else { } else {
x.codecDecodeSelfFromArray(yyl2, d) x.codecDecodeSelfFromArray(yyl2, d)
} }
} else { } else {
panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) panic(errCodecSelferOnlyMapOrArrayEncodeToStruct1234)
} }
} }
} }
@@ -289,8 +326,6 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
var h codecSelfer1234 var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d) z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r _, _, _ = h, z, r
var yys3Slc = z.DecScratchBuffer() // default slice to decode into
_ = yys3Slc
var yyhl3 bool = l >= 0 var yyhl3 bool = l >= 0
for yyj3 := 0; ; yyj3++ { for yyj3 := 0; ; yyj3++ {
if yyhl3 { if yyhl3 {
@@ -302,10 +337,9 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
break break
} }
} }
z.DecSendContainerState(codecSelfer_containerMapKey1234) r.ReadMapElemKey()
yys3Slc = r.DecodeBytes(yys3Slc, true, true) yys3 := z.StringView(r.DecStructFieldKey(codecSelferValueTypeString1234, z.DecScratchArrayBuffer()))
yys3 := string(yys3Slc) r.ReadMapElemValue()
z.DecSendContainerState(codecSelfer_containerMapValue1234)
switch yys3 { switch yys3 {
case "kind": case "kind":
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
@@ -339,7 +373,8 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
yym9 := z.DecBinary() yym9 := z.DecBinary()
_ = yym9 _ = yym9
if false { if false {
} else if z.HasExtensions() && z.DecExt(yyv8) { } else if yyxt9 := z.Extension(z.I2Rtid(yyv8)); yyxt9 != nil {
z.DecExtension(yyv8, yyxt9)
} else { } else {
*((*int64)(yyv8)) = int64(r.DecodeInt(64)) *((*int64)(yyv8)) = int64(r.DecodeInt(64))
} }
@@ -392,102 +427,81 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
*((*string)(yyv16)) = r.DecodeString() *((*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))
}
}
case "EvictLocalStoragePods":
if r.TryDecodeAsNil() {
x.EvictLocalStoragePods = false
} else {
yyv20 := &x.EvictLocalStoragePods
yym21 := z.DecBinary()
_ = yym21
if false {
} else {
*((*bool)(yyv20)) = r.DecodeBool()
}
}
default: default:
z.DecStructFieldNotFound(-1, yys3) z.DecStructFieldNotFound(-1, yys3)
} // end switch yys3 } // end switch yys3
} // end for yyj3 } // end for yyj3
z.DecSendContainerState(codecSelfer_containerMapEnd1234) r.ReadMapEnd()
} }
func (x *DeschedulerConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { func (x *DeschedulerConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
var h codecSelfer1234 var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d) z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r _, _, _ = h, z, r
var yyj18 int var yyj22 int
var yyb18 bool var yyb22 bool
var yyhl18 bool = l >= 0 var yyhl22 bool = l >= 0
yyj18++ yyj22++
if yyhl18 { if yyhl22 {
yyb18 = yyj18 > l yyb22 = yyj22 > l
} else { } else {
yyb18 = r.CheckBreak() yyb22 = r.CheckBreak()
} }
if yyb18 { if yyb22 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
return return
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) r.ReadArrayElem()
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
x.Kind = "" x.Kind = ""
} else { } else {
yyv19 := &x.Kind yyv23 := &x.Kind
yym20 := z.DecBinary()
_ = yym20
if false {
} else {
*((*string)(yyv19)) = r.DecodeString()
}
}
yyj18++
if yyhl18 {
yyb18 = yyj18 > l
} else {
yyb18 = r.CheckBreak()
}
if yyb18 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.APIVersion = ""
} else {
yyv21 := &x.APIVersion
yym22 := z.DecBinary()
_ = yym22
if false {
} else {
*((*string)(yyv21)) = r.DecodeString()
}
}
yyj18++
if yyhl18 {
yyb18 = yyj18 > l
} else {
yyb18 = r.CheckBreak()
}
if yyb18 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.DeschedulingInterval = 0
} else {
yyv23 := &x.DeschedulingInterval
yym24 := z.DecBinary() yym24 := z.DecBinary()
_ = yym24 _ = yym24
if false { if false {
} else if z.HasExtensions() && z.DecExt(yyv23) {
} else { } else {
*((*int64)(yyv23)) = int64(r.DecodeInt(64)) *((*string)(yyv23)) = r.DecodeString()
} }
} }
yyj18++ yyj22++
if yyhl18 { if yyhl22 {
yyb18 = yyj18 > l yyb22 = yyj22 > l
} else { } else {
yyb18 = r.CheckBreak() yyb22 = r.CheckBreak()
} }
if yyb18 { if yyb22 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
return return
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) r.ReadArrayElem()
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
x.KubeconfigFile = "" x.APIVersion = ""
} else { } else {
yyv25 := &x.KubeconfigFile yyv25 := &x.APIVersion
yym26 := z.DecBinary() yym26 := z.DecBinary()
_ = yym26 _ = yym26
if false { if false {
@@ -495,65 +509,67 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.
*((*string)(yyv25)) = r.DecodeString() *((*string)(yyv25)) = r.DecodeString()
} }
} }
yyj18++ yyj22++
if yyhl18 { if yyhl22 {
yyb18 = yyj18 > l yyb22 = yyj22 > l
} else { } else {
yyb18 = r.CheckBreak() yyb22 = r.CheckBreak()
} }
if yyb18 { if yyb22 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
return return
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) r.ReadArrayElem()
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
x.PolicyConfigFile = "" x.DeschedulingInterval = 0
} else { } else {
yyv27 := &x.PolicyConfigFile yyv27 := &x.DeschedulingInterval
yym28 := z.DecBinary() yym28 := z.DecBinary()
_ = yym28 _ = yym28
if false { if false {
} else if yyxt28 := z.Extension(z.I2Rtid(yyv27)); yyxt28 != nil {
z.DecExtension(yyv27, yyxt28)
} else { } else {
*((*string)(yyv27)) = r.DecodeString() *((*int64)(yyv27)) = int64(r.DecodeInt(64))
} }
} }
yyj18++ yyj22++
if yyhl18 { if yyhl22 {
yyb18 = yyj18 > l yyb22 = yyj22 > l
} else { } else {
yyb18 = r.CheckBreak() yyb22 = r.CheckBreak()
} }
if yyb18 { if yyb22 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
return return
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) r.ReadArrayElem()
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
x.DryRun = false x.KubeconfigFile = ""
} else { } else {
yyv29 := &x.DryRun yyv29 := &x.KubeconfigFile
yym30 := z.DecBinary() yym30 := z.DecBinary()
_ = yym30 _ = yym30
if false { if false {
} else { } else {
*((*bool)(yyv29)) = r.DecodeBool() *((*string)(yyv29)) = r.DecodeString()
} }
} }
yyj18++ yyj22++
if yyhl18 { if yyhl22 {
yyb18 = yyj18 > l yyb22 = yyj22 > l
} else { } else {
yyb18 = r.CheckBreak() yyb22 = r.CheckBreak()
} }
if yyb18 { if yyb22 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
return return
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) r.ReadArrayElem()
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
x.NodeSelector = "" x.PolicyConfigFile = ""
} else { } else {
yyv31 := &x.NodeSelector yyv31 := &x.PolicyConfigFile
yym32 := z.DecBinary() yym32 := z.DecBinary()
_ = yym32 _ = yym32
if false { if false {
@@ -561,18 +577,106 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.
*((*string)(yyv31)) = r.DecodeString() *((*string)(yyv31)) = r.DecodeString()
} }
} }
for { yyj22++
yyj18++ if yyhl22 {
if yyhl18 { yyb22 = yyj22 > l
yyb18 = yyj18 > l } else {
yyb22 = r.CheckBreak()
}
if yyb22 {
r.ReadArrayEnd()
return
}
r.ReadArrayElem()
if r.TryDecodeAsNil() {
x.DryRun = false
} else {
yyv33 := &x.DryRun
yym34 := z.DecBinary()
_ = yym34
if false {
} else { } else {
yyb18 = r.CheckBreak() *((*bool)(yyv33)) = r.DecodeBool()
} }
if yyb18 { }
yyj22++
if yyhl22 {
yyb22 = yyj22 > l
} else {
yyb22 = r.CheckBreak()
}
if yyb22 {
r.ReadArrayEnd()
return
}
r.ReadArrayElem()
if r.TryDecodeAsNil() {
x.NodeSelector = ""
} else {
yyv35 := &x.NodeSelector
yym36 := z.DecBinary()
_ = yym36
if false {
} else {
*((*string)(yyv35)) = r.DecodeString()
}
}
yyj22++
if yyhl22 {
yyb22 = yyj22 > l
} else {
yyb22 = r.CheckBreak()
}
if yyb22 {
r.ReadArrayEnd()
return
}
r.ReadArrayElem()
if r.TryDecodeAsNil() {
x.MaxNoOfPodsToEvictPerNode = 0
} else {
yyv37 := &x.MaxNoOfPodsToEvictPerNode
yym38 := z.DecBinary()
_ = yym38
if false {
} else {
*((*int)(yyv37)) = int(r.DecodeInt(codecSelferBitsize1234))
}
}
yyj22++
if yyhl22 {
yyb22 = yyj22 > l
} else {
yyb22 = r.CheckBreak()
}
if yyb22 {
r.ReadArrayEnd()
return
}
r.ReadArrayElem()
if r.TryDecodeAsNil() {
x.EvictLocalStoragePods = false
} else {
yyv39 := &x.EvictLocalStoragePods
yym40 := z.DecBinary()
_ = yym40
if false {
} else {
*((*bool)(yyv39)) = r.DecodeBool()
}
}
for {
yyj22++
if yyhl22 {
yyb22 = yyj22 > l
} else {
yyb22 = r.CheckBreak()
}
if yyb22 {
break break
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) r.ReadArrayElem()
z.DecStructFieldNotFound(yyj18-1, "") z.DecStructFieldNotFound(yyj22-1, "")
} }
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
} }

View File

@@ -22,6 +22,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type DeschedulerConfiguration struct { type DeschedulerConfiguration struct {
metav1.TypeMeta metav1.TypeMeta
@@ -40,4 +42,10 @@ type DeschedulerConfiguration struct {
// Node selectors // Node selectors
NodeSelector string NodeSelector string
// MaxNoOfPodsToEvictPerNode restricts maximum of pods to be evicted per node.
MaxNoOfPodsToEvictPerNode int
// EvictLocalStoragePods allows pods using local storage to be evicted.
EvictLocalStoragePods bool
} }

View File

@@ -14,10 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// ************************************************************ // Code generated by codecgen - DO NOT EDIT.
// DO NOT EDIT.
// THIS FILE IS AUTO-GENERATED BY codecgen.
// ************************************************************
package v1alpha1 package v1alpha1
@@ -33,31 +30,29 @@ import (
const ( const (
// ----- content types ---- // ----- content types ----
codecSelferC_UTF81234 = 1 codecSelferCcUTF81234 = 1
codecSelferC_RAW1234 = 0 codecSelferCcRAW1234 = 0
// ----- value types used ---- // ----- value types used ----
codecSelferValueTypeArray1234 = 10 codecSelferValueTypeArray1234 = 10
codecSelferValueTypeMap1234 = 9 codecSelferValueTypeMap1234 = 9
// ----- containerStateValues ---- codecSelferValueTypeString1234 = 6
codecSelfer_containerMapKey1234 = 2 codecSelferValueTypeInt1234 = 2
codecSelfer_containerMapValue1234 = 3 codecSelferValueTypeUint1234 = 3
codecSelfer_containerMapEnd1234 = 4 codecSelferValueTypeFloat1234 = 4
codecSelfer_containerArrayElem1234 = 6
codecSelfer_containerArrayEnd1234 = 7
) )
var ( var (
codecSelferBitsize1234 = uint8(reflect.TypeOf(uint(0)).Bits()) 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{} type codecSelfer1234 struct{}
func init() { func init() {
if codec1978.GenVersion != 5 { if codec1978.GenVersion != 8 {
_, file, _, _ := runtime.Caller(0) _, file, _, _ := runtime.Caller(0)
err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v", err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v",
5, codec1978.GenVersion, file) 8, codec1978.GenVersion, file)
panic(err) panic(err)
} }
if false { // reference the types, but skip this branch at build/run time if false { // reference the types, but skip this branch at build/run time
@@ -77,12 +72,14 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
yym1 := z.EncBinary() yym1 := z.EncBinary()
_ = yym1 _ = yym1
if false { if false {
} else if z.HasExtensions() && z.EncExt(x) { } else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
z.EncExtension(x, yyxt1)
} else { } else {
yysep2 := !z.EncBinary() yysep2 := !z.EncBinary()
yy2arr2 := z.EncBasicHandle().StructToArray yy2arr2 := z.EncBasicHandle().StructToArray
var yyq2 [7]bool var yyq2 [9]bool
_, _, _ = yysep2, yyq2, yy2arr2 _ = yyq2
_, _ = yysep2, yy2arr2
const yyr2 bool = false const yyr2 bool = false
yyq2[0] = x.Kind != "" yyq2[0] = x.Kind != ""
yyq2[1] = x.APIVersion != "" yyq2[1] = x.APIVersion != ""
@@ -90,76 +87,78 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
yyq2[4] = x.PolicyConfigFile != "" yyq2[4] = x.PolicyConfigFile != ""
yyq2[5] = x.DryRun != false yyq2[5] = x.DryRun != false
yyq2[6] = x.NodeSelector != "" yyq2[6] = x.NodeSelector != ""
var yynn2 int yyq2[7] = x.MaxNoOfPodsToEvictPerNode != 0
yyq2[8] = x.EvictLocalStoragePods != false
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
r.EncodeArrayStart(7) r.WriteArrayStart(9)
} else { } else {
yynn2 = 1 var yynn2 = 1
for _, b := range yyq2 { for _, b := range yyq2 {
if b { if b {
yynn2++ yynn2++
} }
} }
r.EncodeMapStart(yynn2) r.WriteMapStart(yynn2)
yynn2 = 0 yynn2 = 0
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
if yyq2[0] { if yyq2[0] {
yym4 := z.EncBinary() yym4 := z.EncBinary()
_ = yym4 _ = yym4
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) r.EncodeString(codecSelferCcUTF81234, string(x.Kind))
} }
} else { } else {
r.EncodeString(codecSelferC_UTF81234, "") r.EncodeString(codecSelferCcUTF81234, "")
} }
} else { } else {
if yyq2[0] { if yyq2[0] {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("kind")) r.EncStructFieldKey(codecSelferValueTypeString1234, `kind`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym5 := z.EncBinary() yym5 := z.EncBinary()
_ = yym5 _ = yym5
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) r.EncodeString(codecSelferCcUTF81234, string(x.Kind))
} }
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
if yyq2[1] { if yyq2[1] {
yym7 := z.EncBinary() yym7 := z.EncBinary()
_ = yym7 _ = yym7
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) r.EncodeString(codecSelferCcUTF81234, string(x.APIVersion))
} }
} else { } else {
r.EncodeString(codecSelferC_UTF81234, "") r.EncodeString(codecSelferCcUTF81234, "")
} }
} else { } else {
if yyq2[1] { if yyq2[1] {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) r.EncStructFieldKey(codecSelferValueTypeString1234, `apiVersion`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym8 := z.EncBinary() yym8 := z.EncBinary()
_ = yym8 _ = yym8
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) r.EncodeString(codecSelferCcUTF81234, string(x.APIVersion))
} }
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
if yyq2[2] { if yyq2[2] {
yym10 := z.EncBinary() yym10 := z.EncBinary()
_ = yym10 _ = yym10
if false { 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 { } else {
r.EncodeInt(int64(x.DeschedulingInterval)) r.EncodeInt(int64(x.DeschedulingInterval))
} }
@@ -168,64 +167,65 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
} }
} else { } else {
if yyq2[2] { if yyq2[2] {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("deschedulingInterval")) r.EncStructFieldKey(codecSelferValueTypeString1234, `deschedulingInterval`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym11 := z.EncBinary() yym11 := z.EncBinary()
_ = yym11 _ = yym11
if false { 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 { } else {
r.EncodeInt(int64(x.DeschedulingInterval)) r.EncodeInt(int64(x.DeschedulingInterval))
} }
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
yym13 := z.EncBinary() yym13 := z.EncBinary()
_ = yym13 _ = yym13
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.KubeconfigFile)) r.EncodeString(codecSelferCcUTF81234, string(x.KubeconfigFile))
} }
} else { } else {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("kubeconfigFile")) r.EncStructFieldKey(codecSelferValueTypeString1234, `kubeconfigFile`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym14 := z.EncBinary() yym14 := z.EncBinary()
_ = yym14 _ = yym14
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.KubeconfigFile)) r.EncodeString(codecSelferCcUTF81234, string(x.KubeconfigFile))
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
if yyq2[4] { if yyq2[4] {
yym16 := z.EncBinary() yym16 := z.EncBinary()
_ = yym16 _ = yym16
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.PolicyConfigFile)) r.EncodeString(codecSelferCcUTF81234, string(x.PolicyConfigFile))
} }
} else { } else {
r.EncodeString(codecSelferC_UTF81234, "") r.EncodeString(codecSelferCcUTF81234, "")
} }
} else { } else {
if yyq2[4] { if yyq2[4] {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("policyConfigFile")) r.EncStructFieldKey(codecSelferValueTypeString1234, `policyConfigFile`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym17 := z.EncBinary() yym17 := z.EncBinary()
_ = yym17 _ = yym17
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.PolicyConfigFile)) r.EncodeString(codecSelferCcUTF81234, string(x.PolicyConfigFile))
} }
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
if yyq2[5] { if yyq2[5] {
yym19 := z.EncBinary() yym19 := z.EncBinary()
_ = yym19 _ = yym19
@@ -238,9 +238,9 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
} }
} else { } else {
if yyq2[5] { if yyq2[5] {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("dryRun")) r.EncStructFieldKey(codecSelferValueTypeString1234, `dryRun`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym20 := z.EncBinary() yym20 := z.EncBinary()
_ = yym20 _ = yym20
if false { if false {
@@ -250,34 +250,84 @@ func (x *DeschedulerConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) r.WriteArrayElem()
if yyq2[6] { if yyq2[6] {
yym22 := z.EncBinary() yym22 := z.EncBinary()
_ = yym22 _ = yym22
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.NodeSelector)) r.EncodeString(codecSelferCcUTF81234, string(x.NodeSelector))
} }
} else { } else {
r.EncodeString(codecSelferC_UTF81234, "") r.EncodeString(codecSelferCcUTF81234, "")
} }
} else { } else {
if yyq2[6] { if yyq2[6] {
z.EncSendContainerState(codecSelfer_containerMapKey1234) r.WriteMapElemKey()
r.EncodeString(codecSelferC_UTF81234, string("nodeSelector")) r.EncStructFieldKey(codecSelferValueTypeString1234, `nodeSelector`)
z.EncSendContainerState(codecSelfer_containerMapValue1234) r.WriteMapElemValue()
yym23 := z.EncBinary() yym23 := z.EncBinary()
_ = yym23 _ = yym23
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.NodeSelector)) r.EncodeString(codecSelferCcUTF81234, string(x.NodeSelector))
} }
} }
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayEnd1234) r.WriteArrayElem()
if yyq2[7] {
yym25 := z.EncBinary()
_ = yym25
if false {
} else {
r.EncodeInt(int64(x.MaxNoOfPodsToEvictPerNode))
}
} else {
r.EncodeInt(0)
}
} else { } else {
z.EncSendContainerState(codecSelfer_containerMapEnd1234) 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.WriteArrayElem()
if yyq2[8] {
yym28 := z.EncBinary()
_ = yym28
if false {
} else {
r.EncodeBool(bool(x.EvictLocalStoragePods))
}
} else {
r.EncodeBool(false)
}
} else {
if yyq2[8] {
r.WriteMapElemKey()
r.EncStructFieldKey(codecSelferValueTypeString1234, `evictLocalStoragePods`)
r.WriteMapElemValue()
yym29 := z.EncBinary()
_ = yym29
if false {
} else {
r.EncodeBool(bool(x.EvictLocalStoragePods))
}
}
}
if yyr2 || yy2arr2 {
r.WriteArrayEnd()
} else {
r.WriteMapEnd()
} }
} }
} }
@@ -290,25 +340,26 @@ func (x *DeschedulerConfiguration) CodecDecodeSelf(d *codec1978.Decoder) {
yym1 := z.DecBinary() yym1 := z.DecBinary()
_ = yym1 _ = yym1
if false { if false {
} else if z.HasExtensions() && z.DecExt(x) { } else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
z.DecExtension(x, yyxt1)
} else { } else {
yyct2 := r.ContainerType() yyct2 := r.ContainerType()
if yyct2 == codecSelferValueTypeMap1234 { if yyct2 == codecSelferValueTypeMap1234 {
yyl2 := r.ReadMapStart() yyl2 := r.ReadMapStart()
if yyl2 == 0 { if yyl2 == 0 {
z.DecSendContainerState(codecSelfer_containerMapEnd1234) r.ReadMapEnd()
} else { } else {
x.codecDecodeSelfFromMap(yyl2, d) x.codecDecodeSelfFromMap(yyl2, d)
} }
} else if yyct2 == codecSelferValueTypeArray1234 { } else if yyct2 == codecSelferValueTypeArray1234 {
yyl2 := r.ReadArrayStart() yyl2 := r.ReadArrayStart()
if yyl2 == 0 { if yyl2 == 0 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
} else { } else {
x.codecDecodeSelfFromArray(yyl2, d) x.codecDecodeSelfFromArray(yyl2, d)
} }
} else { } else {
panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) panic(errCodecSelferOnlyMapOrArrayEncodeToStruct1234)
} }
} }
} }
@@ -317,8 +368,6 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
var h codecSelfer1234 var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d) z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r _, _, _ = h, z, r
var yys3Slc = z.DecScratchBuffer() // default slice to decode into
_ = yys3Slc
var yyhl3 bool = l >= 0 var yyhl3 bool = l >= 0
for yyj3 := 0; ; yyj3++ { for yyj3 := 0; ; yyj3++ {
if yyhl3 { if yyhl3 {
@@ -330,10 +379,9 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
break break
} }
} }
z.DecSendContainerState(codecSelfer_containerMapKey1234) r.ReadMapElemKey()
yys3Slc = r.DecodeBytes(yys3Slc, true, true) yys3 := z.StringView(r.DecStructFieldKey(codecSelferValueTypeString1234, z.DecScratchArrayBuffer()))
yys3 := string(yys3Slc) r.ReadMapElemValue()
z.DecSendContainerState(codecSelfer_containerMapValue1234)
switch yys3 { switch yys3 {
case "kind": case "kind":
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
@@ -367,7 +415,8 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
yym9 := z.DecBinary() yym9 := z.DecBinary()
_ = yym9 _ = yym9
if false { if false {
} else if z.HasExtensions() && z.DecExt(yyv8) { } else if yyxt9 := z.Extension(z.I2Rtid(yyv8)); yyxt9 != nil {
z.DecExtension(yyv8, yyxt9)
} else { } else {
*((*int64)(yyv8)) = int64(r.DecodeInt(64)) *((*int64)(yyv8)) = int64(r.DecodeInt(64))
} }
@@ -420,102 +469,81 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.De
*((*string)(yyv16)) = r.DecodeString() *((*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))
}
}
case "evictLocalStoragePods":
if r.TryDecodeAsNil() {
x.EvictLocalStoragePods = false
} else {
yyv20 := &x.EvictLocalStoragePods
yym21 := z.DecBinary()
_ = yym21
if false {
} else {
*((*bool)(yyv20)) = r.DecodeBool()
}
}
default: default:
z.DecStructFieldNotFound(-1, yys3) z.DecStructFieldNotFound(-1, yys3)
} // end switch yys3 } // end switch yys3
} // end for yyj3 } // end for yyj3
z.DecSendContainerState(codecSelfer_containerMapEnd1234) r.ReadMapEnd()
} }
func (x *DeschedulerConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { func (x *DeschedulerConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
var h codecSelfer1234 var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d) z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r _, _, _ = h, z, r
var yyj18 int var yyj22 int
var yyb18 bool var yyb22 bool
var yyhl18 bool = l >= 0 var yyhl22 bool = l >= 0
yyj18++ yyj22++
if yyhl18 { if yyhl22 {
yyb18 = yyj18 > l yyb22 = yyj22 > l
} else { } else {
yyb18 = r.CheckBreak() yyb22 = r.CheckBreak()
} }
if yyb18 { if yyb22 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
return return
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) r.ReadArrayElem()
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
x.Kind = "" x.Kind = ""
} else { } else {
yyv19 := &x.Kind yyv23 := &x.Kind
yym20 := z.DecBinary()
_ = yym20
if false {
} else {
*((*string)(yyv19)) = r.DecodeString()
}
}
yyj18++
if yyhl18 {
yyb18 = yyj18 > l
} else {
yyb18 = r.CheckBreak()
}
if yyb18 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.APIVersion = ""
} else {
yyv21 := &x.APIVersion
yym22 := z.DecBinary()
_ = yym22
if false {
} else {
*((*string)(yyv21)) = r.DecodeString()
}
}
yyj18++
if yyhl18 {
yyb18 = yyj18 > l
} else {
yyb18 = r.CheckBreak()
}
if yyb18 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.DeschedulingInterval = 0
} else {
yyv23 := &x.DeschedulingInterval
yym24 := z.DecBinary() yym24 := z.DecBinary()
_ = yym24 _ = yym24
if false { if false {
} else if z.HasExtensions() && z.DecExt(yyv23) {
} else { } else {
*((*int64)(yyv23)) = int64(r.DecodeInt(64)) *((*string)(yyv23)) = r.DecodeString()
} }
} }
yyj18++ yyj22++
if yyhl18 { if yyhl22 {
yyb18 = yyj18 > l yyb22 = yyj22 > l
} else { } else {
yyb18 = r.CheckBreak() yyb22 = r.CheckBreak()
} }
if yyb18 { if yyb22 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
return return
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) r.ReadArrayElem()
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
x.KubeconfigFile = "" x.APIVersion = ""
} else { } else {
yyv25 := &x.KubeconfigFile yyv25 := &x.APIVersion
yym26 := z.DecBinary() yym26 := z.DecBinary()
_ = yym26 _ = yym26
if false { if false {
@@ -523,65 +551,67 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.
*((*string)(yyv25)) = r.DecodeString() *((*string)(yyv25)) = r.DecodeString()
} }
} }
yyj18++ yyj22++
if yyhl18 { if yyhl22 {
yyb18 = yyj18 > l yyb22 = yyj22 > l
} else { } else {
yyb18 = r.CheckBreak() yyb22 = r.CheckBreak()
} }
if yyb18 { if yyb22 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
return return
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) r.ReadArrayElem()
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
x.PolicyConfigFile = "" x.DeschedulingInterval = 0
} else { } else {
yyv27 := &x.PolicyConfigFile yyv27 := &x.DeschedulingInterval
yym28 := z.DecBinary() yym28 := z.DecBinary()
_ = yym28 _ = yym28
if false { if false {
} else if yyxt28 := z.Extension(z.I2Rtid(yyv27)); yyxt28 != nil {
z.DecExtension(yyv27, yyxt28)
} else { } else {
*((*string)(yyv27)) = r.DecodeString() *((*int64)(yyv27)) = int64(r.DecodeInt(64))
} }
} }
yyj18++ yyj22++
if yyhl18 { if yyhl22 {
yyb18 = yyj18 > l yyb22 = yyj22 > l
} else { } else {
yyb18 = r.CheckBreak() yyb22 = r.CheckBreak()
} }
if yyb18 { if yyb22 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
return return
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) r.ReadArrayElem()
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
x.DryRun = false x.KubeconfigFile = ""
} else { } else {
yyv29 := &x.DryRun yyv29 := &x.KubeconfigFile
yym30 := z.DecBinary() yym30 := z.DecBinary()
_ = yym30 _ = yym30
if false { if false {
} else { } else {
*((*bool)(yyv29)) = r.DecodeBool() *((*string)(yyv29)) = r.DecodeString()
} }
} }
yyj18++ yyj22++
if yyhl18 { if yyhl22 {
yyb18 = yyj18 > l yyb22 = yyj22 > l
} else { } else {
yyb18 = r.CheckBreak() yyb22 = r.CheckBreak()
} }
if yyb18 { if yyb22 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
return return
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) r.ReadArrayElem()
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
x.NodeSelector = "" x.PolicyConfigFile = ""
} else { } else {
yyv31 := &x.NodeSelector yyv31 := &x.PolicyConfigFile
yym32 := z.DecBinary() yym32 := z.DecBinary()
_ = yym32 _ = yym32
if false { if false {
@@ -589,18 +619,106 @@ func (x *DeschedulerConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.
*((*string)(yyv31)) = r.DecodeString() *((*string)(yyv31)) = r.DecodeString()
} }
} }
for { yyj22++
yyj18++ if yyhl22 {
if yyhl18 { yyb22 = yyj22 > l
yyb18 = yyj18 > l } else {
yyb22 = r.CheckBreak()
}
if yyb22 {
r.ReadArrayEnd()
return
}
r.ReadArrayElem()
if r.TryDecodeAsNil() {
x.DryRun = false
} else {
yyv33 := &x.DryRun
yym34 := z.DecBinary()
_ = yym34
if false {
} else { } else {
yyb18 = r.CheckBreak() *((*bool)(yyv33)) = r.DecodeBool()
} }
if yyb18 { }
yyj22++
if yyhl22 {
yyb22 = yyj22 > l
} else {
yyb22 = r.CheckBreak()
}
if yyb22 {
r.ReadArrayEnd()
return
}
r.ReadArrayElem()
if r.TryDecodeAsNil() {
x.NodeSelector = ""
} else {
yyv35 := &x.NodeSelector
yym36 := z.DecBinary()
_ = yym36
if false {
} else {
*((*string)(yyv35)) = r.DecodeString()
}
}
yyj22++
if yyhl22 {
yyb22 = yyj22 > l
} else {
yyb22 = r.CheckBreak()
}
if yyb22 {
r.ReadArrayEnd()
return
}
r.ReadArrayElem()
if r.TryDecodeAsNil() {
x.MaxNoOfPodsToEvictPerNode = 0
} else {
yyv37 := &x.MaxNoOfPodsToEvictPerNode
yym38 := z.DecBinary()
_ = yym38
if false {
} else {
*((*int)(yyv37)) = int(r.DecodeInt(codecSelferBitsize1234))
}
}
yyj22++
if yyhl22 {
yyb22 = yyj22 > l
} else {
yyb22 = r.CheckBreak()
}
if yyb22 {
r.ReadArrayEnd()
return
}
r.ReadArrayElem()
if r.TryDecodeAsNil() {
x.EvictLocalStoragePods = false
} else {
yyv39 := &x.EvictLocalStoragePods
yym40 := z.DecBinary()
_ = yym40
if false {
} else {
*((*bool)(yyv39)) = r.DecodeBool()
}
}
for {
yyj22++
if yyhl22 {
yyb22 = yyj22 > l
} else {
yyb22 = r.CheckBreak()
}
if yyb22 {
break break
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) r.ReadArrayElem()
z.DecStructFieldNotFound(yyj18-1, "") z.DecStructFieldNotFound(yyj22-1, "")
} }
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) r.ReadArrayEnd()
} }

View File

@@ -22,6 +22,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type DeschedulerConfiguration struct { type DeschedulerConfiguration struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
@@ -40,4 +42,10 @@ type DeschedulerConfiguration struct {
// Node selectors // Node selectors
NodeSelector string `json:"nodeSelector,omitempty"` NodeSelector string `json:"nodeSelector,omitempty"`
// MaxNoOfPodsToEvictPerNode restricts maximum of pods to be evicted per node.
MaxNoOfPodsToEvictPerNode int `json:"maxNoOfPodsToEvictPerNode,omitempty"`
// EvictLocalStoragePods allows pods using local storage to be evicted.
EvictLocalStoragePods bool `json:"evictLocalStoragePods,omitempty"`
} }

View File

@@ -1,7 +1,7 @@
// +build !ignore_autogenerated // +build !ignore_autogenerated
/* /*
Copyright 2017 The Kubernetes Authors. Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -21,14 +21,15 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
time "time"
componentconfig "github.com/kubernetes-incubator/descheduler/pkg/apis/componentconfig" componentconfig "github.com/kubernetes-incubator/descheduler/pkg/apis/componentconfig"
conversion "k8s.io/apimachinery/pkg/conversion" conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
time "time"
) )
func init() { func init() {
SchemeBuilder.Register(RegisterConversions) localSchemeBuilder.Register(RegisterConversions)
} }
// RegisterConversions adds conversion functions to the given scheme. // RegisterConversions adds conversion functions to the given scheme.
@@ -46,6 +47,8 @@ func autoConvert_v1alpha1_DeschedulerConfiguration_To_componentconfig_Deschedule
out.PolicyConfigFile = in.PolicyConfigFile out.PolicyConfigFile = in.PolicyConfigFile
out.DryRun = in.DryRun out.DryRun = in.DryRun
out.NodeSelector = in.NodeSelector out.NodeSelector = in.NodeSelector
out.MaxNoOfPodsToEvictPerNode = in.MaxNoOfPodsToEvictPerNode
out.EvictLocalStoragePods = in.EvictLocalStoragePods
return nil return nil
} }
@@ -60,6 +63,8 @@ func autoConvert_componentconfig_DeschedulerConfiguration_To_v1alpha1_Deschedule
out.PolicyConfigFile = in.PolicyConfigFile out.PolicyConfigFile = in.PolicyConfigFile
out.DryRun = in.DryRun out.DryRun = in.DryRun
out.NodeSelector = in.NodeSelector out.NodeSelector = in.NodeSelector
out.MaxNoOfPodsToEvictPerNode = in.MaxNoOfPodsToEvictPerNode
out.EvictLocalStoragePods = in.EvictLocalStoragePods
return nil return nil
} }

View File

@@ -1,7 +1,7 @@
// +build !ignore_autogenerated // +build !ignore_autogenerated
/* /*
Copyright 2017 The Kubernetes Authors. Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -21,29 +21,31 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
reflect "reflect"
) )
func init() { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
SchemeBuilder.Register(RegisterDeepCopies) func (in *DeschedulerConfiguration) DeepCopyInto(out *DeschedulerConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
return
} }
// RegisterDeepCopies adds deep-copy functions to the given scheme. Public // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeschedulerConfiguration.
// to allow building arbitrary schemes. func (in *DeschedulerConfiguration) DeepCopy() *DeschedulerConfiguration {
func RegisterDeepCopies(scheme *runtime.Scheme) error { if in == nil {
return scheme.AddGeneratedDeepCopyFuncs( return nil
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_DeschedulerConfiguration, InType: reflect.TypeOf(&DeschedulerConfiguration{})}, }
) out := new(DeschedulerConfiguration)
in.DeepCopyInto(out)
return out
} }
// DeepCopy_v1alpha1_DeschedulerConfiguration is an autogenerated deepcopy function. // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func DeepCopy_v1alpha1_DeschedulerConfiguration(in interface{}, out interface{}, c *conversion.Cloner) error { func (in *DeschedulerConfiguration) DeepCopyObject() runtime.Object {
{ if c := in.DeepCopy(); c != nil {
in := in.(*DeschedulerConfiguration) return c
out := out.(*DeschedulerConfiguration) } else {
*out = *in
return nil return nil
} }
} }

View File

@@ -1,7 +1,7 @@
// +build !ignore_autogenerated // +build !ignore_autogenerated
/* /*
Copyright 2017 The Kubernetes Authors. Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,7 +1,7 @@
// +build !ignore_autogenerated // +build !ignore_autogenerated
/* /*
Copyright 2017 The Kubernetes Authors. Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -21,29 +21,31 @@ limitations under the License.
package componentconfig package componentconfig
import ( import (
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
reflect "reflect"
) )
func init() { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
SchemeBuilder.Register(RegisterDeepCopies) func (in *DeschedulerConfiguration) DeepCopyInto(out *DeschedulerConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
return
} }
// RegisterDeepCopies adds deep-copy functions to the given scheme. Public // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeschedulerConfiguration.
// to allow building arbitrary schemes. func (in *DeschedulerConfiguration) DeepCopy() *DeschedulerConfiguration {
func RegisterDeepCopies(scheme *runtime.Scheme) error { if in == nil {
return scheme.AddGeneratedDeepCopyFuncs( return nil
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_componentconfig_DeschedulerConfiguration, InType: reflect.TypeOf(&DeschedulerConfiguration{})}, }
) out := new(DeschedulerConfiguration)
in.DeepCopyInto(out)
return out
} }
// DeepCopy_componentconfig_DeschedulerConfiguration is an autogenerated deepcopy function. // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func DeepCopy_componentconfig_DeschedulerConfiguration(in interface{}, out interface{}, c *conversion.Cloner) error { func (in *DeschedulerConfiguration) DeepCopyObject() runtime.Object {
{ if c := in.DeepCopy(); c != nil {
in := in.(*DeschedulerConfiguration) return c
out := out.(*DeschedulerConfiguration) } else {
*out = *in
return nil return nil
} }
} }

View File

@@ -19,9 +19,10 @@ package client
import ( import (
"fmt" "fmt"
clientset "k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
) )
func CreateClient(kubeconfig string) (clientset.Interface, error) { func CreateClient(kubeconfig string) (clientset.Interface, error) {

View File

@@ -55,14 +55,16 @@ func Run(rs *options.DeschedulerServer) error {
return err return err
} }
if len(nodes) == 0 { if len(nodes) <= 1 {
glog.V(1).Infof("node list is empty") glog.V(1).Infof("The cluster size is 0 or 1 meaning eviction causes service disruption or degradation. So aborting..")
return nil return nil
} }
strategies.RemoveDuplicatePods(rs, deschedulerPolicy.Strategies["RemoveDuplicates"], evictionPolicyGroupVersion, nodes) nodePodCount := strategies.InitializeNodePodCount(nodes)
strategies.LowNodeUtilization(rs, deschedulerPolicy.Strategies["LowNodeUtilization"], evictionPolicyGroupVersion, nodes) strategies.RemoveDuplicatePods(rs, deschedulerPolicy.Strategies["RemoveDuplicates"], evictionPolicyGroupVersion, nodes, nodePodCount)
strategies.RemovePodsViolatingInterPodAntiAffinity(rs, deschedulerPolicy.Strategies["RemovePodsViolatingInterPodAntiAffinity"], evictionPolicyGroupVersion, nodes) 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 return nil
} }

View File

@@ -19,11 +19,11 @@ package evictions
import ( import (
"fmt" "fmt"
"k8s.io/api/core/v1"
policy "k8s.io/api/policy/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/api/v1" clientset "k8s.io/client-go/kubernetes"
policy "k8s.io/kubernetes/pkg/apis/policy/v1beta1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
eutils "github.com/kubernetes-incubator/descheduler/pkg/descheduler/evictions/utils" eutils "github.com/kubernetes-incubator/descheduler/pkg/descheduler/evictions/utils"
) )

View File

@@ -18,10 +18,10 @@ package evictions
import ( import (
"github.com/kubernetes-incubator/descheduler/test" "github.com/kubernetes-incubator/descheduler/test"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing" core "k8s.io/client-go/testing"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
"testing" "testing"
) )

View File

@@ -17,7 +17,7 @@ limitations under the License.
package utils package utils
import ( import (
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset" clientset "k8s.io/client-go/kubernetes"
) )
const ( const (

View File

@@ -20,13 +20,14 @@ import (
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/kubernetes-incubator/descheduler/pkg/utils"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels" "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/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 // ReadyNodes returns ready nodes irrespective of whether they are
@@ -81,11 +82,15 @@ func GetNodeLister(client clientset.Interface, stopChannel <-chan struct{}) core
store := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) store := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
nodeLister := corelisters.NewNodeLister(store) nodeLister := corelisters.NewNodeLister(store)
reflector := cache.NewReflector(listWatcher, &v1.Node{}, store, time.Hour) 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 return nodeLister
} }
// IsReady checks if the descheduler could run against given node.
func IsReady(node *v1.Node) bool { func IsReady(node *v1.Node) bool {
for i := range node.Status.Conditions { for i := range node.Status.Conditions {
cond := &node.Status.Conditions[i] cond := &node.Status.Conditions[i]
@@ -111,3 +116,52 @@ func IsReady(node *v1.Node) bool {
}*/ }*/
return true 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
}

View File

@@ -20,8 +20,9 @@ import (
"testing" "testing"
"github.com/kubernetes-incubator/descheduler/test" "github.com/kubernetes-incubator/descheduler/test"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/api/core/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
) )
func TestReadyNodes(t *testing.T) { func TestReadyNodes(t *testing.T) {
@@ -72,3 +73,275 @@ func TestReadyNodesWithNodeSelector(t *testing.T) {
t.Errorf("Expected node1, got %s", nodes[0].Name) 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)
}
}
}

View File

@@ -17,19 +17,74 @@ limitations under the License.
package pod package pod
import ( import (
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime" clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/api" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/apis/core/v1/helper/qos"
"k8s.io/kubernetes/pkg/api/v1/helper/qos"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/kubelet/types" "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, evictLocalStoragePods bool) bool {
ownerRefList := OwnerRef(pod)
if IsMirrorPod(pod) || (!evictLocalStoragePods && 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, evictLocalStoragePods bool) ([]*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, evictLocalStoragePods) {
continue
} else {
evictablePods = append(evictablePods, pod)
}
}
return evictablePods, nil
}
func ListPodsOnANode(client clientset.Interface, node *v1.Node) ([]*v1.Pod, error) { 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( 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 { if err != nil {
return []*v1.Pod{}, err return []*v1.Pod{}, err
} }
@@ -38,7 +93,6 @@ func ListPodsOnANode(client clientset.Interface, node *v1.Node) ([]*v1.Pod, erro
for i := range podList.Items { for i := range podList.Items {
pods = append(pods, &podList.Items[i]) pods = append(pods, &podList.Items[i])
} }
return pods, nil return pods, nil
} }
@@ -58,9 +112,11 @@ func IsGuaranteedPod(pod *v1.Pod) bool {
return qos.GetPodQOS(pod) == v1.PodQOSGuaranteed return qos.GetPodQOS(pod) == v1.PodQOSGuaranteed
} }
func IsDaemonsetPod(sr *v1.SerializedReference) bool { func IsDaemonsetPod(ownerRefList []metav1.OwnerReference) bool {
if sr != nil { for _, ownerRef := range ownerRefList {
return sr.Reference.Kind == "DaemonSet" if ownerRef.Kind == "DaemonSet" {
return true
}
} }
return false return false
} }
@@ -81,15 +137,7 @@ func IsPodWithLocalStorage(pod *v1.Pod) bool {
return false return false
} }
// CreatorRef returns the kind of the creator reference of the pod. // OwnerRef returns the ownerRefList for the pod.
func CreatorRef(pod *v1.Pod) (*v1.SerializedReference, error) { func OwnerRef(pod *v1.Pod) []metav1.OwnerReference {
creatorRef, found := pod.ObjectMeta.Annotations[v1.CreatedByAnnotation] return pod.ObjectMeta.GetOwnerReferences()
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
} }

View File

@@ -20,8 +20,8 @@ import (
"testing" "testing"
"github.com/kubernetes-incubator/descheduler/test" "github.com/kubernetes-incubator/descheduler/test"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/v1"
) )
func TestPodTypes(t *testing.T) { func TestPodTypes(t *testing.T) {
@@ -33,13 +33,18 @@ func TestPodTypes(t *testing.T) {
p3 := test.BuildTestPod("p3", 400, 0, n1.Name) p3 := test.BuildTestPod("p3", 400, 0, n1.Name)
p4 := test.BuildTestPod("p4", 400, 0, n1.Name) p4 := test.BuildTestPod("p4", 400, 0, n1.Name)
p5 := test.BuildTestPod("p5", 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. // The following 4 pods won't get evicted.
// A daemonset. // A daemonset.
p2.Annotations = test.GetDaemonSetAnnotation() //p2.Annotations = test.GetDaemonSetAnnotation()
p2.ObjectMeta.OwnerReferences = test.GetDaemonSetOwnerRefList()
// A pod with local storage. // A pod with local storage.
p3.Annotations = test.GetNormalPodAnnotation() p3.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
p3.Spec.Volumes = []v1.Volume{ p3.Spec.Volumes = []v1.Volume{
{ {
Name: "sample", Name: "sample",
@@ -64,13 +69,16 @@ func TestPodTypes(t *testing.T) {
if !IsPodWithLocalStorage(p3) { if !IsPodWithLocalStorage(p3) {
t.Errorf("Expected p3 to be a pod with local storage.") t.Errorf("Expected p3 to be a pod with local storage.")
} }
sr, _ := CreatorRef(p2) ownerRefList := OwnerRef(p2)
if !IsDaemonsetPod(sr) { if !IsDaemonsetPod(ownerRefList) {
t.Errorf("Expected p2 to be a daemonset pod.") t.Errorf("Expected p2 to be a daemonset pod.")
} }
sr, _ = CreatorRef(p1) ownerRefList = OwnerRef(p1)
if IsDaemonsetPod(sr) || IsPodWithLocalStorage(p1) || IsCriticalPod(p1) || IsMirrorPod(p1) { if IsDaemonsetPod(ownerRefList) || IsPodWithLocalStorage(p1) || IsCriticalPod(p1) || IsMirrorPod(p1) {
t.Errorf("Expected p1 to be a normal pod.") t.Errorf("Expected p1 to be a normal pod.")
} }
if !IsLatencySensitivePod(p6) {
t.Errorf("Expected p6 to be latency sensitive pod")
}
} }

View File

@@ -17,12 +17,12 @@ limitations under the License.
package strategies package strategies
import ( import (
"github.com/golang/glog"
"strings" "strings"
"k8s.io/kubernetes/pkg/api/v1" "github.com/golang/glog"
//TODO: Change to client-go instead of generated clientset.
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset" "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/cmd/descheduler/app/options"
"github.com/kubernetes-incubator/descheduler/pkg/api" "github.com/kubernetes-incubator/descheduler/pkg/api"
@@ -36,41 +36,45 @@ type DuplicatePodsMap map[string][]*v1.Pod
// RemoveDuplicatePods removes the duplicate pods on node. This strategy evicts all duplicate pods on node. // 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 // 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. // 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 { if !strategy.Enabled {
return return
} }
deleteDuplicatePods(ds.Client, policyGroupVersion, nodes, ds.DryRun) deleteDuplicatePods(ds.Client, policyGroupVersion, nodes, ds.DryRun, nodepodCount, ds.MaxNoOfPodsToEvictPerNode, ds.EvictLocalStoragePods)
} }
// deleteDuplicatePods evicts the pod from node and returns the count of evicted pods. // 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, evictLocalStoragePods bool) int {
podsEvicted := 0 podsEvicted := 0
for _, node := range nodes { for _, node := range nodes {
glog.V(1).Infof("Processing node: %#v", node.Name) glog.V(1).Infof("Processing node: %#v", node.Name)
dpm := ListDuplicatePodsOnANode(client, node) dpm := ListDuplicatePodsOnANode(client, node, evictLocalStoragePods)
for creator, pods := range dpm { for creator, pods := range dpm {
if len(pods) > 1 { if len(pods) > 1 {
glog.V(1).Infof("%#v", creator) glog.V(1).Infof("%#v", creator)
// i = 0 does not evict the first pod // i = 0 does not evict the first pod
for i := 1; i < len(pods); i++ { for i := 1; i < len(pods); i++ {
if maxPodsToEvict > 0 && nodepodCount[node]+1 > maxPodsToEvict {
break
}
success, err := evictions.EvictPod(client, pods[i], policyGroupVersion, dryRun) success, err := evictions.EvictPod(client, pods[i], policyGroupVersion, dryRun)
if !success { if !success {
glog.Infof("Error when evicting pod: %#v (%#v)", pods[i].Name, err) glog.Infof("Error when evicting pod: %#v (%#v)", pods[i].Name, err)
} else { } else {
podsEvicted++ nodepodCount[node]++
glog.V(1).Infof("Evicted pod: %#v (%#v)", pods[i].Name, err) glog.V(1).Infof("Evicted pod: %#v (%#v)", pods[i].Name, err)
} }
} }
} }
} }
podsEvicted += nodepodCount[node]
} }
return podsEvicted return podsEvicted
} }
// ListDuplicatePodsOnANode lists duplicate pods on a given node. // ListDuplicatePodsOnANode lists duplicate pods on a given node.
func ListDuplicatePodsOnANode(client clientset.Interface, node *v1.Node) DuplicatePodsMap { func ListDuplicatePodsOnANode(client clientset.Interface, node *v1.Node, evictLocalStoragePods bool) DuplicatePodsMap {
pods, err := podutil.ListPodsOnANode(client, node) pods, err := podutil.ListEvictablePodsOnNode(client, node, evictLocalStoragePods)
if err != nil { if err != nil {
return nil return nil
} }
@@ -81,15 +85,14 @@ func ListDuplicatePodsOnANode(client clientset.Interface, node *v1.Node) Duplica
func FindDuplicatePods(pods []*v1.Pod) DuplicatePodsMap { func FindDuplicatePods(pods []*v1.Pod) DuplicatePodsMap {
dpm := DuplicatePodsMap{} dpm := DuplicatePodsMap{}
for _, pod := range pods { for _, pod := range pods {
sr, err := podutil.CreatorRef(pod) // Ignoring the error here as in the ListDuplicatePodsOnNode function we call ListEvictablePodsOnNode
if err != nil || sr == nil { // which checks for error.
continue 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 return dpm
} }

View File

@@ -20,11 +20,11 @@ import (
"testing" "testing"
"github.com/kubernetes-incubator/descheduler/test" "github.com/kubernetes-incubator/descheduler/test"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing" 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. //TODO:@ravisantoshgudimetla This could be made table driven.
@@ -37,17 +37,21 @@ func TestFindDuplicatePods(t *testing.T) {
p5 := test.BuildTestPod("p5", 100, 0, node.Name) p5 := test.BuildTestPod("p5", 100, 0, node.Name)
p6 := test.BuildTestPod("p6", 100, 0, node.Name) p6 := test.BuildTestPod("p6", 100, 0, node.Name)
p7 := test.BuildTestPod("p7", 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. // All the following pods expect for one will be evicted.
p1.Annotations = test.GetReplicaSetAnnotation() p1.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
p2.Annotations = test.GetReplicaSetAnnotation() p2.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
p3.Annotations = test.GetReplicaSetAnnotation() p3.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
p8.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
p9.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
// The following 4 pods won't get evicted. // The following 4 pods won't get evicted.
// A daemonset. // A daemonset.
p4.Annotations = test.GetDaemonSetAnnotation() p4.ObjectMeta.OwnerReferences = test.GetDaemonSetOwnerRefList()
// A pod with local storage. // A pod with local storage.
p5.Annotations = test.GetNormalPodAnnotation() p5.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
p5.Spec.Volumes = []v1.Volume{ p5.Spec.Volumes = []v1.Volume{
{ {
Name: "sample", Name: "sample",
@@ -66,12 +70,14 @@ func TestFindDuplicatePods(t *testing.T) {
expectedEvictedPodCount := 2 expectedEvictedPodCount := 2
fakeClient := &fake.Clientset{} fakeClient := &fake.Clientset{}
fakeClient.Fake.AddReactor("list", "pods", func(action core.Action) (bool, runtime.Object, error) { 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) { fakeClient.Fake.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) {
return true, node, nil 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, false)
if podsEvicted != expectedEvictedPodCount { if podsEvicted != expectedEvictedPodCount {
t.Errorf("Unexpected no of pods evicted") t.Errorf("Unexpected no of pods evicted")
} }

View File

@@ -20,28 +20,31 @@ import (
"sort" "sort"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "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" 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/cmd/descheduler/app/options"
"github.com/kubernetes-incubator/descheduler/pkg/api" "github.com/kubernetes-incubator/descheduler/pkg/api"
"github.com/kubernetes-incubator/descheduler/pkg/descheduler/evictions" "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" podutil "github.com/kubernetes-incubator/descheduler/pkg/descheduler/pod"
) )
type NodeUsageMap struct { type NodeUsageMap struct {
node *v1.Node node *v1.Node
usage api.ResourceThresholds usage api.ResourceThresholds
allPods []*v1.Pod
nonRemovablePods []*v1.Pod nonRemovablePods []*v1.Pod
bePods []*v1.Pod bePods []*v1.Pod
bPods []*v1.Pod bPods []*v1.Pod
gPods []*v1.Pod gPods []*v1.Pod
} }
type NodePodsMap map[*v1.Node][]*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 { if !strategy.Enabled {
return return
} }
@@ -57,42 +60,61 @@ func LowNodeUtilization(ds *options.DeschedulerServer, strategy api.DeschedulerS
return return
} }
npm := CreateNodePodsMap(ds.Client, nodes) npm := createNodePodsMap(ds.Client, nodes)
lowNodes, targetNodes, _ := classifyNodes(npm, thresholds, targetThresholds) lowNodes, targetNodes := classifyNodes(npm, thresholds, targetThresholds, ds.EvictLocalStoragePods)
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 { if len(lowNodes) == 0 {
glog.V(1).Infof("No node is underutilized") glog.V(1).Infof("No node is underutilized, nothing to do here, you might tune your thersholds further")
return
} else if len(lowNodes) < strategy.Params.NodeResourceUtilizationThresholds.NumberOfNodes {
glog.V(1).Infof("number of nodes underutilized is less than NumberOfNodes")
return
} else if len(lowNodes) == len(nodes) {
glog.V(1).Infof("all nodes are underutilized")
return
} else if len(targetNodes) == 0 {
glog.V(1).Infof("no node is above target utilization")
return 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 { func validateThresholds(thresholds api.ResourceThresholds) bool {
if thresholds == nil { if thresholds == nil || len(thresholds) == 0 {
glog.V(1).Infof("no resource threshold is configured") glog.V(1).Infof("no resource threshold is configured")
return false return false
} }
found := false
for name := range thresholds { for name := range thresholds {
if name == v1.ResourceCPU || name == v1.ResourceMemory || name == v1.ResourcePods { switch name {
found = true case v1.ResourceCPU:
break 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 { return true
glog.V(1).Infof("one of cpu, memory, or pods resource threshold must be configured")
return false
}
return found
} }
//This function could be merged into above once we are clear. //This function could be merged into above once we are clear.
@@ -107,26 +129,33 @@ func validateTargetThresholds(targetThresholds api.ResourceThresholds) bool {
return true return true
} }
func classifyNodes(npm NodePodsMap, thresholds api.ResourceThresholds, targetThresholds api.ResourceThresholds) ([]NodeUsageMap, []NodeUsageMap, []NodeUsageMap) { // classifyNodes classifies the nodes into low-utilization or high-utilization nodes. If a node lies between
lowNodes, targetNodes, otherNodes := []NodeUsageMap{}, []NodeUsageMap{}, []NodeUsageMap{} // low and high thresholds, it is simply ignored.
func classifyNodes(npm NodePodsMap, thresholds api.ResourceThresholds, targetThresholds api.ResourceThresholds, evictLocalStoragePods bool) ([]NodeUsageMap, []NodeUsageMap) {
lowNodes, targetNodes := []NodeUsageMap{}, []NodeUsageMap{}
for node, pods := range npm { for node, pods := range npm {
usage, nonRemovablePods, bePods, bPods, gPods := NodeUtilization(node, pods) usage, allPods, nonRemovablePods, bePods, bPods, gPods := NodeUtilization(node, pods, evictLocalStoragePods)
nuMap := NodeUsageMap{node, usage, nonRemovablePods, bePods, bPods, gPods} nuMap := NodeUsageMap{node, usage, allPods, nonRemovablePods, bePods, bPods, gPods}
glog.V(1).Infof("Node %#v usage: %#v", node.Name, usage)
if IsNodeWithLowUtilization(usage, thresholds) { // 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) lowNodes = append(lowNodes, nuMap)
} else if IsNodeAboveTargetUtilization(usage, targetThresholds) { } else if IsNodeAboveTargetUtilization(usage, targetThresholds) {
glog.V(2).Infof("Node %#v is over utilized with usage: %#v", node.Name, usage)
targetNodes = append(targetNodes, nuMap) targetNodes = append(targetNodes, nuMap)
} else { } else {
// Seems we don't need to collect them? glog.V(2).Infof("Node %#v is appropriately utilized with usage: %#v", node.Name, usage)
otherNodes = append(otherNodes, nuMap)
} }
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 podsEvicted := 0
SortNodesByUsage(targetNodes) SortNodesByUsage(targetNodes)
@@ -155,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 { for _, node := range targetNodes {
nodeCapacity := node.node.Status.Capacity nodeCapacity := node.node.Status.Capacity
if len(node.node.Status.Allocatable) > 0 { if len(node.node.Status.Allocatable) > 0 {
nodeCapacity = node.node.Status.Allocatable nodeCapacity = node.node.Status.Allocatable
} }
glog.V(1).Infof("evicting pods from node %#v with usage: %#v", node.node.Name, node.usage) glog.V(3).Infof("evicting pods from node %#v with usage: %#v", node.node.Name, node.usage)
// evict best effort pods currentPodsEvicted := nodepodCount[node.node]
evictPods(node.bePods, client, evictionPolicyGroupVersion, targetThresholds, nodeCapacity, node.usage, &totalPods, &totalCpu, &totalMem, &podsEvicted, dryRun)
// evict burstable pods // Check if one pod has priority, if yes, assume that all pods have priority and evict pods based on priority.
evictPods(node.bPods, client, evictionPolicyGroupVersion, targetThresholds, nodeCapacity, node.usage, &totalPods, &totalCpu, &totalMem, &podsEvicted, dryRun) if node.allPods[0].Spec.Priority != nil {
// evict guaranteed pods glog.V(1).Infof("All pods have priority associated with them. Evicting pods based on priority")
evictPods(node.gPods, client, evictionPolicyGroupVersion, targetThresholds, nodeCapacity, node.usage, &totalPods, &totalCpu, &totalMem, &podsEvicted, dryRun) 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, &currentPodsEvicted, 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, &currentPodsEvicted, dryRun, maxPodsToEvict)
// evict burstable pods
evictPods(node.bPods, client, evictionPolicyGroupVersion, targetThresholds, nodeCapacity, node.usage, &totalPods, &totalCpu, &totalMem, &currentPodsEvicted, dryRun, maxPodsToEvict)
// evict guaranteed pods
evictPods(node.gPods, client, evictionPolicyGroupVersion, targetThresholds, nodeCapacity, node.usage, &totalPods, &totalCpu, &totalMem, &currentPodsEvicted, 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 return podsEvicted
} }
@@ -181,17 +233,20 @@ func evictPods(inputPods []*v1.Pod,
totalCpu *float64, totalCpu *float64,
totalMem *float64, totalMem *float64,
podsEvicted *int, podsEvicted *int,
dryRun bool) { dryRun bool, maxPodsToEvict int) {
if IsNodeAboveTargetUtilization(nodeUsage, targetThresholds) && (*totalPods > 0 || *totalCpu > 0 || *totalMem > 0) { if IsNodeAboveTargetUtilization(nodeUsage, targetThresholds) && (*totalPods > 0 || *totalCpu > 0 || *totalMem > 0) {
onePodPercentage := api.Percentage((float64(1) * 100) / float64(nodeCapacity.Pods().Value())) onePodPercentage := api.Percentage((float64(1) * 100) / float64(nodeCapacity.Pods().Value()))
for _, pod := range inputPods { for _, pod := range inputPods {
if maxPodsToEvict > 0 && *podsEvicted+1 > maxPodsToEvict {
break
}
cUsage := helper.GetResourceRequest(pod, v1.ResourceCPU) cUsage := helper.GetResourceRequest(pod, v1.ResourceCPU)
mUsage := helper.GetResourceRequest(pod, v1.ResourceMemory) mUsage := helper.GetResourceRequest(pod, v1.ResourceMemory)
success, err := evictions.EvictPod(client, pod, evictionPolicyGroupVersion, dryRun) success, err := evictions.EvictPod(client, pod, evictionPolicyGroupVersion, dryRun)
if !success { if !success {
glog.Infof("Error when evicting pod: %#v (%#v)", pod.Name, err) glog.Warningf("Error when evicting pod: %#v (%#v)", pod.Name, err)
} else { } else {
glog.V(1).Infof("Evicted pod: %#v (%#v)", pod.Name, err) glog.V(3).Infof("Evicted pod: %#v (%#v)", pod.Name, err)
// update remaining pods // update remaining pods
*podsEvicted++ *podsEvicted++
nodeUsage[v1.ResourcePods] -= onePodPercentage nodeUsage[v1.ResourcePods] -= onePodPercentage
@@ -205,7 +260,7 @@ func evictPods(inputPods []*v1.Pod,
*totalMem -= float64(mUsage) *totalMem -= float64(mUsage)
nodeUsage[v1.ResourceMemory] -= api.Percentage(float64(mUsage) / float64(nodeCapacity.Memory().Value()) * 100) nodeUsage[v1.ResourceMemory] -= api.Percentage(float64(mUsage) / float64(nodeCapacity.Memory().Value()) * 100)
glog.V(1).Infof("updated node usage: %#v", 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 // 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) { if !IsNodeAboveTargetUtilization(nodeUsage, targetThresholds) || (*totalPods <= 0 && *totalCpu <= 0 && *totalMem <= 0) {
break break
@@ -233,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{} npm := NodePodsMap{}
for _, node := range nodes { for _, node := range nodes {
pods, err := podutil.ListPodsOnANode(client, node) pods, err := podutil.ListPodsOnANode(client, node)
if err != nil { if err != nil {
glog.Infof("node %s will not be processed, error in accessing its pods (%#v)", node.Name, err) glog.Warningf("node %s will not be processed, error in accessing its pods (%#v)", node.Name, err)
} else { } else {
npm[node] = pods npm[node] = pods
} }
@@ -272,19 +350,16 @@ func IsNodeWithLowUtilization(nodeThresholds api.ResourceThresholds, thresholds
return true 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, evictLocalStoragePods bool) (api.ResourceThresholds, []*v1.Pod, []*v1.Pod, []*v1.Pod, []*v1.Pod, []*v1.Pod) {
bePods := []*v1.Pod{} bePods := []*v1.Pod{}
nonRemovablePods := []*v1.Pod{} nonRemovablePods := []*v1.Pod{}
bPods := []*v1.Pod{} bPods := []*v1.Pod{}
gPods := []*v1.Pod{} gPods := []*v1.Pod{}
totalReqs := map[v1.ResourceName]resource.Quantity{} totalReqs := map[v1.ResourceName]resource.Quantity{}
for _, pod := range pods { for _, pod := range pods {
sr, err := podutil.CreatorRef(pod) // We need to compute the usage of nonRemovablePods unless it is a best effort pod. So, cannot use podutil.ListEvictablePodsOnNode
if err != nil { if !podutil.IsEvictable(pod, evictLocalStoragePods) {
sr = nil
}
if podutil.IsMirrorPod(pod) || podutil.IsPodWithLocalStorage(pod) || sr == nil || podutil.IsDaemonsetPod(sr) || podutil.IsCriticalPod(pod) {
nonRemovablePods = append(nonRemovablePods, pod) nonRemovablePods = append(nonRemovablePods, pod)
if podutil.IsBestEffortPod(pod) { if podutil.IsBestEffortPod(pod) {
continue continue
@@ -298,11 +373,7 @@ func NodeUtilization(node *v1.Node, pods []*v1.Pod) (api.ResourceThresholds, []*
gPods = append(gPods, pod) gPods = append(gPods, pod)
} }
req, _, err := helper.PodRequestsAndLimits(pod) req, _ := helper.PodRequestsAndLimits(pod)
if err != nil {
glog.Infof("Error computing resource usage of pod, ignoring: %#v", pod.Name)
continue
}
for name, quantity := range req { for name, quantity := range req {
if name == v1.ResourceCPU || name == v1.ResourceMemory { if name == v1.ResourceCPU || name == v1.ResourceMemory {
if value, ok := totalReqs[name]; !ok { if value, ok := totalReqs[name]; !ok {
@@ -327,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.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.ResourceMemory] = api.Percentage(float64(totalMemReq.Value()) / float64(nodeCapacity.Memory().Value()) * 100)
usage[v1.ResourcePods] = api.Percentage((float64(totalPods) * 100) / float64(nodeCapacity.Pods().Value())) 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
} }

View File

@@ -18,19 +18,21 @@ package strategies
import ( import (
"fmt" "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" "strings"
"testing" "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. // TODO: Make this table driven.
func TestLowNodeUtilization(t *testing.T) { func TestLowNodeUtilizationWithoutPriority(t *testing.T) {
var thresholds = make(api.ResourceThresholds) var thresholds = make(api.ResourceThresholds)
var targetThresholds = make(api.ResourceThresholds) var targetThresholds = make(api.ResourceThresholds)
thresholds[v1.ResourceCPU] = 30 thresholds[v1.ResourceCPU] = 30
@@ -40,6 +42,9 @@ func TestLowNodeUtilization(t *testing.T) {
n1 := test.BuildTestNode("n1", 4000, 3000, 9) n1 := test.BuildTestNode("n1", 4000, 3000, 9)
n2 := test.BuildTestNode("n2", 4000, 3000, 10) 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 := test.BuildTestPod("p1", 400, 0, n1.Name)
p2 := test.BuildTestPod("p2", 400, 0, n1.Name) p2 := test.BuildTestPod("p2", 400, 0, n1.Name)
p3 := test.BuildTestPod("p3", 400, 0, n1.Name) p3 := test.BuildTestPod("p3", 400, 0, n1.Name)
@@ -51,16 +56,16 @@ func TestLowNodeUtilization(t *testing.T) {
p7 := test.BuildTestPod("p7", 400, 0, n1.Name) p7 := test.BuildTestPod("p7", 400, 0, n1.Name)
p8 := test.BuildTestPod("p8", 400, 0, n1.Name) p8 := test.BuildTestPod("p8", 400, 0, n1.Name)
p1.Annotations = test.GetReplicaSetAnnotation() p1.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
p2.Annotations = test.GetReplicaSetAnnotation() p2.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
p3.Annotations = test.GetReplicaSetAnnotation() p3.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
p4.Annotations = test.GetReplicaSetAnnotation() p4.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
p5.Annotations = test.GetReplicaSetAnnotation() p5.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
// The following 4 pods won't get evicted. // The following 4 pods won't get evicted.
// A daemonset. // A daemonset.
p6.Annotations = test.GetDaemonSetAnnotation() p6.ObjectMeta.OwnerReferences = test.GetDaemonSetOwnerRefList()
// A pod with local storage. // A pod with local storage.
p7.Annotations = test.GetNormalPodAnnotation() p7.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
p7.Spec.Volumes = []v1.Volume{ p7.Spec.Volumes = []v1.Volume{
{ {
Name: "sample", Name: "sample",
@@ -77,7 +82,7 @@ func TestLowNodeUtilization(t *testing.T) {
p8.Namespace = "kube-system" p8.Namespace = "kube-system"
p8.Annotations = test.GetCriticalPodAnnotation() p8.Annotations = test.GetCriticalPodAnnotation()
p9 := test.BuildTestPod("p9", 400, 0, n1.Name) p9 := test.BuildTestPod("p9", 400, 0, n1.Name)
p9.Annotations = test.GetReplicaSetAnnotation() p9.ObjectMeta.OwnerReferences = test.GetReplicaSetOwnerRefList()
fakeClient := &fake.Clientset{} fakeClient := &fake.Clientset{}
fakeClient.Fake.AddReactor("list", "pods", func(action core.Action) (bool, runtime.Object, error) { fakeClient.Fake.AddReactor("list", "pods", func(action core.Action) (bool, runtime.Object, error) {
list := action.(core.ListAction) list := action.(core.ListAction)
@@ -88,6 +93,9 @@ func TestLowNodeUtilization(t *testing.T) {
if strings.Contains(fieldString, "n2") { if strings.Contains(fieldString, "n2") {
return true, &v1.PodList{Items: []v1.Pod{*p9}}, nil 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) return true, nil, fmt.Errorf("Failed to list: %v", list)
}) })
fakeClient.Fake.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) { fakeClient.Fake.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) {
@@ -97,15 +105,221 @@ func TestLowNodeUtilization(t *testing.T) {
return true, n1, nil return true, n1, nil
case n2.Name: case n2.Name:
return true, n2, nil return true, n2, nil
case n3.Name:
return true, n3, nil
} }
return true, nil, fmt.Errorf("Wrong node: %v", getAction.GetName()) return true, nil, fmt.Errorf("Wrong node: %v", getAction.GetName())
}) })
expectedPodsEvicted := 4 expectedPodsEvicted := 3
npm := CreateNodePodsMap(fakeClient, []*v1.Node{n1, n2}) npm := createNodePodsMap(fakeClient, []*v1.Node{n1, n2, n3})
lowNodes, targetNodes, _ := classifyNodes(npm, thresholds, targetThresholds) lowNodes, targetNodes := classifyNodes(npm, thresholds, targetThresholds, false)
podsEvicted := evictPodsFromTargetNodes(fakeClient, "v1", targetNodes, lowNodes, targetThresholds, false) 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 { 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, false)
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)
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)
}
}
}

View 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, ds.EvictLocalStoragePods)
}
func removePodsViolatingNodeAffinityCount(ds *options.DeschedulerServer, strategy api.DeschedulerStrategy, evictionPolicyGroupVersion string, nodes []*v1.Node, nodepodCount nodePodEvictedCount, maxPodsToEvict int, evictLocalStoragePods bool) 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, evictLocalStoragePods)
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
}

View 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, false)
if actualEvictedPodCount != tc.expectedEvictedPodCount {
t.Errorf("Test %#v failed, expected %v pod evictions, but got %v pod evictions\n", tc.description, tc.expectedEvictedPodCount, actualEvictedPodCount)
}
}
}

View File

@@ -19,43 +19,45 @@ package strategies
import ( import (
"github.com/kubernetes-incubator/descheduler/cmd/descheduler/app/options" "github.com/kubernetes-incubator/descheduler/cmd/descheduler/app/options"
"github.com/kubernetes-incubator/descheduler/pkg/api" "github.com/kubernetes-incubator/descheduler/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
"github.com/golang/glog"
"github.com/kubernetes-incubator/descheduler/pkg/descheduler/evictions" "github.com/kubernetes-incubator/descheduler/pkg/descheduler/evictions"
podutil "github.com/kubernetes-incubator/descheduler/pkg/descheduler/pod" 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" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
//TODO: Change to client-go instead of generated clientset. clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
priorityutil "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/priorities/util" priorityutil "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/priorities/util"
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
) )
// RemovePodsViolatingInterPodAntiAffinity with elimination strategy // RemovePodsViolatingInterPodAntiAffinity with elimination strategy
func RemovePodsViolatingInterPodAntiAffinity(ds *options.DeschedulerServer, strategy api.DeschedulerStrategy, policyGroupVersion string, nodes []*v1.Node) { func RemovePodsViolatingInterPodAntiAffinity(ds *options.DeschedulerServer, strategy api.DeschedulerStrategy, policyGroupVersion string, nodes []*v1.Node, nodePodCount nodePodEvictedCount) {
if !strategy.Enabled { if !strategy.Enabled {
return return
} }
removePodsWithAffinityRules(ds.Client, policyGroupVersion, nodes, ds.DryRun) removePodsWithAffinityRules(ds.Client, policyGroupVersion, nodes, ds.DryRun, nodePodCount, ds.MaxNoOfPodsToEvictPerNode, ds.EvictLocalStoragePods)
} }
// removePodsWithAffinityRules evicts pods on the node which are having a pod affinity rules. // 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) int { func removePodsWithAffinityRules(client clientset.Interface, policyGroupVersion string, nodes []*v1.Node, dryRun bool, nodePodCount nodePodEvictedCount, maxPodsToEvict int, evictLocalStoragePods bool) int {
podsEvicted := 0 podsEvicted := 0
for _, node := range nodes { for _, node := range nodes {
glog.V(1).Infof("Processing node: %#v\n", node.Name) glog.V(1).Infof("Processing node: %#v\n", node.Name)
pods, err := podutil.ListPodsOnANode(client, node) pods, err := podutil.ListEvictablePodsOnNode(client, node, evictLocalStoragePods)
if err != nil { if err != nil {
return 0 return 0
} }
totalPods := len(pods) totalPods := len(pods)
for i := 0; i < totalPods; i++ { for i := 0; i < totalPods; i++ {
if maxPodsToEvict > 0 && nodePodCount[node]+1 > maxPodsToEvict {
break
}
if checkPodsWithAntiAffinityExist(pods[i], pods) { if checkPodsWithAntiAffinityExist(pods[i], pods) {
success, err := evictions.EvictPod(client, pods[i], policyGroupVersion, dryRun) success, err := evictions.EvictPod(client, pods[i], policyGroupVersion, dryRun)
if !success { if !success {
glog.Infof("Error when evicting pod: %#v (%#v)\n", pods[i].Name, err) glog.Infof("Error when evicting pod: %#v (%#v)\n", pods[i].Name, err)
} else { } else {
podsEvicted++ nodePodCount[node]++
glog.V(1).Infof("Evicted pod: %#v (%#v)\n because of existing anti-affinity", pods[i].Name, err) 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 // Since the current pod is evicted all other pods which have anti-affinity with this
// pod need not be evicted. // pod need not be evicted.
@@ -66,13 +68,14 @@ func removePodsWithAffinityRules(client clientset.Interface, policyGroupVersion
} }
} }
} }
podsEvicted += nodePodCount[node]
} }
return podsEvicted return podsEvicted
} }
// checkPodsWithAntiAffinityExist checks if there are other pods on the node that the current pod cannot tolerate. // 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 { func checkPodsWithAntiAffinityExist(pod *v1.Pod, pods []*v1.Pod) bool {
affinity := schedulercache.ReconcileAffinity(pod) affinity := pod.Spec.Affinity
if affinity != nil && affinity.PodAntiAffinity != nil { if affinity != nil && affinity.PodAntiAffinity != nil {
for _, term := range getPodAntiAffinityTerms(affinity.PodAntiAffinity) { for _, term := range getPodAntiAffinityTerms(affinity.PodAntiAffinity) {
namespaces := priorityutil.GetNamespacesFromPodAffinityTerm(pod, &term) namespaces := priorityutil.GetNamespacesFromPodAffinityTerm(pod, &term)

View File

@@ -19,13 +19,12 @@ package strategies
import ( import (
"testing" "testing"
"fmt"
"github.com/kubernetes-incubator/descheduler/test" "github.com/kubernetes-incubator/descheduler/test"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing" core "k8s.io/client-go/testing"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
) )
func TestPodAntiAffinity(t *testing.T) { func TestPodAntiAffinity(t *testing.T) {
@@ -33,54 +32,58 @@ func TestPodAntiAffinity(t *testing.T) {
p1 := test.BuildTestPod("p1", 100, 0, node.Name) p1 := test.BuildTestPod("p1", 100, 0, node.Name)
p2 := test.BuildTestPod("p2", 100, 0, node.Name) p2 := test.BuildTestPod("p2", 100, 0, node.Name)
p3 := test.BuildTestPod("p3", 100, 0, node.Name) p3 := test.BuildTestPod("p3", 100, 0, node.Name)
p3.Labels = map[string]string{"foo": "bar"} p4 := test.BuildTestPod("p4", 100, 0, node.Name)
p1.Spec.Affinity = &v1.Affinity{ p2.Labels = map[string]string{"foo": "bar"}
PodAntiAffinity: &v1.PodAntiAffinity{ p1.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{ p2.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
{ p3.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
LabelSelector: &metav1.LabelSelector{ p4.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
MatchExpressions: []metav1.LabelSelectorRequirement{
{ // set pod anti affinity
Key: "foo", setPodAntiAffinity(p1)
Operator: metav1.LabelSelectorOpIn, setPodAntiAffinity(p3)
Values: []string{"bar"}, setPodAntiAffinity(p4)
},
}, // create fake client
},
TopologyKey: "region",
},
},
},
}
p3.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",
},
},
},
}
fakeClient := &fake.Clientset{} fakeClient := &fake.Clientset{}
fakeClient.Fake.AddReactor("list", "pods", func(action core.Action) (bool, runtime.Object, error) { fakeClient.Fake.AddReactor("list", "pods", func(action core.Action) (bool, runtime.Object, error) {
return true, &v1.PodList{Items: []v1.Pod{*p1, *p2, *p3}}, nil 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) { fakeClient.Fake.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) {
return true, node, nil return true, node, nil
}) })
expectedEvictedPodCount := 1 npe := nodePodEvictedCount{}
podsEvicted := removePodsWithAffinityRules(fakeClient, "v1", []*v1.Node{node}, false) npe[node] = 0
expectedEvictedPodCount := 3
podsEvicted := removePodsWithAffinityRules(fakeClient, "v1", []*v1.Node{node}, false, npe, 0, false)
if podsEvicted != expectedEvictedPodCount { if podsEvicted != expectedEvictedPodCount {
fmt.Println(podsEvicted) t.Errorf("Unexpected no of pods evicted: pods evicted: %d, expected: %d", podsEvicted, expectedEvictedPodCount)
t.Errorf("Unexpected no of pods evicted") }
npe[node] = 0
expectedEvictedPodCount = 1
podsEvicted = removePodsWithAffinityRules(fakeClient, "v1", []*v1.Node{node}, false, npe, 1, false)
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",
},
},
},
} }
} }

View 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
View 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
View 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
View 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

View File

@@ -14,6 +14,7 @@
#!/bin/bash #!/bin/bash
# run unit tests # This just run unit-tests. Ignoring the current directory so as to avoid running e2e tests.
go test $(go list github.com/kubernetes-incubator/descheduler/... | grep -v github.com/kubernetes-incubator/descheduler/vendor/) 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/)

View File

@@ -19,14 +19,12 @@ package test
import ( import (
"fmt" "fmt"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 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 // BuildTestPod creates a test pod with given parameters.
// move to utils after refactor.
// buildTestPod creates a test pod with given parameters.
func BuildTestPod(name string, cpu int64, memory int64, nodeName string) *v1.Pod { func BuildTestPod(name string, cpu int64, memory int64, nodeName string) *v1.Pod {
pod := &v1.Pod{ pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
@@ -39,6 +37,7 @@ func BuildTestPod(name string, cpu int64, memory int64, nodeName string) *v1.Pod
{ {
Resources: v1.ResourceRequirements{ Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{}, Requests: v1.ResourceList{},
Limits: v1.ResourceList{},
}, },
}, },
}, },
@@ -64,25 +63,25 @@ func GetMirrorPodAnnotation() map[string]string {
} }
} }
// GetNormalPodAnnotation returns the annotation needed for a pod. // GetNormalPodOwnerRefList returns the ownerRef needed for a pod.
func GetNormalPodAnnotation() map[string]string { func GetNormalPodOwnerRefList() []metav1.OwnerReference {
return map[string]string{ ownerRefList := make([]metav1.OwnerReference, 0)
"kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"Pod\"}}", ownerRefList = append(ownerRefList, metav1.OwnerReference{Kind: "Pod", APIVersion: "v1"})
} return ownerRefList
} }
// GetReplicaSetAnnotation returns the annotation needed for replicaset pod. // GetReplicaSetOwnerRefList returns the ownerRef needed for replicaset pod.
func GetReplicaSetAnnotation() map[string]string { func GetReplicaSetOwnerRefList() []metav1.OwnerReference {
return map[string]string{ ownerRefList := make([]metav1.OwnerReference, 0)
"kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicaSet\"}}", ownerRefList = append(ownerRefList, metav1.OwnerReference{Kind: "ReplicaSet", APIVersion: "v1"})
} return ownerRefList
} }
// GetDaemonSetAnnotation returns the annotation needed for daemonset pod. // GetDaemonSetOwnerRefList returns the ownerRef needed for daemonset pod.
func GetDaemonSetAnnotation() map[string]string { func GetDaemonSetOwnerRefList() []metav1.OwnerReference {
return map[string]string{ ownerRefList := make([]metav1.OwnerReference, 0)
"kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"DaemonSet\"}}", ownerRefList = append(ownerRefList, metav1.OwnerReference{Kind: "DaemonSet", APIVersion: "v1"})
} return ownerRefList
} }
// GetCriticalPodAnnotation returns the annotation needed for critical pod. // GetCriticalPodAnnotation returns the annotation needed for critical pod.

11
vendor/cloud.google.com/go/.travis.yml generated vendored Normal file
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 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
View File

@@ -0,0 +1,245 @@
# Google Cloud for Go
[![Build Status](https://travis-ci.org/GoogleCloudPlatform/google-cloud-go.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/google-cloud-go)
[![GoDoc](https://godoc.org/cloud.google.com/go?status.svg)](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 [![GoDoc](https://godoc.org/cloud.google.com/go/datastore?status.svg)](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 [![GoDoc](https://godoc.org/cloud.google.com/go/storage?status.svg)](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 [![GoDoc](https://godoc.org/cloud.google.com/go/pubsub?status.svg)](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
View 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
View 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
View 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
View 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
View 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)
}
}
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 = &quote
} 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}

308
vendor/cloud.google.com/go/bigquery/read_test.go generated vendored Normal file
View File

@@ -0,0 +1,308 @@
// 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"
)
type readTabledataArgs struct {
conf *readTableConf
tok string
}
type readQueryArgs struct {
conf *readQueryConf
tok string
}
// readServiceStub services read requests by returning data from an in-memory list of values.
type readServiceStub struct {
// values and pageTokens are used as sources of data to return in response to calls to readTabledata or readQuery.
values [][][]Value // contains pages / rows / columns.
pageTokens map[string]string // maps incoming page token to returned page token.
// arguments are recorded for later inspection.
readTabledataCalls []readTabledataArgs
readQueryCalls []readQueryArgs
service
}
func (s *readServiceStub) readValues(tok string) *readDataResult {
result := &readDataResult{
pageToken: s.pageTokens[tok],
rows: s.values[0],
}
s.values = s.values[1:]
return result
}
func (s *readServiceStub) readTabledata(ctx context.Context, conf *readTableConf, token string) (*readDataResult, error) {
s.readTabledataCalls = append(s.readTabledataCalls, readTabledataArgs{conf, token})
return s.readValues(token), nil
}
func (s *readServiceStub) readQuery(ctx context.Context, conf *readQueryConf, token string) (*readDataResult, error) {
s.readQueryCalls = append(s.readQueryCalls, readQueryArgs{conf, token})
return s.readValues(token), nil
}
func TestRead(t *testing.T) {
// The data for the service stub to return is populated for each test case in the testCases for loop.
service := &readServiceStub{}
c := &Client{
service: service,
}
queryJob := &Job{
projectID: "project-id",
jobID: "job-id",
service: service,
isQuery: true,
}
for _, src := range []ReadSource{defaultTable(service), queryJob} {
testCases := []struct {
data [][][]Value
pageTokens map[string]string
want []ValueList
}{
{
data: [][][]Value{{{1, 2}, {11, 12}}, {{30, 40}, {31, 41}}},
pageTokens: map[string]string{"": "a", "a": ""},
want: []ValueList{{1, 2}, {11, 12}, {30, 40}, {31, 41}},
},
{
data: [][][]Value{{{1, 2}, {11, 12}}, {{30, 40}, {31, 41}}},
pageTokens: map[string]string{"": ""}, // no more pages after first one.
want: []ValueList{{1, 2}, {11, 12}},
},
}
for _, tc := range testCases {
service.values = tc.data
service.pageTokens = tc.pageTokens
if got, ok := doRead(t, c, src); ok {
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("reading: got:\n%v\nwant:\n%v", got, tc.want)
}
}
}
}
}
// doRead calls Read with a ReadSource. Get is repeatedly called on the Iterator returned by Read and the results are returned.
func doRead(t *testing.T, c *Client, src ReadSource) ([]ValueList, bool) {
it, err := c.Read(context.Background(), src)
if err != nil {
t.Errorf("err calling Read: %v", err)
return nil, false
}
var got []ValueList
for it.Next(context.Background()) {
var vals ValueList
if err := it.Get(&vals); err != nil {
t.Errorf("err calling Get: %v", err)
return nil, false
} else {
got = append(got, vals)
}
}
return got, true
}
func TestNoMoreValues(t *testing.T) {
c := &Client{
service: &readServiceStub{
values: [][][]Value{{{1, 2}, {11, 12}}},
},
}
it, err := c.Read(context.Background(), defaultTable(c.service))
if err != nil {
t.Fatalf("err calling Read: %v", err)
}
var vals ValueList
// We expect to retrieve two values and then fail on the next attempt.
if !it.Next(context.Background()) {
t.Fatalf("Next: got: false: want: true")
}
if !it.Next(context.Background()) {
t.Fatalf("Next: got: false: want: true")
}
if err := it.Get(&vals); err != nil {
t.Fatalf("Get: got: %v: want: nil", err)
}
if it.Next(context.Background()) {
t.Fatalf("Next: got: true: want: false")
}
if err := it.Get(&vals); err == nil {
t.Fatalf("Get: got: %v: want: non-nil", err)
}
}
// delayedReadStub simulates reading results from a query that has not yet
// completed. Its readQuery method initially reports that the query job is not
// yet complete. Subsequently, it proxies the request through to another
// service stub.
type delayedReadStub struct {
numDelays int
readServiceStub
}
func (s *delayedReadStub) readQuery(ctx context.Context, conf *readQueryConf, token string) (*readDataResult, error) {
if s.numDelays > 0 {
s.numDelays--
return nil, errIncompleteJob
}
return s.readServiceStub.readQuery(ctx, conf, token)
}
// TestIncompleteJob tests that an Iterator which reads from a query job will block until the job is complete.
func TestIncompleteJob(t *testing.T) {
service := &delayedReadStub{
numDelays: 2,
readServiceStub: readServiceStub{
values: [][][]Value{{{1, 2}}},
},
}
c := &Client{service: service}
queryJob := &Job{
projectID: "project-id",
jobID: "job-id",
service: service,
isQuery: true,
}
it, err := c.Read(context.Background(), queryJob)
if err != nil {
t.Fatalf("err calling Read: %v", err)
}
var got ValueList
want := ValueList{1, 2}
if !it.Next(context.Background()) {
t.Fatalf("Next: got: false: want: true")
}
if err := it.Get(&got); err != nil {
t.Fatalf("Error calling Get: %v", err)
}
if service.numDelays != 0 {
t.Errorf("remaining numDelays : got: %v want:0", service.numDelays)
}
if !reflect.DeepEqual(got, want) {
t.Errorf("reading: got:\n%v\nwant:\n%v", got, want)
}
}
type errorReadService struct {
service
}
func (s *errorReadService) readTabledata(ctx context.Context, conf *readTableConf, token string) (*readDataResult, error) {
return nil, errors.New("bang!")
}
func TestReadError(t *testing.T) {
// test that service read errors are propagated back to the caller.
c := &Client{service: &errorReadService{}}
it, err := c.Read(context.Background(), defaultTable(c.service))
if err != nil {
// Read should not return an error; only Err should.
t.Fatalf("err calling Read: %v", err)
}
if it.Next(context.Background()) {
t.Fatalf("Next: got: true: want: false")
}
if err := it.Err(); err.Error() != "bang!" {
t.Fatalf("Get: got: %v: want: bang!", err)
}
}
func TestReadTabledataOptions(t *testing.T) {
// test that read options are propagated.
s := &readServiceStub{
values: [][][]Value{{{1, 2}}},
}
c := &Client{service: s}
it, err := c.Read(context.Background(), defaultTable(s), RecordsPerRequest(5))
if err != nil {
t.Fatalf("err calling Read: %v", err)
}
if !it.Next(context.Background()) {
t.Fatalf("Next: got: false: want: true")
}
want := []readTabledataArgs{{
conf: &readTableConf{
projectID: "project-id",
datasetID: "dataset-id",
tableID: "table-id",
paging: pagingConf{
recordsPerRequest: 5,
setRecordsPerRequest: true,
},
},
tok: "",
}}
if !reflect.DeepEqual(s.readTabledataCalls, want) {
t.Errorf("reading: got:\n%v\nwant:\n%v", s.readTabledataCalls, want)
}
}
func TestReadQueryOptions(t *testing.T) {
// test that read options are propagated.
s := &readServiceStub{
values: [][][]Value{{{1, 2}}},
}
c := &Client{service: s}
queryJob := &Job{
projectID: "project-id",
jobID: "job-id",
service: s,
isQuery: true,
}
it, err := c.Read(context.Background(), queryJob, RecordsPerRequest(5))
if err != nil {
t.Fatalf("err calling Read: %v", err)
}
if !it.Next(context.Background()) {
t.Fatalf("Next: got: false: want: true")
}
want := []readQueryArgs{{
conf: &readQueryConf{
projectID: "project-id",
jobID: "job-id",
paging: pagingConf{
recordsPerRequest: 5,
setRecordsPerRequest: true,
},
},
tok: "",
}}
if !reflect.DeepEqual(s.readQueryCalls, want) {
t.Errorf("reading: got:\n%v\nwant:\n%v", s.readQueryCalls, want)
}
}

233
vendor/cloud.google.com/go/bigquery/schema.go generated vendored Normal file
View File

@@ -0,0 +1,233 @@
// 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"
bq "google.golang.org/api/bigquery/v2"
)
// Schema describes the fields in a table or query result.
type Schema []*FieldSchema
type FieldSchema struct {
// The field name.
// Must contain only letters (a-z, A-Z), numbers (0-9), or underscores (_),
// and must start with a letter or underscore.
// The maximum length is 128 characters.
Name string
// A description of the field. The maximum length is 16,384 characters.
Description string
// Whether the field may contain multiple values.
Repeated bool
// Whether the field is required. Ignored if Repeated is true.
Required bool
// The field data type. If Type is Record, then this field contains a nested schema,
// which is described by Schema.
Type FieldType
// Describes the nested schema if Type is set to Record.
Schema Schema
}
func (fs *FieldSchema) asTableFieldSchema() *bq.TableFieldSchema {
tfs := &bq.TableFieldSchema{
Description: fs.Description,
Name: fs.Name,
Type: string(fs.Type),
}
if fs.Repeated {
tfs.Mode = "REPEATED"
} else if fs.Required {
tfs.Mode = "REQUIRED"
} // else leave as default, which is interpreted as NULLABLE.
for _, f := range fs.Schema {
tfs.Fields = append(tfs.Fields, f.asTableFieldSchema())
}
return tfs
}
func (s Schema) asTableSchema() *bq.TableSchema {
var fields []*bq.TableFieldSchema
for _, f := range s {
fields = append(fields, f.asTableFieldSchema())
}
return &bq.TableSchema{Fields: fields}
}
// customizeCreateTable allows a Schema to be used directly as an option to CreateTable.
func (s Schema) customizeCreateTable(conf *createTableConf) {
conf.schema = s.asTableSchema()
}
func convertTableFieldSchema(tfs *bq.TableFieldSchema) *FieldSchema {
fs := &FieldSchema{
Description: tfs.Description,
Name: tfs.Name,
Repeated: tfs.Mode == "REPEATED",
Required: tfs.Mode == "REQUIRED",
Type: FieldType(tfs.Type),
}
for _, f := range tfs.Fields {
fs.Schema = append(fs.Schema, convertTableFieldSchema(f))
}
return fs
}
func convertTableSchema(ts *bq.TableSchema) Schema {
var s Schema
for _, f := range ts.Fields {
s = append(s, convertTableFieldSchema(f))
}
return s
}
type FieldType string
const (
StringFieldType FieldType = "STRING"
IntegerFieldType FieldType = "INTEGER"
FloatFieldType FieldType = "FLOAT"
BooleanFieldType FieldType = "BOOLEAN"
TimestampFieldType FieldType = "TIMESTAMP"
RecordFieldType FieldType = "RECORD"
)
var errNoStruct = errors.New("bigquery: can only infer schema from struct or pointer to struct")
var errUnsupportedFieldType = errors.New("bigquery: unsupported type of field in struct")
// InferSchema tries to derive a BigQuery schema from the supplied struct value.
// NOTE: All fields in the returned Schema are configured to be required,
// unless the corresponding field in the supplied struct is a slice or array.
// It is considered an error if the struct (including nested structs) contains
// any exported fields that are pointers or one of the following types:
// map, interface, complex64, complex128, func, chan.
// In these cases, an error will be returned.
// Future versions may handle these cases without error.
func InferSchema(st interface{}) (Schema, error) {
return inferStruct(reflect.TypeOf(st))
}
func inferStruct(rt reflect.Type) (Schema, error) {
switch rt.Kind() {
case reflect.Struct:
return inferFields(rt)
default:
return nil, errNoStruct
}
}
// inferFieldSchema infers the FieldSchema for a Go type
func inferFieldSchema(rt reflect.Type) (*FieldSchema, error) {
switch {
case isByteSlice(rt):
return &FieldSchema{Required: true, Type: StringFieldType}, nil
case isTimeTime(rt):
return &FieldSchema{Required: true, Type: TimestampFieldType}, nil
case isRepeated(rt):
et := rt.Elem()
if isRepeated(et) && !isByteSlice(et) {
// Multi dimensional slices/arrays are not supported by BigQuery
return nil, errUnsupportedFieldType
}
f, err := inferFieldSchema(et)
if err != nil {
return nil, err
}
f.Repeated = true
f.Required = false
return f, nil
case isStruct(rt):
nested, err := inferFields(rt)
if err != nil {
return nil, err
}
return &FieldSchema{Required: true, Type: RecordFieldType, Schema: nested}, nil
}
switch rt.Kind() {
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int,
reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr:
return &FieldSchema{Required: true, Type: IntegerFieldType}, nil
case reflect.String:
return &FieldSchema{Required: true, Type: StringFieldType}, nil
case reflect.Bool:
return &FieldSchema{Required: true, Type: BooleanFieldType}, nil
case reflect.Float32, reflect.Float64:
return &FieldSchema{Required: true, Type: FloatFieldType}, nil
default:
return nil, errUnsupportedFieldType
}
}
// inferFields extracts all exported field types from struct type.
func inferFields(rt reflect.Type) (Schema, error) {
var s Schema
for i := 0; i < rt.NumField(); i++ {
field := rt.Field(i)
if field.PkgPath != "" {
// field is unexported.
continue
}
if field.Anonymous {
// TODO(nightlyone) support embedded (see https://github.com/GoogleCloudPlatform/google-cloud-go/issues/238)
return nil, errUnsupportedFieldType
}
f, err := inferFieldSchema(field.Type)
if err != nil {
return nil, err
}
f.Name = field.Name
s = append(s, f)
}
return s, nil
}
func isByteSlice(rt reflect.Type) bool {
return rt.Kind() == reflect.Slice && rt.Elem().Kind() == reflect.Uint8
}
func isTimeTime(rt reflect.Type) bool {
return rt.PkgPath() == "time" && rt.Name() == "Time"
}
func isStruct(rt reflect.Type) bool {
return rt.Kind() == reflect.Struct
}
func isRepeated(rt reflect.Type) bool {
switch rt.Kind() {
case reflect.Slice, reflect.Array:
return true
default:
return false
}
}

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