1
0
Fork 0
mirror of https://github.com/prometheus-operator/prometheus-operator.git synced 2025-04-15 16:56:24 +00:00

prometheus: Introduce RuleFile Custom Resource Definition

This patch introduces a new Custom Resource Definition to the
Prometheus Operator - the Rule CRD. It addresses two main
needs:

1. Prometheus (alerting and recording) Rule validation during creation time
via Kubernetes Custom Resource Definition validation.

2. Life-cycle management of Prometheus application Rules alongside the
application itself, inside the applications Kubernetes namespace, not
necessarily the namespace of the scraping Prometheus instance.

A user defines Prometheus alerting and recording Rules via a Kubernetes
Custom Resource Definition. These Custom Resource Definitions can be
fully validated by the Kubernetes API server during creation time via
automatically generated OpenAPI specifications. Instead of the
restriction of a Prometheus instance to only select Rule definitions
inside its own namespace, the Prometheus specification is extended to
also specify namespaces to look for Rule Custom Resource Definitions
outside its own namespace.

---

Dependent technical changes:

- prometheus: Use github.com/jimmidyson/configmap-reload to reload rules

- prometheus: Remove Prometheus Statefulset deletion function. Starting
with K8s >=1.8 this is handled via OwnerReferences.

- prometheus: Do not add rule files checksum to Prometheus configuration
secret

- prometheus: Update StatefulSet only on relevant changes. Instead of
updating the Prometheus StatefulSet on every `sync()` run, only update
it if the input parameters to `makeStatefulSet` change.  Enforce this
via a checksum of the parameters which is saved inside the annotations
of the statefulset.

- e2e/prometheus: Check how often resources (Secret, ConfigMap,
Prometheus CRD, Service) are updated to enforce that Prometheus Operator
only updated created resources if necessary.

- contrib/prometheus-config-reloader: Remove logic to retriev K8s
ConfigMaps. These are mounted into the pod right away now.
This commit is contained in:
Max Leonard Inden 2018-05-08 09:43:45 +02:00
parent 894695f48f
commit 89fc4e3069
No known key found for this signature in database
GPG key ID: 5403C5464810BC26
789 changed files with 4235 additions and 321888 deletions

View file

@ -26,6 +26,11 @@ This Document documents the types introduced by the Prometheus Operator to be co
* [RelabelConfig](#relabelconfig)
* [RemoteReadSpec](#remotereadspec)
* [RemoteWriteSpec](#remotewritespec)
* [Rule](#rule)
* [RuleFile](#rulefile)
* [RuleFileList](#rulefilelist)
* [RuleFileSpec](#rulefilespec)
* [RuleGroup](#rulegroup)
* [ServiceMonitor](#servicemonitor)
* [ServiceMonitorList](#servicemonitorlist)
* [ServiceMonitorSpec](#servicemonitorspec)
@ -211,7 +216,9 @@ Specification of the desired behavior of the Prometheus cluster. More info: http
| externalUrl | The external URL the Prometheus instances will be available under. This is necessary to generate correct URLs. This is necessary if Prometheus is not served from root of a DNS name. | string | false |
| routePrefix | The route prefix Prometheus registers HTTP handlers for. This is useful, if using ExternalURL and a proxy is rewriting HTTP routes of a request, and the actual ExternalURL is still true, but the server serves requests under a different route prefix. For example for use with `kubectl proxy`. | string | false |
| storage | Storage spec to specify how storage shall be used. | *[StorageSpec](#storagespec) | false |
| ruleSelector | A selector to select which ConfigMaps to mount for loading rule files from. | *[metav1.LabelSelector](https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#labelselector-v1-meta) | false |
| ruleFileSelector | A selector to select which RuleFiles to mount for loading alerting rules from. | *[metav1.LabelSelector](https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#labelselector-v1-meta) | false |
| ruleSelector | DEPRECATED with Prometheus Operator 'v0.20.0'. Any value in this field will just be copied to 'RuleFileSelector' field | *[metav1.LabelSelector](https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#labelselector-v1-meta) | false |
| ruleFileNamespaceSelector | Namespaces to be selected for RuleFiles discovery. If empty, only check own namespace. | *[metav1.LabelSelector](https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#labelselector-v1-meta) | false |
| alerting | Define details regarding alerting. | *[AlertingSpec](#alertingspec) | false |
| resources | Define resources requests and limits for single Pods. | [v1.ResourceRequirements](https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#resourcerequirements-v1-core) | false |
| nodeSelector | Define which Nodes the Pods are scheduled on. | map[string]string | false |
@ -294,6 +301,65 @@ RemoteWriteSpec defines the remote_write configuration for prometheus.
[Back to TOC](#table-of-contents)
## Rule
Rule describes an alerting or recording rule.
| Field | Description | Scheme | Required |
| ----- | ----------- | ------ | -------- |
| record | | string | false |
| alert | | string | false |
| expr | | string | true |
| for | | time.Duration | false |
| labels | | map[string]string | false |
| annotations | | map[string]string | false |
[Back to TOC](#table-of-contents)
## RuleFile
RuleFile defines alerting rules for a Prometheus instance
| Field | Description | Scheme | Required |
| ----- | ----------- | ------ | -------- |
| metadata | Standard objects metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata | [metav1.ObjectMeta](https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#objectmeta-v1-meta) | false |
| spec | Specification of desired alerting rule definitions for Prometheus. | [RuleFileSpec](#rulefilespec) | true |
[Back to TOC](#table-of-contents)
## RuleFileList
A list of RuleFiles.
| Field | Description | Scheme | Required |
| ----- | ----------- | ------ | -------- |
| metadata | Standard list metadata More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata | [metav1.ListMeta](https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#listmeta-v1-meta) | false |
| items | List of Rules | []*[RuleFile](#rulefile) | true |
[Back to TOC](#table-of-contents)
## RuleFileSpec
RuleFileSpec contains specification parameters for a Rule.
| Field | Description | Scheme | Required |
| ----- | ----------- | ------ | -------- |
| groups | Content of Prometheus rule file | [][RuleGroup](#rulegroup) | false |
[Back to TOC](#table-of-contents)
## RuleGroup
RuleGroup is a list of sequentially evaluated recording and alerting rules.
| Field | Description | Scheme | Required |
| ----- | ----------- | ------ | -------- |
| name | | string | true |
| interval | | time.Duration | false |
| rules | | [][Rule](#rule) | true |
[Back to TOC](#table-of-contents)
## ServiceMonitor
ServiceMonitor defines monitoring for a set of services.

View file

@ -40,6 +40,7 @@ rules:
- prometheuses/finalizers
- alertmanagers/finalizers
- servicemonitors
- rulefiles
verbs:
- "*"
- apiGroups:

View file

@ -54,6 +54,7 @@ rules:
- prometheuses/finalizers
- alertmanagers/finalizers
- servicemonitors
- rulefiles
verbs:
- "*"
- apiGroups:

58
Gopkg.lock generated
View file

@ -61,43 +61,6 @@
revision = "26b41036311f2da8242db402557a0dbd09dc83da"
version = "v2.6.0"
[[projects]]
name = "github.com/ericchiang/k8s"
packages = [
".",
"api/resource",
"api/unversioned",
"api/v1",
"apis/apps/v1alpha1",
"apis/apps/v1beta1",
"apis/authentication/v1",
"apis/authentication/v1beta1",
"apis/authorization/v1",
"apis/authorization/v1beta1",
"apis/autoscaling/v1",
"apis/autoscaling/v2alpha1",
"apis/batch/v1",
"apis/batch/v2alpha1",
"apis/certificates/v1alpha1",
"apis/certificates/v1beta1",
"apis/extensions/v1beta1",
"apis/imagepolicy/v1alpha1",
"apis/meta/v1",
"apis/policy/v1alpha1",
"apis/policy/v1beta1",
"apis/rbac/v1alpha1",
"apis/rbac/v1beta1",
"apis/settings/v1alpha1",
"apis/storage/v1",
"apis/storage/v1beta1",
"runtime",
"runtime/schema",
"util/intstr",
"watch/versioned"
]
revision = "68b0248f880c5967d1ba7166486cb216af849b15"
version = "v0.4.0"
[[projects]]
name = "github.com/fsnotify/fsnotify"
packages = ["."]
@ -246,6 +209,15 @@
packages = ["."]
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
[[projects]]
branch = "master"
name = "github.com/kylelemons/godebug"
packages = [
"diff",
"pretty"
]
revision = "d65d576e9348f5982d7f6d83682b694e731a45c6"
[[projects]]
branch = "master"
name = "github.com/mailru/easyjson"
@ -404,16 +376,6 @@
packages = ["rate"]
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
[[projects]]
branch = "master"
name = "golang.org/x/tools"
packages = [
"go/ast/astutil",
"go/buildutil",
"go/loader"
]
revision = "2226533658007779ffd629b495a088530c84dc50"
[[projects]]
name = "gopkg.in/alecthomas/kingpin.v2"
packages = ["."]
@ -595,6 +557,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "46a17224425510beb17903cabf6407f31d7c7b24fd75037df3a5cfd0c7436f28"
inputs-digest = "f195376f6f2edf7355611d989c4b2e1370722a23205711e61e97aa903ebf4e5a"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -59,3 +59,7 @@
[[override]]
name = "github.com/ericchiang/k8s"
version = "v0.4.0"
[[constraint]]
branch = "master"
name = "github.com/kylelemons/godebug"

View file

@ -23,9 +23,10 @@ po-crdgen:
crossbuild: promu
@$(PROMU) crossbuild
cd contrib/prometheus-config-reloader && make build
test:
@go test -short $(pkgs)
@go test $(TEST_RUN_ARGS) -short $(pkgs)
format:
go fmt $(pkgs)
@ -35,6 +36,7 @@ check-license:
container:
docker build -t $(REPO):$(TAG) .
cd contrib/prometheus-config-reloader && docker build -t quay.io/coreos/prometheus-config-reloader:$(TAG) .
e2e-test:
go test -timeout 55m -v ./test/e2e/ $(TEST_RUN_ARGS) --kubeconfig=$(KUBECONFIG) --operator-image=$(REPO):$(TAG) --namespace=$(NAMESPACE)
@ -48,7 +50,7 @@ e2e:
e2e-helm:
./helm/hack/e2e-test.sh
# package the chart and verify if they have the version bumped
# package the chart and verify if they have the version bumped
helm/hack/helm-package.sh "alertmanager grafana prometheus prometheus-operator exporter-kube-dns exporter-kube-scheduler exporter-kubelets exporter-node exporter-kube-controller-manager exporter-kube-etcd exporter-kube-state exporter-kubernetes exporter-coredns"
helm/hack/sync-repo.sh false
@ -126,5 +128,6 @@ generate-crd: generate-openapi po-crdgen
po-crdgen prometheus > example/prometheus-operator-crd/prometheus.crd.yaml
po-crdgen alertmanager > example/prometheus-operator-crd/alertmanager.crd.yaml
po-crdgen servicemonitor > example/prometheus-operator-crd/servicemonitor.crd.yaml
po-crdgen rulefile > example/prometheus-operator-crd/rulefile.crd.yaml
.PHONY: all build crossbuild test format check-license container e2e-test e2e-status e2e clean-e2e embedmd apidocgen docs generate-crd jb

View file

@ -36,6 +36,7 @@ rules:
- prometheuses/finalizers
- alertmanagers/finalizers
- servicemonitors
- rulefiles
verbs:
- "*"
- apiGroups:

View file

@ -45,7 +45,7 @@ func initFlags(crdkind monitoringv1.CrdKind, flagset *flag.FlagSet) *flag.FlagSe
func init() {
var command *flag.FlagSet
if len(os.Args) == 1 {
fmt.Println("usage: po-crdgen [prometheus | alertmanager | servicemonitor] [<options>]")
fmt.Println("usage: po-crdgen [prometheus | alertmanager | servicemonitor | rulefile] [<options>]")
os.Exit(1)
}
switch os.Args[1] {
@ -55,8 +55,10 @@ func init() {
command = initFlags(monitoringv1.DefaultCrdKinds.ServiceMonitor, flag.NewFlagSet("servicemonitor", flag.ExitOnError))
case "alertmanager":
command = initFlags(monitoringv1.DefaultCrdKinds.Alertmanager, flag.NewFlagSet("alertmanager", flag.ExitOnError))
case "rulefile":
command = initFlags(monitoringv1.DefaultCrdKinds.RuleFile, flag.NewFlagSet("rulefile", flag.ExitOnError))
default:
fmt.Printf("%q is not valid command.\n choices: [prometheus, alertmanager, servicemonitor]", os.Args[1])
fmt.Printf("%q is not valid command.\n choices: [prometheus, alertmanager, servicemonitor, rulefile]", os.Args[1])
os.Exit(2)
}
command.Parse(os.Args[2:])

View file

@ -3,14 +3,15 @@ image:
generate: image
@echo ">> Compiling assets and generating Kubernetes manifests"
docker run --rm -u=$(shell id -u $(USER)):$(shell id -g $(USER)) -v $(shell dirname $(dir $(abspath $(dir $$PWD)))):/go/src/github.com/coreos/prometheus-operator/ --workdir /go/src/github.com/coreos/prometheus-operator/contrib/kube-prometheus po-jsonnet make crdtojsonnet generate-raw
docker run --rm -u=$(shell id -u $(USER)):$(shell id -g $(USER)) -v $(shell dirname $(dir $(abspath $(dir $$PWD)))):/go/src/github.com/coreos/prometheus-operator/ --workdir /go/src/github.com/coreos/prometheus-operator/contrib/kube-prometheus po-jsonnet make generate-raw
crdtojsonnet:
cat ../../example/prometheus-operator-crd/alertmanager.crd.yaml | gojsontoyaml -yamltojson > jsonnet/kube-prometheus/prometheus-operator/alertmanager-crd.libsonnet
cat ../../example/prometheus-operator-crd/prometheus.crd.yaml | gojsontoyaml -yamltojson > jsonnet/kube-prometheus/prometheus-operator/prometheus-crd.libsonnet
cat ../../example/prometheus-operator-crd/servicemonitor.crd.yaml | gojsontoyaml -yamltojson > jsonnet/kube-prometheus/prometheus-operator/servicemonitor-crd.libsonnet
cat ../../example/prometheus-operator-crd/rulefile.crd.yaml | gojsontoyaml -yamltojson > jsonnet/kube-prometheus/prometheus-operator/rulefile-crd.libsonnet
generate-raw:
generate-raw: crdtojsonnet
jb install
./build.sh

View file

@ -86,6 +86,10 @@ set -x
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
# Make sure to start with a clean 'manifests' dir
rm -rf manifests
mkdir manifests
# optional, but we would like to generate yaml, not json
jsonnet -J vendor -m manifests example.jsonnet | xargs -I{} sh -c 'cat $1 | gojsontoyaml > $1.yaml; rm -f $1' -- {}

View file

@ -4,6 +4,10 @@ set -x
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
# Make sure to start with a clean 'manifests' dir
rm -rf manifests
mkdir manifests
# optional, but we would like to generate yaml, not json
jsonnet -J vendor -m manifests example.jsonnet | xargs -I{} sh -c 'cat $1 | gojsontoyaml > $1.yaml; rm -f $1' -- {}

File diff suppressed because one or more lines are too long

View file

@ -666,7 +666,7 @@ spec:
description: Specify whether the ConfigMap must be defined
type: boolean
prefix:
description: An optional identifer to prepend to each key
description: An optional identifier to prepend to each key
in the ConfigMap. Must be a C_IDENTIFIER.
type: string
secretRef:
@ -1120,6 +1120,14 @@ spec:
description: Whether this container has a read-only root filesystem.
Default is false.
type: boolean
runAsGroup:
description: The GID to run the entrypoint of the container
process. Uses runtime default if unset. May also be set
in PodSecurityContext. If set in both SecurityContext and
PodSecurityContext, the value specified in SecurityContext
takes precedence.
format: int64
type: integer
runAsNonRoot:
description: Indicates that the container must run as a non-root
user. If true, the Kubelet will validate the image at runtime
@ -1231,8 +1239,7 @@ spec:
description: mountPropagation determines how mounts are
propagated from the host to container and the other way
around. When not set, MountPropagationHostToContainer
is used. This field is alpha in 1.8 and can be reworked
or removed in a future release.
is used. This field is beta in 1.10.
type: string
name:
description: This must match the Name of a Volume.
@ -1614,6 +1621,13 @@ spec:
If unset, the Kubelet will not modify the ownership and permissions of any volume.
format: int64
type: integer
runAsGroup:
description: The GID to run the entrypoint of the container process.
Uses runtime default if unset. May also be set in SecurityContext. If
set in both SecurityContext and PodSecurityContext, the value
specified in SecurityContext takes precedence for that container.
format: int64
type: integer
runAsNonRoot:
description: Indicates that the container must run as a non-root
user. If true, the Kubelet will validate the image at runtime

View file

@ -27,6 +27,21 @@ spec:
description: 'Specification of the desired behavior of the Prometheus cluster.
More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status'
properties:
additionalAlertManagerConfigs:
description: SecretKeySelector selects a key of a Secret.
properties:
key:
description: The key of the secret to select from. Must be a valid
secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
optional:
description: Specify whether the Secret or it's key must be defined
type: boolean
required:
- key
additionalScrapeConfigs:
description: SecretKeySelector selects a key of a Secret.
properties:
@ -734,7 +749,7 @@ spec:
description: Specify whether the ConfigMap must be defined
type: boolean
prefix:
description: An optional identifer to prepend to each key
description: An optional identifier to prepend to each key
in the ConfigMap. Must be a C_IDENTIFIER.
type: string
secretRef:
@ -1188,6 +1203,14 @@ spec:
description: Whether this container has a read-only root filesystem.
Default is false.
type: boolean
runAsGroup:
description: The GID to run the entrypoint of the container
process. Uses runtime default if unset. May also be set
in PodSecurityContext. If set in both SecurityContext and
PodSecurityContext, the value specified in SecurityContext
takes precedence.
format: int64
type: integer
runAsNonRoot:
description: Indicates that the container must run as a non-root
user. If true, the Kubelet will validate the image at runtime
@ -1299,8 +1322,7 @@ spec:
description: mountPropagation determines how mounts are
propagated from the host to container and the other way
around. When not set, MountPropagationHostToContainer
is used. This field is alpha in 1.8 and can be reworked
or removed in a future release.
is used. This field is beta in 1.10.
type: string
name:
description: This must match the Name of a Volume.
@ -1877,6 +1899,90 @@ spec:
the server serves requests under a different route prefix. For example
for use with `kubectl proxy`.
type: string
ruleFileNamespaceSelector:
description: A label selector is a label query over a set of resources.
The result of matchLabels and matchExpressions are ANDed. An empty
label selector matches all objects. A null label selector matches
no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that contains
values, a key, and an operator that relates the key and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship to a
set of values. Valid operators are In, NotIn, Exists and
DoesNotExist.
type: string
values:
description: values is an array of string values. If the operator
is In or NotIn, the values array must be non-empty. If the
operator is Exists or DoesNotExist, the values array must
be empty. This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: array
matchLabels:
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator is
"In", and the values array contains only "value". The requirements
are ANDed.
type: object
ruleFileSelector:
description: A label selector is a label query over a set of resources.
The result of matchLabels and matchExpressions are ANDed. An empty
label selector matches all objects. A null label selector matches
no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that contains
values, a key, and an operator that relates the key and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship to a
set of values. Valid operators are In, NotIn, Exists and
DoesNotExist.
type: string
values:
description: values is an array of string values. If the operator
is In or NotIn, the values array must be non-empty. If the
operator is Exists or DoesNotExist, the values array must
be empty. This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: array
matchLabels:
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator is
"In", and the values array contains only "value". The requirements
are ANDed.
type: object
ruleSelector:
description: A label selector is a label query over a set of resources.
The result of matchLabels and matchExpressions are ANDed. An empty
@ -1948,6 +2054,13 @@ spec:
If unset, the Kubelet will not modify the ownership and permissions of any volume.
format: int64
type: integer
runAsGroup:
description: The GID to run the entrypoint of the container process.
Uses runtime default if unset. May also be set in SecurityContext. If
set in both SecurityContext and PodSecurityContext, the value
specified in SecurityContext takes precedence for that container.
format: int64
type: integer
runAsNonRoot:
description: Indicates that the container must run as a non-root
user. If true, the Kubelet will validate the image at runtime

View file

@ -16,8 +16,9 @@ data:
\ - \"expr\": |\n sum by (namespace, label_name) (\n sum(kube_pod_container_resource_requests_memory_bytes{job=\"kube-state-metrics\"})
by (namespace, pod)\n * on (namespace, pod) group_left(label_name)\n label_replace(kube_pod_labels{job=\"kube-state-metrics\"},
\"pod_name\", \"$1\", \"pod\", \"(.*)\")\n )\n \"record\": \"namespace_name:kube_pod_container_resource_requests_memory_bytes:sum\"\n
\ - \"expr\": |\n sum by (namespace, label_name) (\n sum(kube_pod_container_resource_requests_cpu_cores{job=\"kube-state-metrics\"})
by (namespace, pod)\n * on (namespace, pod) group_left(label_name)\n label_replace(kube_pod_labels{job=\"kube-state-metrics\"},
\ - \"expr\": |\n sum by (namespace, label_name) (\n sum(kube_pod_container_resource_requests_cpu_cores{job=\"kube-state-metrics\"}
and on(pod) kube_pod_status_scheduled{condition=\"true\"}) by (namespace, pod)\n
\ * on (namespace, pod) group_left(label_name)\n label_replace(kube_pod_labels{job=\"kube-state-metrics\"},
\"pod_name\", \"$1\", \"pod\", \"(.*)\")\n )\n \"record\": \"namespace_name:kube_pod_container_resource_requests_cpu_cores:sum\"\n-
\"name\": \"node.rules\"\n \"rules\": \n - \"expr\": \"sum(min(kube_pod_info)
by (node))\"\n \"record\": \":kube_pod_info_node_count:\"\n - \"expr\": |\n

View file

@ -16,18 +16,10 @@ package main
import (
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/ericchiang/k8s"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/improbable-eng/thanos/pkg/reloader"
"github.com/oklog/run"
kingpin "gopkg.in/alecthomas/kingpin.v2"
@ -43,9 +35,6 @@ func main() {
cfgFile := app.Flag("config-file", "config file watched by the reloader").
String()
ruleListFile := app.Flag("rule-list-file", "file listing configmaps of rules to load dynamically").
String()
cfgSubstFile := app.Flag("config-envsubst-file", "output file for environment variable substituted config file").
String()
@ -59,15 +48,11 @@ func main() {
os.Exit(2)
}
if err := os.MkdirAll(*ruleDir, 0777); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(2)
}
client, err := k8s.NewInClusterClient()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
if *ruleDir != "" {
if err := os.MkdirAll(*ruleDir, 0777); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(2)
}
}
var g run.Group
@ -81,115 +66,9 @@ func main() {
cancel()
})
}
{
ctx, cancel := context.WithCancel(context.Background())
tick := time.NewTicker(1 * time.Minute)
rfet := newRuleFetcher(client, *ruleListFile, *ruleDir)
g.Add(func() error {
defer tick.Stop()
for {
select {
case <-tick.C:
if err := rfet.Refresh(ctx); err != nil {
level.Error(logger).Log("msg", "updating rules failed", "err", err)
}
case <-ctx.Done():
return nil
}
}
}, func(error) {
cancel()
})
}
if err := g.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
type configMapRef struct {
Key string `json:"key"`
}
type ruleFetcher struct {
client *k8s.Client
listFile string
outDir string
lastHash [sha256.Size]byte
}
func newRuleFetcher(client *k8s.Client, listFile, outDir string) *ruleFetcher {
return &ruleFetcher{
client: client,
listFile: listFile,
outDir: outDir,
}
}
func (rf *ruleFetcher) Refresh(ctx context.Context) error {
b, err := ioutil.ReadFile(rf.listFile)
if err != nil {
return err
}
h := sha256.Sum256(b)
if rf.lastHash == h {
return nil
}
var cms struct {
Items []*configMapRef `json:"items"`
}
err = json.Unmarshal(b, &cms)
if err != nil {
return err
}
if err := rf.refresh(ctx, cms.Items); err != nil {
return err
}
rf.lastHash = h
return nil
}
func (rf *ruleFetcher) refresh(ctx context.Context, cms []*configMapRef) error {
tmpdir := rf.outDir + ".tmp"
if err := os.MkdirAll(tmpdir, 0777); err != nil {
return err
}
defer os.RemoveAll(tmpdir)
for i, cm := range cms {
parts := strings.Split(cm.Key, "/")
if len(parts) != 2 {
return fmt.Errorf("malformatted configmap key: %s, must be namespace/name", cm.Key)
}
namespace, name := parts[0], parts[1]
cm, err := rf.client.CoreV1().GetConfigMap(ctx, name, namespace)
if err != nil {
return err
}
dir := filepath.Join(tmpdir, fmt.Sprintf("rules-%d", i))
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
return err
}
for fn, content := range cm.Data {
if err := ioutil.WriteFile(filepath.Join(dir, fn), []byte(content), 0666); err != nil {
return err
}
}
}
if err := os.RemoveAll(rf.outDir); err != nil {
return err
}
return os.Rename(tmpdir, rf.outDir)
}

View file

@ -1900,6 +1900,90 @@ spec:
the server serves requests under a different route prefix. For example
for use with `kubectl proxy`.
type: string
ruleFileNamespaceSelector:
description: A label selector is a label query over a set of resources.
The result of matchLabels and matchExpressions are ANDed. An empty
label selector matches all objects. A null label selector matches
no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that contains
values, a key, and an operator that relates the key and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship to a
set of values. Valid operators are In, NotIn, Exists and
DoesNotExist.
type: string
values:
description: values is an array of string values. If the operator
is In or NotIn, the values array must be non-empty. If the
operator is Exists or DoesNotExist, the values array must
be empty. This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: array
matchLabels:
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator is
"In", and the values array contains only "value". The requirements
are ANDed.
type: object
ruleFileSelector:
description: A label selector is a label query over a set of resources.
The result of matchLabels and matchExpressions are ANDed. An empty
label selector matches all objects. A null label selector matches
no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that contains
values, a key, and an operator that relates the key and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship to a
set of values. Valid operators are In, NotIn, Exists and
DoesNotExist.
type: string
values:
description: values is an array of string values. If the operator
is In or NotIn, the values array must be non-empty. If the
operator is Exists or DoesNotExist, the values array must
be empty. This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: array
matchLabels:
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator is
"In", and the values array contains only "value". The requirements
are ANDed.
type: object
ruleSelector:
description: A label selector is a label query over a set of resources.
The result of matchLabels and matchExpressions are ANDed. An empty

View file

@ -0,0 +1,343 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
name: rulefiles.monitoring.coreos.com
spec:
group: monitoring.coreos.com
names:
kind: RuleFile
plural: rulefiles
scope: Namespaced
validation:
openAPIV3Schema:
description: RuleFile defines alerting rules for a Prometheus instance
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
metadata:
description: ObjectMeta is metadata that all persisted resources must have,
which includes all objects users must create.
properties:
annotations:
description: 'Annotations is an unstructured key value map stored with
a resource that may be set by external tools to store and retrieve
arbitrary metadata. They are not queryable and should be preserved
when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations'
type: object
clusterName:
description: The name of the cluster which the object belongs to. This
is used to distinguish resources with same name and namespace in different
clusters. This field is not set anywhere right now and apiserver is
going to ignore it if set in create or update request.
type: string
creationTimestamp:
format: date-time
type: string
deletionGracePeriodSeconds:
description: Number of seconds allowed for this object to gracefully
terminate before it will be removed from the system. Only set when
deletionTimestamp is also set. May only be shortened. Read-only.
format: int64
type: integer
deletionTimestamp:
format: date-time
type: string
finalizers:
description: Must be empty before the object is deleted from the registry.
Each entry is an identifier for the responsible component that will
remove the entry from the list. If the deletionTimestamp of the object
is non-nil, entries in this list can only be removed.
items:
type: string
type: array
generateName:
description: |-
GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.
If this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).
Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency
type: string
generation:
description: A sequence number representing a specific generation of
the desired state. Populated by the system. Read-only.
format: int64
type: integer
initializers:
description: Initializers tracks the progress of initialization.
properties:
pending:
description: Pending is a list of initializers that must execute
in order before this object is visible. When the last pending
initializer is removed, and no failing result is set, the initializers
struct will be set to nil and the object is considered as initialized
and visible to all clients.
items:
description: Initializer is information about an initializer that
has not yet completed.
properties:
name:
description: name of the process that is responsible for initializing
this object.
type: string
required:
- name
type: array
result:
description: Status is a return value for calls that don't return
other objects.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this
representation of an object. Servers should convert recognized
schemas to the latest internal value, and may reject unrecognized
values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
type: string
code:
description: Suggested HTTP return code for this status, 0 if
not set.
format: int32
type: integer
details:
description: StatusDetails is a set of additional properties
that MAY be set by the server to provide additional information
about a response. The Reason field of a Status object defines
what attributes will be set. Clients must ignore fields that
do not match the defined type of each attribute, and should
assume that any attribute may be empty, invalid, or under
defined.
properties:
causes:
description: The Causes array includes more details associated
with the StatusReason failure. Not all StatusReasons may
provide detailed causes.
items:
description: StatusCause provides more information about
an api.Status failure, including cases when multiple
errors are encountered.
properties:
field:
description: |-
The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.
Examples:
"name" - the field "name" on the current resource
"items[0].name" - the field "name" on the first array entry in "items"
type: string
message:
description: A human-readable description of the cause
of the error. This field may be presented as-is
to a reader.
type: string
reason:
description: A machine-readable description of the
cause of the error. If this value is empty there
is no information available.
type: string
type: array
group:
description: The group attribute of the resource associated
with the status StatusReason.
type: string
kind:
description: 'The kind attribute of the resource associated
with the status StatusReason. On some operations may differ
from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
name:
description: The name attribute of the resource associated
with the status StatusReason (when there is a single name
which can be described).
type: string
retryAfterSeconds:
description: If specified, the time in seconds before the
operation should be retried. Some errors may indicate
the client must take an alternate action - for those errors
this field may indicate how long to wait before taking
the alternate action.
format: int32
type: integer
uid:
description: 'UID of the resource. (when there is a single
resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
type: string
kind:
description: 'Kind is a string value representing the REST resource
this object represents. Servers may infer this from the endpoint
the client submits requests to. Cannot be updated. In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
message:
description: A human-readable description of the status of this
operation.
type: string
metadata:
description: ListMeta describes metadata that synthetic resources
must have, including lists and various status objects. A resource
may have only one of {ObjectMeta, ListMeta}.
properties:
continue:
description: continue may be set if the user set a limit
on the number of items returned, and indicates that the
server has more data available. The value is opaque and
may be used to issue another request to the endpoint that
served this list to retrieve the next set of available
objects. Continuing a list may not be possible if the
server configuration has changed or more than a few minutes
have passed. The resourceVersion field returned when using
this continue value will be identical to the value in
the first response.
type: string
resourceVersion:
description: 'String that identifies the server''s internal
version of this object that can be used by clients to
determine when objects have changed. Value must be treated
as opaque by clients and passed unmodified back to the
server. Populated by the system. Read-only. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency'
type: string
selfLink:
description: selfLink is a URL representing this object.
Populated by the system. Read-only.
type: string
reason:
description: A machine-readable description of why this operation
is in the "Failure" status. If this value is empty there is
no information available. A Reason clarifies an HTTP status
code but does not override it.
type: string
status:
description: 'Status of the operation. One of: "Success" or
"Failure". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status'
type: string
required:
- pending
labels:
description: 'Map of string keys and values that can be used to organize
and categorize (scope and select) objects. May match selectors of
replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels'
type: object
name:
description: 'Name must be unique within a namespace. Is required when
creating resources, although some resources may allow a client to
request the generation of an appropriate name automatically. Name
is primarily intended for creation idempotence and configuration definition.
Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
type: string
namespace:
description: |-
Namespace defines the space within each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.
Must be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces
type: string
ownerReferences:
description: List of objects depended by this object. If ALL objects
in the list have been deleted, this object will be garbage collected.
If this object is managed by a controller, then an entry in this list
will point to this controller, with the controller field set to true.
There cannot be more than one managing controller.
items:
description: OwnerReference contains enough information to let you
identify an owning object. Currently, an owning object must be in
the same namespace, so there is no namespace field.
properties:
apiVersion:
description: API version of the referent.
type: string
blockOwnerDeletion:
description: If true, AND if the owner has the "foregroundDeletion"
finalizer, then the owner cannot be deleted from the key-value
store until this reference is removed. Defaults to false. To
set this field, a user needs "delete" permission of the owner,
otherwise 422 (Unprocessable Entity) will be returned.
type: boolean
controller:
description: If true, this reference points to the managing controller.
type: boolean
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
type: string
uid:
description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
type: string
required:
- apiVersion
- kind
- name
- uid
type: array
resourceVersion:
description: |-
An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.
Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency
type: string
selfLink:
description: SelfLink is a URL representing this object. Populated by
the system. Read-only.
type: string
uid:
description: |-
UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.
Populated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids
type: string
spec:
description: RuleFileSpec contains specification parameters for a Rule.
properties:
groups:
description: Content of Prometheus rule file
items:
description: RuleGroup is a list of sequentially evaluated recording
and alerting rules.
properties:
interval:
format: int64
type: integer
name:
type: string
rules:
items:
description: Rule describes an alerting or recording rule.
properties:
alert:
type: string
annotations:
type: object
expr:
type: string
for:
format: int64
type: integer
labels:
type: object
record:
type: string
required:
- expr
type: array
required:
- name
- rules
type: array
required:
- spec
version: v1
status:
acceptedNames:
kind: ""
plural: ""
conditions: null

View file

@ -23,6 +23,7 @@ rules:
- prometheuses/finalizers
- alertmanagers/finalizers
- servicemonitors
- rulefiles
verbs:
- "*"
- apiGroups:

View file

@ -29,6 +29,7 @@ const (
PrometheusKindKey = "prometheus"
AlertManagerKindKey = "alertmanager"
ServiceMonitorKindKey = "servicemonitor"
RuleFileKindKey = "rulefile"
)
type CrdKind struct {
@ -42,6 +43,7 @@ type CrdKinds struct {
Prometheus CrdKind
Alertmanager CrdKind
ServiceMonitor CrdKind
RuleFile CrdKind
}
var DefaultCrdKinds CrdKinds = CrdKinds{
@ -49,6 +51,7 @@ var DefaultCrdKinds CrdKinds = CrdKinds{
Prometheus: CrdKind{Plural: PrometheusName, Kind: PrometheusesKind, SpecName: "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.Prometheus"},
ServiceMonitor: CrdKind{Plural: ServiceMonitorName, Kind: ServiceMonitorsKind, SpecName: "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.ServiceMonitor"},
Alertmanager: CrdKind{Plural: AlertmanagerName, Kind: AlertmanagersKind, SpecName: "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.Alertmanager"},
RuleFile: CrdKind{Plural: RuleFileName, Kind: RuleFilesKind, SpecName: "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.RuleFile"},
}
// Implement the flag.Value interface
@ -60,10 +63,12 @@ func (crdkinds *CrdKinds) String() string {
func (crdkinds *CrdKinds) Set(value string) error {
*crdkinds = DefaultCrdKinds
if value == "" {
value = fmt.Sprintf("%s=%s:%s,%s=%s:%s,%s=%s:%s",
value = fmt.Sprintf("%s=%s:%s,%s=%s:%s,%s=%s:%s,%s=%s:%s",
PrometheusKindKey, PrometheusesKind, PrometheusName,
AlertManagerKindKey, AlertmanagersKind, AlertmanagerName,
ServiceMonitorKindKey, ServiceMonitorsKind, ServiceMonitorName)
ServiceMonitorKindKey, ServiceMonitorsKind, ServiceMonitorName,
RuleFileKindKey, RuleFilesKind, RuleFileName,
)
}
splited := strings.Split(value, ",")
for _, pair := range splited {
@ -77,6 +82,8 @@ func (crdkinds *CrdKinds) Set(value string) error {
(*crdkinds).ServiceMonitor = crdKind
case AlertManagerKindKey:
(*crdkinds).Alertmanager = crdKind
case RuleFileKindKey:
(*crdkinds).RuleFile = crdKind
default:
fmt.Printf("Warning: unknown kind: %s... ignoring", kindKey)
}
@ -93,6 +100,7 @@ type MonitoringV1Interface interface {
PrometheusesGetter
AlertmanagersGetter
ServiceMonitorsGetter
RuleFilesGetter
}
// +k8s:deepcopy-gen=false
@ -114,6 +122,10 @@ func (c *MonitoringV1Client) ServiceMonitors(namespace string) ServiceMonitorInt
return newServiceMonitors(c.restClient, c.dynamicClient, c.crdKinds.ServiceMonitor, namespace)
}
func (c *MonitoringV1Client) RuleFiles(namespace string) RuleFileInterface {
return newRuleFiles(c.restClient, c.dynamicClient, c.crdKinds.RuleFile, namespace)
}
func (c *MonitoringV1Client) RESTClient() rest.Interface {
return c.restClient
}

View file

@ -787,9 +787,21 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
Ref: ref("github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.StorageSpec"),
},
},
"ruleFileSelector": {
SchemaProps: spec.SchemaProps{
Description: "A selector to select which RuleFiles to mount for loading alerting rules from.",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"),
},
},
"ruleSelector": {
SchemaProps: spec.SchemaProps{
Description: "A selector to select which ConfigMaps to mount for loading rule files from.",
Description: "DEPRECATED with Prometheus Operator 'v0.20.0'. Any value in this field will just be copied to 'RuleFileSelector' field",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"),
},
},
"ruleFileNamespaceSelector": {
SchemaProps: spec.SchemaProps{
Description: "Namespaces to be selected for RuleFiles discovery. If empty, only check own namespace.",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"),
},
},
@ -1193,6 +1205,210 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
Dependencies: []string{
"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.BasicAuth", "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.RelabelConfig", "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.TLSConfig"},
},
"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.Rule": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "Rule describes an alerting or recording rule.",
Properties: map[string]spec.Schema{
"record": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"alert": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"expr": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"for": {
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
Format: "int64",
},
},
"labels": {
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
"annotations": {
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
},
Required: []string{"expr"},
},
},
Dependencies: []string{},
},
"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.RuleFile": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "RuleFile defines alerting rules for a Prometheus instance",
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Description: "Standard objects metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"spec": {
SchemaProps: spec.SchemaProps{
Description: "Specification of desired alerting rule definitions for Prometheus.",
Ref: ref("github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.RuleFileSpec"),
},
},
},
Required: []string{"spec"},
},
},
Dependencies: []string{
"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.RuleFileSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
},
"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.RuleFileList": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "A list of RuleFiles.",
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Description: "Standard list metadata More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
},
},
"items": {
SchemaProps: spec.SchemaProps{
Description: "List of Rules",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.RuleFile"),
},
},
},
},
},
},
Required: []string{"items"},
},
},
Dependencies: []string{
"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.RuleFile", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
},
"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.RuleFileSpec": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "RuleFileSpec contains specification parameters for a Rule.",
Properties: map[string]spec.Schema{
"groups": {
SchemaProps: spec.SchemaProps{
Description: "Content of Prometheus rule file",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.RuleGroup"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.RuleGroup"},
},
"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.RuleGroup": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "RuleGroup is a list of sequentially evaluated recording and alerting rules.",
Properties: map[string]spec.Schema{
"name": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"interval": {
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
Format: "int64",
},
},
"rules": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.Rule"),
},
},
},
},
},
},
Required: []string{"name", "rules"},
},
},
Dependencies: []string{
"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.Rule"},
},
"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1.ServiceMonitor": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{

View file

@ -0,0 +1,200 @@
// Copyright 2016 The prometheus-operator 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 v1
import (
"encoding/json"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
)
const (
RuleFilesKind = "RuleFile"
RuleFileName = "rulefiles"
)
type RuleFilesGetter interface {
RuleFiles(namespace string) RuleFileInterface
}
var _ RuleFileInterface = &rulefiles{}
type RuleFileInterface interface {
Create(*RuleFile) (*RuleFile, error)
Get(name string, opts metav1.GetOptions) (*RuleFile, error)
Update(*RuleFile) (*RuleFile, error)
Delete(name string, options *metav1.DeleteOptions) error
List(opts metav1.ListOptions) (runtime.Object, error)
Watch(opts metav1.ListOptions) (watch.Interface, error)
DeleteCollection(dopts *metav1.DeleteOptions, lopts metav1.ListOptions) error
}
type rulefiles struct {
restClient rest.Interface
client dynamic.ResourceInterface
crdKind CrdKind
ns string
}
func newRuleFiles(r rest.Interface, c *dynamic.Client, crdKind CrdKind, namespace string) *rulefiles {
return &rulefiles{
restClient: r,
client: c.Resource(
&metav1.APIResource{
Kind: crdKind.Kind,
Name: crdKind.Plural,
Namespaced: true,
},
namespace,
),
crdKind: crdKind,
ns: namespace,
}
}
func (s *rulefiles) Create(o *RuleFile) (*RuleFile, error) {
us, err := UnstructuredFromRuleFile(o)
if err != nil {
return nil, err
}
us, err = s.client.Create(us)
if err != nil {
return nil, err
}
return RuleFileFromUnstructured(us)
}
func (s *rulefiles) Get(name string, opts metav1.GetOptions) (*RuleFile, error) {
obj, err := s.client.Get(name, opts)
if err != nil {
return nil, err
}
return RuleFileFromUnstructured(obj)
}
func (s *rulefiles) Update(o *RuleFile) (*RuleFile, error) {
us, err := UnstructuredFromRuleFile(o)
if err != nil {
return nil, err
}
curs, err := s.Get(o.Name, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrap(err, "unable to get current version for update")
}
us.SetResourceVersion(curs.ObjectMeta.ResourceVersion)
us, err = s.client.Update(us)
if err != nil {
return nil, err
}
return RuleFileFromUnstructured(us)
}
func (s *rulefiles) Delete(name string, options *metav1.DeleteOptions) error {
return s.client.Delete(name, options)
}
func (s *rulefiles) List(opts metav1.ListOptions) (runtime.Object, error) {
req := s.restClient.Get().
Namespace(s.ns).
Resource(s.crdKind.Plural)
b, err := req.DoRaw()
if err != nil {
return nil, err
}
var sm RuleFileList
return &sm, json.Unmarshal(b, &sm)
}
func (s *rulefiles) Watch(opts metav1.ListOptions) (watch.Interface, error) {
r, err := s.restClient.Get().
Prefix("watch").
Namespace(s.ns).
Resource(s.crdKind.Plural).
Stream()
if err != nil {
return nil, err
}
return watch.NewStreamWatcher(&ruleFileDecoder{
dec: json.NewDecoder(r),
close: r.Close,
}), nil
}
func (s *rulefiles) DeleteCollection(dopts *metav1.DeleteOptions, lopts metav1.ListOptions) error {
return s.client.DeleteCollection(dopts, lopts)
}
// RuleFileFromUnstructured unmarshals a RuleFile object from dynamic client's unstructured
func RuleFileFromUnstructured(r *unstructured.Unstructured) (*RuleFile, error) {
b, err := json.Marshal(r.Object)
if err != nil {
return nil, err
}
var s RuleFile
if err := json.Unmarshal(b, &s); err != nil {
return nil, err
}
s.TypeMeta.Kind = RuleFilesKind
s.TypeMeta.APIVersion = Group + "/" + Version
return &s, nil
}
// UnstructuredFromRuleFile marshals a RuleFile object into dynamic client's unstructured
func UnstructuredFromRuleFile(s *RuleFile) (*unstructured.Unstructured, error) {
s.TypeMeta.Kind = RuleFilesKind
s.TypeMeta.APIVersion = Group + "/" + Version
b, err := json.Marshal(s)
if err != nil {
return nil, err
}
var r unstructured.Unstructured
if err := json.Unmarshal(b, &r.Object); err != nil {
return nil, err
}
return &r, nil
}
type ruleFileDecoder struct {
dec *json.Decoder
close func() error
}
func (d *ruleFileDecoder) Close() {
d.close()
}
func (d *ruleFileDecoder) Decode() (action watch.EventType, object runtime.Object, err error) {
var e struct {
Type watch.EventType
Object RuleFile
}
if err := d.dec.Decode(&e); err != nil {
return watch.Error, nil, err
}
return e.Type, &e.Object, nil
}

View file

@ -15,6 +15,8 @@
package v1
import (
"time"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@ -98,8 +100,14 @@ type PrometheusSpec struct {
RoutePrefix string `json:"routePrefix,omitempty"`
// Storage spec to specify how storage shall be used.
Storage *StorageSpec `json:"storage,omitempty"`
// A selector to select which ConfigMaps to mount for loading rule files from.
// A selector to select which RuleFiles to mount for loading alerting rules from.
RuleFileSelector *metav1.LabelSelector `json:"ruleFileSelector,omitempty"`
// DEPRECATED with Prometheus Operator 'v0.20.0'. Any value in this field
// will just be copied to 'RuleFileSelector' field
RuleSelector *metav1.LabelSelector `json:"ruleSelector,omitempty"`
// Namespaces to be selected for RuleFiles discovery. If empty, only
// check own namespace.
RuleFileNamespaceSelector *metav1.LabelSelector `json:"ruleFileNamespaceSelector,omitempty"`
// Define details regarding alerting.
Alerting *AlertingSpec `json:"alerting,omitempty"`
// Define resources requests and limits for single Pods.
@ -397,6 +405,57 @@ type ServiceMonitorList struct {
Items []*ServiceMonitor `json:"items"`
}
// A list of RuleFiles.
// +k8s:openapi-gen=true
type RuleFileList struct {
metav1.TypeMeta `json:",inline"`
// Standard list metadata
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
metav1.ListMeta `json:"metadata,omitempty"`
// List of Rules
Items []*RuleFile `json:"items"`
}
// RuleFile defines alerting rules for a Prometheus instance
// +k8s:openapi-gen=true
type RuleFile struct {
metav1.TypeMeta `json:",inline"`
// Standard objects metadata. More info:
// http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
metav1.ObjectMeta `json:"metadata,omitempty"`
// Specification of desired alerting rule definitions for Prometheus.
Spec RuleFileSpec `json:"spec"`
}
// RuleFileSpec contains specification parameters for a Rule.
// +k8s:openapi-gen=true
type RuleFileSpec struct {
// Content of Prometheus rule file
Groups []RuleGroup `json:"groups,omitempty"`
}
// RuleGroup and Rule are copied instead of vendored because the
// upstream Prometheus struct definitions don't have json struct tags.
// RuleGroup is a list of sequentially evaluated recording and alerting rules.
// +k8s:openapi-gen=true
type RuleGroup struct {
Name string `json:"name"`
Interval time.Duration `json:"interval,omitempty"`
Rules []Rule `json:"rules"`
}
// Rule describes an alerting or recording rule.
// +k8s:openapi-gen=true
type Rule struct {
Record string `json:"record,omitempty"`
Alert string `json:"alert,omitempty"`
Expr string `json:"expr"`
For time.Duration `json:"for,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}
// Describes an Alertmanager cluster.
// +k8s:openapi-gen=true
type Alertmanager struct {
@ -550,3 +609,11 @@ func (l *ServiceMonitor) DeepCopyObject() runtime.Object {
func (l *ServiceMonitorList) DeepCopyObject() runtime.Object {
return l.DeepCopy()
}
func (f *RuleFile) DeepCopyObject() runtime.Object {
return f.DeepCopy()
}
func (l *RuleFileList) DeepCopyObject() runtime.Object {
return l.DeepCopy()
}

View file

@ -274,6 +274,7 @@ func (in *CrdKinds) DeepCopyInto(out *CrdKinds) {
out.Prometheus = in.Prometheus
out.Alertmanager = in.Alertmanager
out.ServiceMonitor = in.ServiceMonitor
out.RuleFile = in.RuleFile
return
}
@ -485,6 +486,15 @@ func (in *PrometheusSpec) DeepCopyInto(out *PrometheusSpec) {
(*in).DeepCopyInto(*out)
}
}
if in.RuleFileSelector != nil {
in, out := &in.RuleFileSelector, &out.RuleFileSelector
if *in == nil {
*out = nil
} else {
*out = new(meta_v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
}
if in.RuleSelector != nil {
in, out := &in.RuleSelector, &out.RuleSelector
if *in == nil {
@ -494,6 +504,15 @@ func (in *PrometheusSpec) DeepCopyInto(out *PrometheusSpec) {
(*in).DeepCopyInto(*out)
}
}
if in.RuleFileNamespaceSelector != nil {
in, out := &in.RuleFileNamespaceSelector, &out.RuleFileNamespaceSelector
if *in == nil {
*out = nil
} else {
*out = new(meta_v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
}
if in.Alerting != nil {
in, out := &in.Alerting, &out.Alerting
if *in == nil {
@ -712,6 +731,131 @@ func (in *RemoteWriteSpec) DeepCopy() *RemoteWriteSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Rule) DeepCopyInto(out *Rule) {
*out = *in
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule.
func (in *Rule) DeepCopy() *Rule {
if in == nil {
return nil
}
out := new(Rule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RuleFile) DeepCopyInto(out *RuleFile) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleFile.
func (in *RuleFile) DeepCopy() *RuleFile {
if in == nil {
return nil
}
out := new(RuleFile)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RuleFileList) DeepCopyInto(out *RuleFileList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]*RuleFile, len(*in))
for i := range *in {
if (*in)[i] == nil {
(*out)[i] = nil
} else {
(*out)[i] = new(RuleFile)
(*in)[i].DeepCopyInto((*out)[i])
}
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleFileList.
func (in *RuleFileList) DeepCopy() *RuleFileList {
if in == nil {
return nil
}
out := new(RuleFileList)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RuleFileSpec) DeepCopyInto(out *RuleFileSpec) {
*out = *in
if in.Groups != nil {
in, out := &in.Groups, &out.Groups
*out = make([]RuleGroup, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleFileSpec.
func (in *RuleFileSpec) DeepCopy() *RuleFileSpec {
if in == nil {
return nil
}
out := new(RuleFileSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RuleGroup) DeepCopyInto(out *RuleGroup) {
*out = *in
if in.Rules != nil {
in, out := &in.Rules, &out.Rules
*out = make([]Rule, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleGroup.
func (in *RuleGroup) DeepCopy() *RuleGroup {
if in == nil {
return nil
}
out := new(RuleGroup)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServiceMonitor) DeepCopyInto(out *ServiceMonitor) {
*out = *in

View file

@ -16,6 +16,8 @@ package prometheus
import (
"bytes"
"crypto/md5"
"encoding/json"
"fmt"
"reflect"
"strings"
@ -58,12 +60,13 @@ type Operator struct {
crdclient apiextensionsclient.Interface
logger log.Logger
promInf cache.SharedIndexInformer
smonInf cache.SharedIndexInformer
cmapInf cache.SharedIndexInformer
secrInf cache.SharedIndexInformer
ssetInf cache.SharedIndexInformer
nsInf cache.SharedIndexInformer
promInf cache.SharedIndexInformer
smonInf cache.SharedIndexInformer
ruleFileInf cache.SharedIndexInformer
cmapInf cache.SharedIndexInformer
secrInf cache.SharedIndexInformer
ssetInf cache.SharedIndexInformer
nsInf cache.SharedIndexInformer
queue workqueue.RateLimitingInterface
@ -213,6 +216,19 @@ func New(conf Config, logger log.Logger) (*Operator, error) {
UpdateFunc: c.handleSmonUpdate,
})
c.ruleFileInf = cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: mclient.MonitoringV1().RuleFiles(c.config.Namespace).List,
WatchFunc: mclient.MonitoringV1().RuleFiles(c.config.Namespace).Watch,
},
&monitoringv1.RuleFile{}, resyncPeriod, cache.Indexers{},
)
c.ruleFileInf.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: c.handleRuleAdd,
DeleteFunc: c.handleRuleDelete,
UpdateFunc: c.handleRuleUpdate,
})
c.cmapInf = cache.NewSharedIndexInformer(
cache.NewListWatchFromClient(c.kclient.Core().RESTClient(), "configmaps", c.config.Namespace, fields.Everything()),
&v1.ConfigMap{}, resyncPeriod, cache.Indexers{},
@ -296,6 +312,7 @@ func (c *Operator) Run(stopc <-chan struct{}) error {
go c.promInf.Run(stopc)
go c.smonInf.Run(stopc)
go c.ruleFileInf.Run(stopc)
go c.cmapInf.Run(stopc)
go c.secrInf.Run(stopc)
go c.ssetInf.Run(stopc)
@ -470,6 +487,7 @@ func (c *Operator) syncNodeEndpoints() error {
return nil
}
// TODO: Don't enque just for the namespace
func (c *Operator) handleSmonAdd(obj interface{}) {
o, ok := c.getObject(obj)
if ok {
@ -477,6 +495,7 @@ func (c *Operator) handleSmonAdd(obj interface{}) {
}
}
// TODO: Don't enque just for the namespace
func (c *Operator) handleSmonUpdate(old, cur interface{}) {
o, ok := c.getObject(cur)
if ok {
@ -484,6 +503,7 @@ func (c *Operator) handleSmonUpdate(old, cur interface{}) {
}
}
// TODO: Don't enque just for the namespace
func (c *Operator) handleSmonDelete(obj interface{}) {
o, ok := c.getObject(obj)
if ok {
@ -491,6 +511,31 @@ func (c *Operator) handleSmonDelete(obj interface{}) {
}
}
// TODO: Don't enque just for the namespace
func (c *Operator) handleRuleAdd(obj interface{}) {
o, ok := c.getObject(obj)
if ok {
c.enqueueForNamespace(o.GetNamespace())
}
}
// TODO: Don't enque just for the namespace
func (c *Operator) handleRuleUpdate(old, cur interface{}) {
o, ok := c.getObject(cur)
if ok {
c.enqueueForNamespace(o.GetNamespace())
}
}
// TODO: Don't enque just for the namespace
func (c *Operator) handleRuleDelete(obj interface{}) {
o, ok := c.getObject(obj)
if ok {
c.enqueueForNamespace(o.GetNamespace())
}
}
// TODO: Do we need to enque secrets just for the namespace or in general?
func (c *Operator) handleSecretDelete(obj interface{}) {
o, ok := c.getObject(obj)
if ok {
@ -512,6 +557,7 @@ func (c *Operator) handleSecretAdd(obj interface{}) {
}
}
// TODO: Do we need to enque configmaps just for the namespace or in general?
func (c *Operator) handleConfigMapAdd(obj interface{}) {
o, ok := c.getObject(obj)
if ok {
@ -673,15 +719,8 @@ func (c *Operator) sync(key string) error {
return err
}
if !exists {
// TODO(fabxc): we want to do server side deletion due to the variety of
// resources we create.
// Doing so just based on the deletion event is not reliable, so
// we have to garbage collect the controller-created resources in some other way.
//
// Let's rely on the index key matching that of the created configmap and StatefulSet for now.
// This does not work if we delete Prometheus resources as the
// controller is not running that could be solved via garbage collection later.
return c.destroyPrometheus(key)
// Dependent resources are cleaned up by K8s via OwnerReferences
return nil
}
p := obj.(*monitoringv1.Prometheus)
@ -691,22 +730,22 @@ func (c *Operator) sync(key string) error {
level.Info(c.logger).Log("msg", "sync prometheus", "key", key)
ruleFileConfigMaps, err := c.ruleFileConfigMaps(p)
err = c.createOrUpdateRuleFileConfigMap(p)
if err != nil {
return errors.Wrap(err, "retrieving rule file configmaps failed")
return err
}
// If no service monitor selectors are configured, the user wants to manage
// configuration himself.
// configuration themselves.
if p.Spec.ServiceMonitorSelector != nil {
// We just always regenerate the configuration to be safe.
if err := c.createConfig(p, ruleFileConfigMaps); err != nil {
if err := c.createOrUpdateConfigurationSecret(p); err != nil {
return errors.Wrap(err, "creating config failed")
}
}
// Create Secret if it doesn't exist.
s, err := makeEmptyConfig(p, ruleFileConfigMaps, c.config)
s, err := makeEmptyConfigurationSecret(p, c.config)
if err != nil {
return errors.Wrap(err, "generating empty config secret failed")
}
@ -732,20 +771,37 @@ func (c *Operator) sync(key string) error {
return errors.Wrap(err, "retrieving statefulset failed")
}
newSSetInputChecksum, err := createSSetInputChecksum(*p, c.config)
if err != nil {
return err
}
if !exists {
sset, err := makeStatefulSet(*p, nil, &c.config, ruleFileConfigMaps)
level.Debug(c.logger).Log("msg", "no current Prometheus statefulset found")
sset, err := makeStatefulSet(*p, "", &c.config, newSSetInputChecksum)
if err != nil {
return errors.Wrap(err, "creating statefulset failed")
return errors.Wrap(err, "making statefulset failed")
}
level.Debug(c.logger).Log("msg", "creating Prometheus statefulset")
if _, err := ssetClient.Create(sset); err != nil {
return errors.Wrap(err, "creating statefulset failed")
}
return nil
}
sset, err := makeStatefulSet(*p, obj.(*appsv1.StatefulSet), &c.config, ruleFileConfigMaps)
if err != nil {
return errors.Wrap(err, "updating statefulset failed")
oldSSetInputChecksum := obj.(*appsv1.StatefulSet).ObjectMeta.Annotations[sSetInputChecksumName]
if newSSetInputChecksum == oldSSetInputChecksum {
level.Debug(c.logger).Log("msg", "new statefulset generation inputs match current, skipping any actions")
return nil
}
sset, err := makeStatefulSet(*p, obj.(*appsv1.StatefulSet).Spec.PodManagementPolicy, &c.config, newSSetInputChecksum)
if err != nil {
return errors.Wrap(err, "making statefulset failed")
}
level.Debug(c.logger).Log("msg", "updating current Prometheus statefulset")
if _, err := ssetClient.Update(sset); err != nil {
return errors.Wrap(err, "updating statefulset failed")
}
@ -753,22 +809,19 @@ func (c *Operator) sync(key string) error {
return nil
}
func (c *Operator) ruleFileConfigMaps(p *monitoringv1.Prometheus) ([]*v1.ConfigMap, error) {
res := []*v1.ConfigMap{}
ruleSelector, err := metav1.LabelSelectorAsSelector(p.Spec.RuleSelector)
// TODO: rename sSetInputChecksum
func createSSetInputChecksum(p monitoringv1.Prometheus, c Config) (string, error) {
json, err := json.Marshal(
struct {
P monitoringv1.Prometheus
C Config
}{p, c},
)
if err != nil {
return nil, err
return "", errors.Wrap(err, "failed to marshal Prometheus CRD and config to json")
}
cache.ListAllByNamespace(c.cmapInf.GetIndexer(), p.Namespace, ruleSelector, func(obj interface{}) {
_, ok := c.keyFunc(obj)
if ok {
res = append(res, obj.(*v1.ConfigMap))
}
})
return res, nil
return fmt.Sprintf("%x", md5.Sum(json)), nil
}
func ListOptions(name string) metav1.ListOptions {
@ -835,82 +888,18 @@ func needsUpdate(pod *v1.Pod, tmpl v1.PodTemplateSpec) bool {
return false
}
// TODO(brancz): Remove this function once Kubernetes 1.7 compatibility is dropped.
// Starting with Kubernetes 1.8 OwnerReferences are properly handled for CRDs.
func (c *Operator) destroyPrometheus(key string) error {
ssetKey := prometheusKeyToStatefulSetKey(key)
obj, exists, err := c.ssetInf.GetStore().GetByKey(ssetKey)
if err != nil {
return errors.Wrap(err, "retrieving statefulset from cache failed")
}
if !exists {
return nil
}
sset := obj.(*appsv1.StatefulSet)
*sset.Spec.Replicas = 0
// Update the replica count to 0 and wait for all pods to be deleted.
ssetClient := c.kclient.AppsV1beta2().StatefulSets(sset.Namespace)
if _, err := ssetClient.Update(sset); err != nil {
return errors.Wrap(err, "updating statefulset for scale-down failed")
}
podClient := c.kclient.Core().Pods(sset.Namespace)
// TODO(fabxc): temprorary solution until StatefulSet status provides necessary info to know
// whether scale-down completed.
for {
pods, err := podClient.List(ListOptions(prometheusNameFromStatefulSetName(sset.Name)))
if err != nil {
return errors.Wrap(err, "retrieving pods of statefulset failed")
}
if len(pods.Items) == 0 {
break
}
time.Sleep(50 * time.Millisecond)
}
// StatefulSet scaled down, we can delete it.
if err := ssetClient.Delete(sset.Name, nil); err != nil {
return errors.Wrap(err, "deleting statefulset failed")
}
// Delete the auto-generate configuration.
// TODO(fabxc): add an ownerRef at creation so we don't delete Secrets
// manually created for Prometheus servers with no ServiceMonitor selectors.
s := c.kclient.Core().Secrets(sset.Namespace)
secret, err := s.Get(sset.Name, metav1.GetOptions{})
if err != nil && !apierrors.IsNotFound(err) {
return errors.Wrap(err, "retrieving config Secret failed")
}
if apierrors.IsNotFound(err) {
// Secret does not exist so nothing to clean up
return nil
}
value, found := secret.Labels[managedByOperatorLabel]
if found && value == managedByOperatorLabelValue {
if err := s.Delete(sset.Name, nil); err != nil {
return errors.Wrap(err, "deleting config Secret failed")
}
}
return nil
}
func loadAdditionalConfigsSecret(additionalConfigs *v1.SecretKeySelector, s *v1.SecretList) ([]byte, error) {
if additionalConfigs != nil {
func loadAdditionalScrapeConfigsSecret(additionalScrapeConfigs *v1.SecretKeySelector, s *v1.SecretList) ([]byte, error) {
if additionalScrapeConfigs != nil {
for _, secret := range s.Items {
if secret.Name == additionalConfigs.Name {
if c, ok := secret.Data[additionalConfigs.Key]; ok {
if secret.Name == additionalScrapeConfigs.Name {
if c, ok := secret.Data[additionalScrapeConfigs.Key]; ok {
return c, nil
}
return nil, fmt.Errorf("key %v could not be found in Secret %v.", additionalConfigs.Key, additionalConfigs.Name)
return nil, fmt.Errorf("key %v could not be found in Secret %v.", additionalScrapeConfigs.Key, additionalScrapeConfigs.Name)
}
}
return nil, fmt.Errorf("secret %v could not be found.", additionalConfigs.Name)
return nil, fmt.Errorf("secret %v could not be found.", additionalScrapeConfigs.Name)
}
return nil, nil
}
@ -997,7 +986,7 @@ func (c *Operator) loadBasicAuthSecrets(mons map[string]*monitoringv1.ServiceMon
}
func (c *Operator) createConfig(p *monitoringv1.Prometheus, ruleFileConfigMaps []*v1.ConfigMap) error {
func (c *Operator) createOrUpdateConfigurationSecret(p *monitoringv1.Prometheus) error {
smons, err := c.selectServiceMonitors(p)
if err != nil {
return errors.Wrap(err, "selecting ServiceMonitors failed")
@ -1017,25 +1006,22 @@ func (c *Operator) createConfig(p *monitoringv1.Prometheus, ruleFileConfigMaps [
return err
}
additionalScrapeConfigs, err := loadAdditionalConfigsSecret(p.Spec.AdditionalScrapeConfigs, listSecrets)
additionalScrapeConfigs, err := loadAdditionalScrapeConfigsSecret(p.Spec.AdditionalScrapeConfigs, listSecrets)
if err != nil {
return errors.Wrap(err, "loading additional scrape configs from Secret failed")
}
additionalAlertManagerConfigs, err := loadAdditionalConfigsSecret(p.Spec.AdditionalAlertManagerConfigs, listSecrets)
additionalAlertManagerConfigs, err := loadAdditionalScrapeConfigsSecret(p.Spec.AdditionalAlertManagerConfigs, listSecrets)
if err != nil {
return errors.Wrap(err, "loading additional alert manager configs from Secret failed")
}
// Update secret based on the most recent configuration.
conf, err := generateConfig(p, smons, len(ruleFileConfigMaps), basicAuthSecrets, additionalScrapeConfigs, additionalAlertManagerConfigs)
conf, err := generateConfig(p, smons, basicAuthSecrets, additionalScrapeConfigs, additionalAlertManagerConfigs)
if err != nil {
return errors.Wrap(err, "generating config failed")
}
s, err := makeConfigSecret(p, ruleFileConfigMaps, c.config)
if err != nil {
return errors.Wrap(err, "generating base secret failed")
}
s := makeConfigSecret(p, c.config)
s.ObjectMeta.Annotations = map[string]string{
"generated": "true",
}
@ -1049,23 +1035,21 @@ func (c *Operator) createConfig(p *monitoringv1.Prometheus, ruleFileConfigMaps [
}
var (
generatedConf = s.Data[configFilename]
generatedConfigMaps = s.Data[ruleConfigmapsFilename]
curConfig, curConfigFound = curSecret.Data[configFilename]
curConfigMaps, curConfigMapsFound = curSecret.Data[ruleConfigmapsFilename]
generatedConf = s.Data[configFilename]
curConfig, curConfigFound = curSecret.Data[configFilename]
)
if curConfigFound && curConfigMapsFound {
if bytes.Equal(curConfig, generatedConf) && bytes.Equal(curConfigMaps, generatedConfigMaps) {
level.Debug(c.logger).Log("msg", "updating config skipped, no configuration change")
if curConfigFound {
if bytes.Equal(curConfig, generatedConf) {
level.Debug(c.logger).Log("msg", "updating Prometheus configuration secret skipped, no configuration change")
return nil
} else {
level.Debug(c.logger).Log("msg", "current config or current configmaps has changed")
level.Debug(c.logger).Log("msg", "current Prometheus configuration has changed")
}
} else {
level.Debug(c.logger).Log("msg", "no current config or current configmaps found", "currentConfigFound", curConfigFound, "currentConfigMapsFound", curConfigMapsFound)
level.Debug(c.logger).Log("msg", "no current Prometheus configuration secret found", "currentConfigFound", curConfigFound)
}
level.Debug(c.logger).Log("msg", "updating configuration")
level.Debug(c.logger).Log("msg", "updating Prometheus configuration secret")
_, err = sClient.Update(s)
return err
}
@ -1117,8 +1101,9 @@ func (c *Operator) selectServiceMonitors(p *monitoringv1.Prometheus) (map[string
func (c *Operator) createCRDs() error {
_, pErr := c.mclient.MonitoringV1().Prometheuses(c.config.Namespace).List(metav1.ListOptions{})
_, sErr := c.mclient.MonitoringV1().ServiceMonitors(c.config.Namespace).List(metav1.ListOptions{})
if pErr == nil && sErr == nil {
// If Prometheus and ServiceMonitor objects are already registered, we
_, rErr := c.mclient.MonitoringV1().RuleFiles(c.config.Namespace).List(metav1.ListOptions{})
if pErr == nil && sErr == nil && rErr == nil {
// If Prometheus, RuleFile and ServiceMonitor objects are already registered, we
// won't attempt to do so again.
return nil
}
@ -1126,6 +1111,7 @@ func (c *Operator) createCRDs() error {
crds := []*extensionsobj.CustomResourceDefinition{
k8sutil.NewCustomResourceDefinition(c.config.CrdKinds.Prometheus, c.config.CrdGroup, c.config.Labels.LabelsMap, c.config.EnableValidation),
k8sutil.NewCustomResourceDefinition(c.config.CrdKinds.ServiceMonitor, c.config.CrdGroup, c.config.Labels.LabelsMap, c.config.EnableValidation),
k8sutil.NewCustomResourceDefinition(c.config.CrdKinds.RuleFile, c.config.CrdGroup, c.config.Labels.LabelsMap, c.config.EnableValidation),
}
crdClient := c.crdclient.ApiextensionsV1beta1().CustomResourceDefinitions()
@ -1143,6 +1129,7 @@ func (c *Operator) createCRDs() error {
}{
{"Prometheus", c.mclient.MonitoringV1().Prometheuses(c.config.Namespace).List},
{"ServiceMonitor", c.mclient.MonitoringV1().ServiceMonitors(c.config.Namespace).List},
{"RuleFile", c.mclient.MonitoringV1().RuleFiles(c.config.Namespace).List},
}
for _, crdListFunc := range crdListFuncs {

View file

@ -16,6 +16,8 @@ package prometheus
import (
"testing"
monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
)
func TestListOptions(t *testing.T) {
@ -26,3 +28,24 @@ func TestListOptions(t *testing.T) {
}
}
}
func TestCreateStatefulSetChecksum(t *testing.T) {
p1 := monitoringv1.Prometheus{}
p1.Spec.Version = "v1.7.0"
p2 := monitoringv1.Prometheus{}
p2.Spec.Version = "v1.7.2"
c := Config{}
p1Checksum, err := createSSetInputChecksum(p1, c)
if err != nil {
t.Fatal(err)
}
p2Checksum, err := createSSetInputChecksum(p2, c)
if err != nil {
t.Fatal(err)
}
if p1Checksum == p2Checksum {
t.Fatal("expected two different Prometheus CRDs to result in two different checksums but got equal checksums")
}
}

View file

@ -36,10 +36,6 @@ func sanitizeLabelName(name string) string {
return invalidLabelCharRE.ReplaceAllString(name, "_")
}
func configMapRuleFileFolder(configMapNumber int) string {
return fmt.Sprintf("/etc/prometheus/config_out/rules/rules-%d/", configMapNumber)
}
func stringMapToMapSlice(m map[string]string) yaml.MapSlice {
res := yaml.MapSlice{}
ks := make([]string, 0)
@ -90,7 +86,7 @@ func buildExternalLabels(p *v1.Prometheus) yaml.MapSlice {
return stringMapToMapSlice(m)
}
func generateConfig(p *v1.Prometheus, mons map[string]*v1.ServiceMonitor, ruleConfigMaps int, basicAuthSecrets map[string]BasicAuthCredentials, additionalScrapeConfigs []byte, additionalAlertManagerConfigs []byte) ([]byte, error) {
func generateConfig(p *v1.Prometheus, mons map[string]*v1.ServiceMonitor, basicAuthSecrets map[string]BasicAuthCredentials, additionalScrapeConfigs []byte, additionalAlertManagerConfigs []byte) ([]byte, error) {
versionStr := p.Spec.Version
if versionStr == "" {
versionStr = DefaultVersion
@ -122,16 +118,10 @@ func generateConfig(p *v1.Prometheus, mons map[string]*v1.ServiceMonitor, ruleCo
},
})
if ruleConfigMaps > 0 {
configMaps := make([]string, ruleConfigMaps)
for i := 0; i < ruleConfigMaps; i++ {
configMaps[i] = configMapRuleFileFolder(i) + "*"
}
cfg = append(cfg, yaml.MapItem{
Key: "rule_files",
Value: configMaps,
})
}
cfg = append(cfg, yaml.MapItem{
Key: "rule_files",
Value: []string{"/etc/prometheus/rules/*.yaml"},
})
identifiers := make([]string, len(mons))
i := 0

View file

@ -25,6 +25,8 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
"github.com/kylelemons/godebug/pretty"
)
func TestConfigGeneration(t *testing.T) {
@ -104,7 +106,6 @@ func TestAlertmanagerBearerToken(t *testing.T) {
},
},
nil,
0,
map[string]BasicAuthCredentials{},
nil,
nil,
@ -122,6 +123,8 @@ func TestAlertmanagerBearerToken(t *testing.T) {
external_labels:
prometheus: default/test
prometheus_replica: $(POD_NAME)
rule_files:
- /etc/prometheus/rules/*.yaml
scrape_configs: []
alerting:
alert_relabel_configs:
@ -150,7 +153,8 @@ alerting:
result := string(cfg)
if expected != result {
t.Fatalf("Unexpected result.\n\nGot:\n\n%s\n\nExpected:\n\n%s\n\n", result, expected)
pretty.Compare(expected, result)
t.Fatal("expected Prometheus configuration and actual configuration do not match")
}
}
@ -183,7 +187,7 @@ func generateTestConfig(version string) ([]byte, error) {
"group": "group1",
},
},
RuleSelector: &metav1.LabelSelector{
RuleFileSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"role": "rulefile",
},
@ -202,7 +206,6 @@ func generateTestConfig(version string) ([]byte, error) {
},
},
makeServiceMonitors(),
1,
map[string]BasicAuthCredentials{},
nil,
nil,

212
pkg/prometheus/rulefile.go Normal file
View file

@ -0,0 +1,212 @@
// Copyright 2016 The prometheus-operator 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 prometheus
import (
"crypto/sha256"
"fmt"
"sort"
"strings"
monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
"k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
"github.com/go-kit/kit/log/level"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
)
func (c *Operator) createOrUpdateRuleFileConfigMap(p *monitoringv1.Prometheus) error {
cClient := c.kclient.CoreV1().ConfigMaps(p.Namespace)
namespaces, err := c.selectRuleFileNamespaces(p)
if err != nil {
return err
}
ruleFiles, err := c.selectRuleFiles(p, namespaces)
if err != nil {
return err
}
newConfigMap := c.makeRulesConfigMap(p, ruleFiles)
currentConfigMap, err := cClient.Get(prometheusRuleFilesConfigMapName(p.Name), metav1.GetOptions{})
if err != nil && !apierrors.IsNotFound(err) {
return err
}
isNotFound := false
if apierrors.IsNotFound(err) {
level.Debug(c.logger).Log(
"msg", "no RuleFiles configmap created yet",
"namespace", p.Namespace,
"prometheus", p.Name,
)
isNotFound = true
}
newChecksum := checksumRuleFiles(ruleFiles)
currentChecksum := checksumRuleFiles(currentConfigMap.Data)
if newChecksum == currentChecksum && !isNotFound {
level.Debug(c.logger).Log(
"msg", "no RuleFile changes",
"namespace", p.Namespace,
"prometheus", p.Name,
)
return nil
}
if isNotFound {
level.Debug(c.logger).Log(
"msg", "no RuleFile found, creating new one",
"namespace", p.Namespace,
"prometheus", p.Name,
)
_, err = cClient.Create(newConfigMap)
} else {
level.Debug(c.logger).Log(
"msg", "updating RuleFile",
"namespace", p.Namespace,
"prometheus", p.Name,
)
_, err = cClient.Update(newConfigMap)
}
if err != nil {
return err
}
return nil
}
func (c *Operator) selectRuleFileNamespaces(p *monitoringv1.Prometheus) ([]string, error) {
namespaces := []string{}
// If 'RuleFilesNamespaceSelector' is nil, only check own namespace.
if p.Spec.RuleFileNamespaceSelector == nil {
namespaces = append(namespaces, p.Namespace)
} else {
ruleFileNamespaceSelector, err := metav1.LabelSelectorAsSelector(p.Spec.RuleFileNamespaceSelector)
if err != nil {
return namespaces, errors.Wrap(err, "convert rule file namespace label selector to selector")
}
cache.ListAll(c.nsInf.GetStore(), ruleFileNamespaceSelector, func(obj interface{}) {
namespaces = append(namespaces, obj.(*v1.Namespace).Name)
})
}
level.Debug(c.logger).Log(
"msg", "selected RuleFileNamespaces",
"namespaces", strings.Join(namespaces, ","),
"namespace", p.Namespace,
"prometheus", p.Name,
)
return namespaces, nil
}
func (c *Operator) selectRuleFiles(p *monitoringv1.Prometheus, namespaces []string) (map[string]string, error) {
ruleFiles := map[string]string{}
// With Prometheus Operator v0.20.0 the 'RuleSelector' field in the Prometheus
// CRD Spec is deprecated. Any value in 'RuleSelector' is just copied to the new
// field 'RuleFileSelector'.
if p.Spec.RuleFileSelector == nil && p.Spec.RuleSelector != nil {
p.Spec.RuleFileSelector = p.Spec.RuleSelector
}
fileSelector, err := metav1.LabelSelectorAsSelector(p.Spec.RuleFileSelector)
if err != nil {
return ruleFiles, errors.Wrap(err, "convert rule file label selector to selector")
}
for _, ns := range namespaces {
var marshalErr error
err := cache.ListAllByNamespace(c.ruleFileInf.GetIndexer(), ns, fileSelector, func(obj interface{}) {
file := obj.(*monitoringv1.RuleFile)
content, err := yaml.Marshal(file.Spec)
if err != nil {
marshalErr = err
return
}
ruleFiles[fmt.Sprintf("%v-%v.yaml", file.Namespace, file.Name)] = string(content)
})
if err != nil {
return nil, err
}
if marshalErr != nil {
return nil, marshalErr
}
}
// sort ruleFiles map
filenames := []string{}
for k, _ := range ruleFiles {
filenames = append(filenames, k)
}
sort.Strings(filenames)
sortedRuleFiles := map[string]string{}
for _, name := range filenames {
sortedRuleFiles[name] = ruleFiles[name]
}
level.Debug(c.logger).Log(
"msg", "selected RuleFiles",
"rulefiles", strings.Join(filenames, ","),
"namespace", p.Namespace,
"prometheus", p.Name,
)
return sortedRuleFiles, nil
}
func (c *Operator) makeRulesConfigMap(p *monitoringv1.Prometheus, ruleFiles map[string]string) *v1.ConfigMap {
boolTrue := true
return &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: prometheusRuleFilesConfigMapName(p.Name),
Labels: managedByOperatorLabels,
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: p.APIVersion,
BlockOwnerDeletion: &boolTrue,
Controller: &boolTrue,
Kind: p.Kind,
Name: p.Name,
UID: p.UID,
},
},
},
Data: ruleFiles,
}
}
func checksumRuleFiles(files map[string]string) string {
var sum string
for name, value := range files {
sum = sum + name + value
}
return fmt.Sprintf("%x", sha256.Sum256([]byte(sum)))
}
func prometheusRuleFilesConfigMapName(prometheusName string) string {
return "prometheus-" + prometheusName + "-rules"
}

View file

@ -15,12 +15,9 @@
package prometheus
import (
"crypto/sha256"
"encoding/json"
"fmt"
"net/url"
"path"
"sort"
"strings"
appsv1 "k8s.io/api/apps/v1beta2"
@ -32,7 +29,6 @@ import (
"github.com/blang/semver"
monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
"github.com/pkg/errors"
yaml "gopkg.in/yaml.v2"
)
const (
@ -42,11 +38,11 @@ const (
storageDir = "/prometheus"
confDir = "/etc/prometheus/config"
confOutDir = "/etc/prometheus/config_out"
rulesDir = "/etc/prometheus/config_out/rules"
rulesDir = "/etc/prometheus/rules"
secretsDir = "/etc/prometheus/secrets/"
configFilename = "prometheus.yaml"
configEnvsubstFilename = "prometheus.env.yaml"
ruleConfigmapsFilename = "configmaps.json"
sSetInputChecksumName = "prometheus-operator-input-checksum"
)
var (
@ -78,7 +74,12 @@ var (
}
)
func makeStatefulSet(p monitoringv1.Prometheus, old *appsv1.StatefulSet, config *Config, ruleConfigMaps []*v1.ConfigMap) (*appsv1.StatefulSet, error) {
func makeStatefulSet(
p monitoringv1.Prometheus,
previousPodManagementPolicy appsv1.PodManagementPolicyType,
config *Config,
inputChecksum string,
) (*appsv1.StatefulSet, error) {
// TODO(fabxc): is this the right point to inject defaults?
// Ideally we would do it before storing but that's currently not possible.
// Potentially an update handler on first insertion.
@ -126,7 +127,7 @@ func makeStatefulSet(p monitoringv1.Prometheus, old *appsv1.StatefulSet, config
}
}
spec, err := makeStatefulSetSpec(p, config, ruleConfigMaps)
spec, err := makeStatefulSetSpec(p, config)
if err != nil {
return nil, errors.Wrap(err, "make StatefulSet spec")
}
@ -151,6 +152,14 @@ func makeStatefulSet(p monitoringv1.Prometheus, old *appsv1.StatefulSet, config
Spec: *spec,
}
if statefulset.ObjectMeta.Annotations == nil {
statefulset.ObjectMeta.Annotations = map[string]string{
sSetInputChecksumName: inputChecksum,
}
} else {
statefulset.ObjectMeta.Annotations[sSetInputChecksumName] = inputChecksum
}
if p.Spec.ImagePullSecrets != nil && len(p.Spec.ImagePullSecrets) > 0 {
statefulset.Spec.Template.Spec.ImagePullSecrets = p.Spec.ImagePullSecrets
}
@ -179,21 +188,15 @@ func makeStatefulSet(p monitoringv1.Prometheus, old *appsv1.StatefulSet, config
statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, pvcTemplate)
}
if old != nil {
statefulset.Annotations = old.Annotations
// Updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden.
statefulset.Spec.PodManagementPolicy = old.Spec.PodManagementPolicy
}
// Updates to statefulset spec for fields other than 'replicas',
// 'template', and 'updateStrategy' are forbidden.
statefulset.Spec.PodManagementPolicy = previousPodManagementPolicy
return statefulset, nil
}
func makeEmptyConfig(p *monitoringv1.Prometheus, configMaps []*v1.ConfigMap, config Config) (*v1.Secret, error) {
s, err := makeConfigSecret(p, configMaps, config)
if err != nil {
return nil, err
}
func makeEmptyConfigurationSecret(p *monitoringv1.Prometheus, config Config) (*v1.Secret, error) {
s := makeConfigSecret(p, config)
s.ObjectMeta.Annotations = map[string]string{
"empty": "true",
@ -223,50 +226,7 @@ func (l *ConfigMapReferenceList) Swap(i, j int) {
l.Items[i], l.Items[j] = l.Items[j], l.Items[i]
}
func makeRuleConfigMap(cm *v1.ConfigMap) (*ConfigMapReference, error) {
keys := []string{}
for k, _ := range cm.Data {
keys = append(keys, k)
}
sort.Strings(keys)
m := yaml.MapSlice{}
for _, k := range keys {
m = append(m, yaml.MapItem{Key: k, Value: cm.Data[k]})
}
b, err := yaml.Marshal(m)
if err != nil {
return nil, err
}
return &ConfigMapReference{
Key: cm.Namespace + "/" + cm.Name,
Checksum: fmt.Sprintf("%x", sha256.Sum256(b)),
}, nil
}
func makeRuleConfigMapListFile(configMaps []*v1.ConfigMap) ([]byte, error) {
cml := &ConfigMapReferenceList{}
for _, cm := range configMaps {
configmap, err := makeRuleConfigMap(cm)
if err != nil {
return nil, err
}
cml.Items = append(cml.Items, configmap)
}
sort.Sort(cml)
return json.Marshal(cml)
}
func makeConfigSecret(p *monitoringv1.Prometheus, configMaps []*v1.ConfigMap, config Config) (*v1.Secret, error) {
b, err := makeRuleConfigMapListFile(configMaps)
if err != nil {
return nil, err
}
func makeConfigSecret(p *monitoringv1.Prometheus, config Config) *v1.Secret {
boolTrue := true
return &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
@ -284,10 +244,9 @@ func makeConfigSecret(p *monitoringv1.Prometheus, configMaps []*v1.ConfigMap, co
},
},
Data: map[string][]byte{
configFilename: []byte{},
ruleConfigmapsFilename: b,
configFilename: []byte{},
},
}, nil
}
}
func makeStatefulSetService(p *monitoringv1.Prometheus, config Config) *v1.Service {
@ -315,7 +274,7 @@ func makeStatefulSetService(p *monitoringv1.Prometheus, config Config) *v1.Servi
return svc
}
func makeStatefulSetSpec(p monitoringv1.Prometheus, c *Config, ruleConfigMaps []*v1.ConfigMap) (*appsv1.StatefulSetSpec, error) {
func makeStatefulSetSpec(p monitoringv1.Prometheus, c *Config) (*appsv1.StatefulSetSpec, error) {
// Prometheus may take quite long to shut down to checkpoint existing data.
// Allow up to 10 minutes for clean termination.
terminationGracePeriod := int64(600)
@ -446,6 +405,16 @@ func makeStatefulSetSpec(p monitoringv1.Prometheus, c *Config, ruleConfigMaps []
EmptyDir: &v1.EmptyDirVolumeSource{},
},
},
{
Name: "rules",
VolumeSource: v1.VolumeSource{
ConfigMap: &v1.ConfigMapVolumeSource{
LocalObjectReference: v1.LocalObjectReference{
Name: prometheusRuleFilesConfigMapName(p.Name),
},
},
},
},
}
promVolumeMounts := []v1.VolumeMount{
@ -454,6 +423,10 @@ func makeStatefulSetSpec(p monitoringv1.Prometheus, c *Config, ruleConfigMaps []
ReadOnly: true,
MountPath: confOutDir,
},
{
Name: "rules",
MountPath: "/etc/prometheus/rules",
},
{
Name: volumeName(p.Name),
MountPath: storageDir,
@ -482,6 +455,10 @@ func makeStatefulSetSpec(p monitoringv1.Prometheus, c *Config, ruleConfigMaps []
Name: "config",
MountPath: confDir,
},
{
Name: "rules",
MountPath: "/etc/prometheus/rules",
},
{
Name: "config-out",
MountPath: confOutDir,
@ -491,9 +468,7 @@ func makeStatefulSetSpec(p monitoringv1.Prometheus, c *Config, ruleConfigMaps []
configReloadArgs := []string{
fmt.Sprintf("--reload-url=%s", localReloadURL),
fmt.Sprintf("--config-file=%s", path.Join(confDir, configFilename)),
fmt.Sprintf("--rule-list-file=%s", path.Join(confDir, ruleConfigmapsFilename)),
fmt.Sprintf("--config-envsubst-file=%s", path.Join(confOutDir, configEnvsubstFilename)),
fmt.Sprintf("--rule-dir=%s", rulesDir),
}
var livenessProbeHandler v1.Handler
@ -600,6 +575,7 @@ func makeStatefulSetSpec(p monitoringv1.Prometheus, c *Config, ruleConfigMaps []
},
},
},
Command: []string{"/bin/prometheus-config-reloader"},
Args: configReloadArgs,
VolumeMounts: configReloadVolumeMounts,
Resources: v1.ResourceRequirements{
@ -609,6 +585,27 @@ func makeStatefulSetSpec(p monitoringv1.Prometheus, c *Config, ruleConfigMaps []
},
},
},
{
Name: "alerting-rule-files-configmap-reloader",
Image: c.ConfigReloaderImage,
Args: []string{
fmt.Sprintf("--webhook-url=%s", localReloadURL),
fmt.Sprintf("--volume-dir=%s", "/etc/prometheus/rules"),
},
VolumeMounts: []v1.VolumeMount{
{
Name: "rules",
ReadOnly: true,
MountPath: "/etc/prometheus/rules",
},
},
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("5m"),
v1.ResourceMemory: resource.MustParse("10Mi"),
},
},
},
}, p.Spec.Containers...),
SecurityContext: securityContext,
ServiceAccountName: p.Spec.ServiceAccountName,

View file

@ -15,6 +15,7 @@
package prometheus
import (
"fmt"
"reflect"
"testing"
@ -24,6 +25,8 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/kylelemons/godebug/pretty"
)
var (
@ -45,7 +48,7 @@ func TestStatefulSetLabelingAndAnnotations(t *testing.T) {
Labels: labels,
Annotations: annotations,
},
}, nil, defaultTestConfig, []*v1.ConfigMap{})
}, "", defaultTestConfig, "")
require.NoError(t, err)
@ -69,7 +72,7 @@ func TestPodLabelsAnnotations(t *testing.T) {
Labels: labels,
},
},
}, nil, defaultTestConfig, []*v1.ConfigMap{})
}, "", defaultTestConfig, "")
require.NoError(t, err)
if _, ok := sset.Spec.Template.ObjectMeta.Labels["testlabel"]; !ok {
t.Fatal("Pod labes are not properly propagated")
@ -109,7 +112,7 @@ func TestStatefulSetPVC(t *testing.T) {
VolumeClaimTemplate: pvc,
},
},
}, nil, defaultTestConfig, []*v1.ConfigMap{})
}, "", defaultTestConfig, "")
require.NoError(t, err)
ssetPvc := sset.Spec.VolumeClaimTemplates[0]
@ -141,7 +144,7 @@ func TestStatefulSetEmptyDir(t *testing.T) {
EmptyDir: &emptyDir,
},
},
}, nil, defaultTestConfig, []*v1.ConfigMap{})
}, "", defaultTestConfig, "")
require.NoError(t, err)
ssetVolumes := sset.Spec.Template.Spec.Volumes
@ -164,7 +167,12 @@ func TestStatefulSetVolumeInitial(t *testing.T) {
MountPath: "/etc/prometheus/config_out",
SubPath: "",
}, {
Name: "prometheus--db",
Name: "rules",
ReadOnly: false,
MountPath: "/etc/prometheus/rules",
SubPath: "",
}, {
Name: "prometheus-volume-init-test-db",
ReadOnly: false,
MountPath: "/prometheus",
SubPath: "",
@ -182,7 +190,7 @@ func TestStatefulSetVolumeInitial(t *testing.T) {
Name: "config",
VolumeSource: v1.VolumeSource{
Secret: &v1.SecretVolumeSource{
SecretName: configSecretName(""),
SecretName: configSecretName("volume-init-test"),
},
},
},
@ -192,6 +200,16 @@ func TestStatefulSetVolumeInitial(t *testing.T) {
EmptyDir: &v1.EmptyDirVolumeSource{},
},
},
{
Name: "rules",
VolumeSource: v1.VolumeSource{
ConfigMap: &v1.ConfigMapVolumeSource{
LocalObjectReference: v1.LocalObjectReference{
Name: "prometheus-volume-init-test-rules",
},
},
},
},
{
Name: "secret-test-secret1",
VolumeSource: v1.VolumeSource{
@ -201,7 +219,7 @@ func TestStatefulSetVolumeInitial(t *testing.T) {
},
},
{
Name: "prometheus--db",
Name: "prometheus-volume-init-test-db",
VolumeSource: v1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{},
},
@ -213,43 +231,28 @@ func TestStatefulSetVolumeInitial(t *testing.T) {
}
sset, err := makeStatefulSet(monitoringv1.Prometheus{
ObjectMeta: metav1.ObjectMeta{
Name: "volume-init-test",
},
Spec: monitoringv1.PrometheusSpec{
Secrets: []string{
"test-secret1",
},
},
}, nil, defaultTestConfig, []*v1.ConfigMap{})
}, "", defaultTestConfig, "")
require.NoError(t, err)
if !reflect.DeepEqual(expected.Spec.Template.Spec.Volumes, sset.Spec.Template.Spec.Volumes) {
t.Fatalf("Unexpected volumes: want %v, got %v",
expected.Spec.Template.Spec.Volumes,
sset.Spec.Template.Spec.Volumes)
fmt.Println(pretty.Compare(expected.Spec.Template.Spec.Volumes, sset.Spec.Template.Spec.Volumes))
t.Fatal("expected volumes to match")
}
if !reflect.DeepEqual(expected.Spec.Template.Spec.Containers[0].VolumeMounts, sset.Spec.Template.Spec.Containers[0].VolumeMounts) {
t.Fatalf("Unexpected volume mounts: want %v, got %v",
expected.Spec.Template.Spec.Containers[0].VolumeMounts,
sset.Spec.Template.Spec.Containers[0].VolumeMounts)
}
}
func TestDeterministicRuleFileHashing(t *testing.T) {
cmr, err := makeRuleConfigMap(makeConfigMap())
if err != nil {
t.Fatal(err)
fmt.Println(pretty.Compare(expected.Spec.Template.Spec.Containers[0].VolumeMounts, sset.Spec.Template.Spec.Containers[0].VolumeMounts))
t.Fatal("expected volume mounts to match")
}
for i := 0; i < 1000; i++ {
testcmr, err := makeRuleConfigMap(makeConfigMap())
if err != nil {
t.Fatal(err)
}
if cmr.Checksum != testcmr.Checksum {
t.Fatalf("Non-deterministic rule file hash generation")
}
}
}
func TestMemoryRequestNotAdjustedWhenLimitLarger2Gi(t *testing.T) {
@ -262,7 +265,7 @@ func TestMemoryRequestNotAdjustedWhenLimitLarger2Gi(t *testing.T) {
},
},
},
}, nil, defaultTestConfig, []*v1.ConfigMap{})
}, "", defaultTestConfig, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
@ -289,7 +292,7 @@ func TestMemoryRequestAdjustedWhenOnlyLimitGiven(t *testing.T) {
},
},
},
}, nil, defaultTestConfig, []*v1.ConfigMap{})
}, "", defaultTestConfig, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
@ -311,7 +314,7 @@ func TestListenLocal(t *testing.T) {
Spec: monitoringv1.PrometheusSpec{
ListenLocal: true,
},
}, nil, defaultTestConfig, []*v1.ConfigMap{})
}, "", defaultTestConfig, "")
if err != nil {
t.Fatalf("Unexpected error while making StatefulSet: %v", err)
}
@ -339,19 +342,3 @@ func TestListenLocal(t *testing.T) {
t.Fatal("Prometheus container should have 0 ports defined")
}
}
func makeConfigMap() *v1.ConfigMap {
res := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "testcm",
Namespace: "default",
},
Data: map[string]string{},
}
res.Data["test1"] = "value 1"
res.Data["test2"] = "value 2"
res.Data["test3"] = "value 3"
return res
}

View file

@ -306,6 +306,8 @@ func TestAlertmanagerZeroDowntimeRollingDeployment(t *testing.T) {
ns := ctx.CreateNamespace(t, framework.KubeClient)
ctx.SetupPrometheusRBAC(t, ns, framework.KubeClient)
alertName := "ExampleAlert"
whReplicas := int32(1)
whdpl := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
@ -411,7 +413,7 @@ inhibit_rules:
if _, err := framework.KubeClient.CoreV1().Secrets(ns).Create(amcfg); err != nil {
t.Fatal(err)
}
if _, err := framework.MonClient.Alertmanagers(ns).Create(alertmanager); err != nil {
if _, err := framework.MonClientV1.Alertmanagers(ns).Create(alertmanager); err != nil {
t.Fatal(err)
}
if _, err := testFramework.CreateServiceAndWaitUntilReady(framework.KubeClient, ns, amsvc); err != nil {
@ -421,27 +423,12 @@ inhibit_rules:
p := framework.MakeBasicPrometheus(ns, "test", "test", 3)
p.Spec.EvaluationInterval = "100ms"
framework.AddAlertingToPrometheus(p, ns, alertmanager.Name)
alertRule := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("prometheus-%s-rules", p.Name),
Labels: map[string]string{
"role": "rulefile",
},
},
Data: map[string]string{
"alerting.rules": `
groups:
- name: ./alerting.rules
rules:
- alert: ExampleAlert
expr: vector(1)
`,
},
}
if _, err := framework.KubeClient.CoreV1().ConfigMaps(ns).Create(alertRule); err != nil {
_, err = framework.MakeAndCreateFiringRuleFile(ns, p.Name, alertName)
if err != nil {
t.Fatal(err)
}
if err := framework.CreatePrometheusAndWaitUntilReady(ns, p); err != nil {
t.Fatal(err)
}
@ -456,7 +443,7 @@ groups:
// The Prometheus config reloader reloads Prometheus periodically, not on
// alert rule change. Thereby one has to wait for Prometheus actually firing
// the alert.
err = framework.WaitForPrometheusFiringAlert(p.Namespace, pSVC.Name, "ExampleAlert")
err = framework.WaitForPrometheusFiringAlert(p.Namespace, pSVC.Name, alertName)
if err != nil {
t.Fatal(err)
}
@ -490,7 +477,7 @@ groups:
}
alertmanager.Spec.Version = "v0.14.0"
if _, err := framework.MonClient.Alertmanagers(ns).Update(alertmanager); err != nil {
if _, err := framework.MonClientV1.Alertmanagers(ns).Update(alertmanager); err != nil {
t.Fatal(err)
}

View file

@ -47,19 +47,19 @@ func TestMain(m *testing.M) {
os.Exit(1)
}
err = k8sutil.WaitForCRDReady(framework.MonClient.Prometheuses(v1.NamespaceAll).List)
err = k8sutil.WaitForCRDReady(framework.MonClientV1.Prometheuses(v1.NamespaceAll).List)
if err != nil {
log.Printf("Prometheus CRD not ready: %v\n", err)
os.Exit(1)
}
err = k8sutil.WaitForCRDReady(framework.MonClient.ServiceMonitors(v1.NamespaceAll).List)
err = k8sutil.WaitForCRDReady(framework.MonClientV1.ServiceMonitors(v1.NamespaceAll).List)
if err != nil {
log.Printf("ServiceMonitor CRD not ready: %v\n", err)
os.Exit(1)
}
err = k8sutil.WaitForCRDReady(framework.MonClient.Alertmanagers(v1.NamespaceAll).List)
err = k8sutil.WaitForCRDReady(framework.MonClientV1.Alertmanagers(v1.NamespaceAll).List)
if err != nil {
log.Printf("Alertmanagers CRD not ready: %v\n", err)
os.Exit(1)

View file

@ -16,6 +16,7 @@ package e2e
import (
"bytes"
"context"
"encoding/json"
"fmt"
"log"
@ -26,6 +27,7 @@ import (
"time"
"k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
@ -35,6 +37,8 @@ import (
monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
"github.com/coreos/prometheus-operator/pkg/prometheus"
testFramework "github.com/coreos/prometheus-operator/test/framework"
"github.com/kylelemons/godebug/pretty"
"github.com/pkg/errors"
)
@ -48,10 +52,10 @@ func TestPrometheusCreateDeleteCluster(t *testing.T) {
name := "test"
prometheusTPR := framework.MakeBasicPrometheus(ns, name, name, 1)
prometheusTPR.Namespace = ns
prometheusCRD := framework.MakeBasicPrometheus(ns, name, name, 1)
prometheusCRD.Namespace = ns
if err := framework.CreatePrometheusAndWaitUntilReady(ns, prometheusTPR); err != nil {
if err := framework.CreatePrometheusAndWaitUntilReady(ns, prometheusCRD); err != nil {
t.Fatal(err)
}
@ -148,7 +152,7 @@ func TestPrometheusResourceUpdate(t *testing.T) {
v1.ResourceMemory: resource.MustParse("200Mi"),
},
}
_, err = framework.MonClient.Prometheuses(ns).Update(p)
_, err = framework.MonClientV1.Prometheuses(ns).Update(p)
if err != nil {
t.Fatal(err)
}
@ -177,6 +181,8 @@ func TestPrometheusResourceUpdate(t *testing.T) {
}
func TestPrometheusReloadConfig(t *testing.T) {
t.Parallel()
ctx := framework.NewTestCtx(t)
defer ctx.Cleanup(t)
ns := ctx.CreateNamespace(t, framework.KubeClient)
@ -269,6 +275,8 @@ scrape_configs:
}
func TestPrometheusAdditionalScrapeConfig(t *testing.T) {
t.Parallel()
ctx := framework.NewTestCtx(t)
defer ctx.Cleanup(t)
ns := ctx.CreateNamespace(t, framework.KubeClient)
@ -279,7 +287,7 @@ func TestPrometheusAdditionalScrapeConfig(t *testing.T) {
svc := framework.MakePrometheusService(prometheusName, group, v1.ServiceTypeClusterIP)
s := framework.MakeBasicServiceMonitor(group)
if _, err := framework.MonClient.ServiceMonitors(ns).Create(s); err != nil {
if _, err := framework.MonClientV1.ServiceMonitors(ns).Create(s); err != nil {
t.Fatal("Creating ServiceMonitor failed: ", err)
}
@ -335,7 +343,7 @@ func TestPrometheusAdditionalAlertManagerConfig(t *testing.T) {
svc := framework.MakePrometheusService(prometheusName, group, v1.ServiceTypeClusterIP)
s := framework.MakeBasicServiceMonitor(group)
if _, err := framework.MonClient.ServiceMonitors(ns).Create(s); err != nil {
if _, err := framework.MonClientV1.ServiceMonitors(ns).Create(s); err != nil {
t.Fatal("Creating ServiceMonitor failed: ", err)
}
@ -412,25 +420,16 @@ func TestPrometheusReloadRules(t *testing.T) {
ctx.SetupPrometheusRBAC(t, ns, framework.KubeClient)
name := "test"
firtAlertName := "firstAlert"
secondAlertName := "secondAlert"
ruleFileConfigMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("prometheus-%s-rules", name),
Labels: map[string]string{
"role": "rulefile",
},
},
Data: map[string]string{
"alerting.rules": "",
},
}
_, err := framework.KubeClient.CoreV1().ConfigMaps(ns).Create(ruleFileConfigMap)
ruleFile, err := framework.MakeAndCreateFiringRuleFile(ns, name, firtAlertName)
if err != nil {
t.Fatal(err)
}
p := framework.MakeBasicPrometheus(ns, name, name, 1)
p.Spec.EvaluationInterval = "1s"
if err := framework.CreatePrometheusAndWaitUntilReady(ns, p); err != nil {
t.Fatal(err)
}
@ -442,25 +441,354 @@ func TestPrometheusReloadRules(t *testing.T) {
ctx.AddFinalizerFn(finalizerFn)
}
ruleFileConfigMap.Data["alerting.rules"] = `
groups:
- name: ./alerting.rules
rules:
- alert: ExampleAlert
expr: vector(1)
`
_, err = framework.KubeClient.CoreV1().ConfigMaps(ns).Update(ruleFileConfigMap)
err = framework.WaitForPrometheusFiringAlert(p.Namespace, pSVC.Name, firtAlertName)
if err != nil {
t.Fatal(err)
}
err = framework.WaitForPrometheusFiringAlert(p.Namespace, pSVC.Name, "ExampleAlert")
ruleFile.Spec.Groups = []monitoringv1.RuleGroup{
monitoringv1.RuleGroup{
Name: "my-alerting-group",
Rules: []monitoringv1.Rule{
monitoringv1.Rule{
Alert: secondAlertName,
Expr: "vector(1)",
},
},
},
}
err = framework.UpdateRuleFile(ns, ruleFile)
if err != nil {
t.Fatal(err)
}
err = framework.WaitForPrometheusFiringAlert(p.Namespace, pSVC.Name, secondAlertName)
if err != nil {
t.Fatal(err)
}
}
// With Prometheus Operator v0.20.0 the 'RuleSelector' field in the Prometheus
// CRD Spec is deprecated. We need to ensure to still support it until the field
// is removed. Any value in 'RuleSelector' should just be copied to the new
// field 'RuleFileSelector'.
func TestPrometheusDeprecatedRuleSelectorField(t *testing.T) {
t.Parallel()
ctx := framework.NewTestCtx(t)
defer ctx.Cleanup(t)
ns := ctx.CreateNamespace(t, framework.KubeClient)
ctx.SetupPrometheusRBAC(t, ns, framework.KubeClient)
name := "test"
firtAlertName := "firstAlert"
_, err := framework.MakeAndCreateFiringRuleFile(ns, name, firtAlertName)
if err != nil {
t.Fatal(err)
}
p := framework.MakeBasicPrometheus(ns, name, name, 1)
p.Spec.EvaluationInterval = "1s"
// Reset new 'RuleFileSelector' field
p.Spec.RuleFileSelector = nil
// Specify old 'RuleFile' field
p.Spec.RuleSelector = &metav1.LabelSelector{
MatchLabels: map[string]string{
"role": "rulefile",
},
}
if err := framework.CreatePrometheusAndWaitUntilReady(ns, p); err != nil {
t.Fatal(err)
}
pSVC := framework.MakePrometheusService(p.Name, "not-relevant", v1.ServiceTypeClusterIP)
if finalizerFn, err := testFramework.CreateServiceAndWaitUntilReady(framework.KubeClient, ns, pSVC); err != nil {
t.Fatal(errors.Wrap(err, "creating Prometheus service failed"))
} else {
ctx.AddFinalizerFn(finalizerFn)
}
err = framework.WaitForPrometheusFiringAlert(p.Namespace, pSVC.Name, firtAlertName)
if err != nil {
t.Fatal(err)
}
}
func TestPrometheusMultipleRuleFilesSameNS(t *testing.T) {
t.Parallel()
ctx := framework.NewTestCtx(t)
defer ctx.Cleanup(t)
ns := ctx.CreateNamespace(t, framework.KubeClient)
ctx.SetupPrometheusRBAC(t, ns, framework.KubeClient)
name := "test"
alertNames := []string{"first-alert", "second-alert"}
for _, alertName := range alertNames {
_, err := framework.MakeAndCreateFiringRuleFile(ns, alertName, alertName)
if err != nil {
t.Fatal(err)
}
}
p := framework.MakeBasicPrometheus(ns, name, name, 1)
p.Spec.EvaluationInterval = "1s"
if err := framework.CreatePrometheusAndWaitUntilReady(ns, p); err != nil {
t.Fatal(err)
}
pSVC := framework.MakePrometheusService(p.Name, "not-relevant", v1.ServiceTypeClusterIP)
if finalizerFn, err := testFramework.CreateServiceAndWaitUntilReady(framework.KubeClient, ns, pSVC); err != nil {
t.Fatal(errors.Wrap(err, "creating Prometheus service failed"))
} else {
ctx.AddFinalizerFn(finalizerFn)
}
for _, alertName := range alertNames {
err := framework.WaitForPrometheusFiringAlert(p.Namespace, pSVC.Name, alertName)
if err != nil {
t.Fatal(err)
}
}
}
func TestPrometheusMultipleRuleFilesDifferentNS(t *testing.T) {
t.Parallel()
ctx := framework.NewTestCtx(t)
defer ctx.Cleanup(t)
rootNS := ctx.CreateNamespace(t, framework.KubeClient)
alertNSOne := ctx.CreateNamespace(t, framework.KubeClient)
alertNSTwo := ctx.CreateNamespace(t, framework.KubeClient)
ctx.SetupPrometheusRBAC(t, rootNS, framework.KubeClient)
name := "test"
ruleFiles := []struct {
alertName string
ns string
}{{"first-alert", alertNSOne}, {"second-alert", alertNSTwo}}
ruleFilesNamespaceSelector := map[string]string{"prometheus": rootNS}
for _, file := range ruleFiles {
testFramework.AddLabelsToNamespace(framework.KubeClient, file.ns, ruleFilesNamespaceSelector)
}
for _, file := range ruleFiles {
_, err := framework.MakeAndCreateFiringRuleFile(file.ns, file.alertName, file.alertName)
if err != nil {
t.Fatal(err)
}
}
p := framework.MakeBasicPrometheus(rootNS, name, name, 1)
p.Spec.EvaluationInterval = "1s"
p.Spec.RuleFileNamespaceSelector = &metav1.LabelSelector{
MatchLabels: ruleFilesNamespaceSelector,
}
if err := framework.CreatePrometheusAndWaitUntilReady(rootNS, p); err != nil {
t.Fatal(err)
}
pSVC := framework.MakePrometheusService(p.Name, "not-relevant", v1.ServiceTypeClusterIP)
if finalizerFn, err := testFramework.CreateServiceAndWaitUntilReady(framework.KubeClient, rootNS, pSVC); err != nil {
t.Fatal(errors.Wrap(err, "creating Prometheus service failed"))
} else {
ctx.AddFinalizerFn(finalizerFn)
}
for _, file := range ruleFiles {
err := framework.WaitForPrometheusFiringAlert(p.Namespace, pSVC.Name, file.alertName)
if err != nil {
t.Fatal(err)
}
}
}
// Make sure the Prometheus operator only updates the Prometheus config secret
// and the Prometheus rules configmap on relevant changes
func TestPrometheusOnlyUpdatedOnRelevantChanges(t *testing.T) {
t.Parallel()
testCTX := framework.NewTestCtx(t)
defer testCTX.Cleanup(t)
ns := testCTX.CreateNamespace(t, framework.KubeClient)
testCTX.SetupPrometheusRBAC(t, ns, framework.KubeClient)
name := "test"
prometheus := framework.MakeBasicPrometheus(ns, name, name, 1)
ctx, cancel := context.WithCancel(context.Background())
type versionedResource interface {
GetResourceVersion() string
}
// TODO: rename resouceDefinitions
resourceDefinitions := []struct {
Name string
Getter func(prometheusName string) (versionedResource, error)
Versions map[string]interface{}
ExpectedChanges int
}{
{
Name: "crd",
Getter: func(prometheusName string) (versionedResource, error) {
return framework.
MonClientV1.
Prometheuses(ns).
Get(prometheusName, metav1.GetOptions{})
},
ExpectedChanges: 1,
},
{
Name: "rulesConfigMap",
Getter: func(prometheusName string) (versionedResource, error) {
return framework.
KubeClient.
CoreV1().
ConfigMaps(ns).
Get("prometheus-"+prometheusName+"-rules", metav1.GetOptions{})
},
ExpectedChanges: 1,
},
{
Name: "configurationSecret",
Getter: func(prometheusName string) (versionedResource, error) {
return framework.
KubeClient.
CoreV1().
Secrets(ns).
Get("prometheus-"+prometheusName, metav1.GetOptions{})
},
ExpectedChanges: 1,
},
{
Name: "statefulset",
Getter: func(prometheusName string) (versionedResource, error) {
return framework.
KubeClient.
AppsV1().
StatefulSets(ns).
Get("prometheus-"+prometheusName, metav1.GetOptions{})
},
// First is the creation of the StatefulSet itself, second is the
// update of the ReadyReplicas status field
ExpectedChanges: 2,
},
{
Name: "service",
Getter: func(prometheusName string) (versionedResource, error) {
return framework.
KubeClient.
CoreV1().
Services(ns).
Get("prometheus-operated", metav1.GetOptions{})
},
ExpectedChanges: 1,
},
}
// Init Versions maps
for i, _ := range resourceDefinitions {
resourceDefinitions[i].Versions = map[string]interface{}{}
}
go func() {
for {
select {
case <-ctx.Done():
return
default:
time.Sleep(10 * time.Millisecond)
for i, resourceDef := range resourceDefinitions {
resource, err := resourceDef.Getter(prometheus.Name)
if apierrors.IsNotFound(err) {
continue
}
if err != nil {
cancel()
t.Fatal(err)
}
resourceDefinitions[i].Versions[resource.GetResourceVersion()] = resource
}
}
}
}()
if err := framework.CreatePrometheusAndWaitUntilReady(ns, prometheus); err != nil {
t.Fatal(err)
}
if err := framework.DeletePrometheusAndWaitUntilGone(ns, name); err != nil {
t.Fatal(err)
}
cancel()
for _, resource := range resourceDefinitions {
if len(resource.Versions) != resource.ExpectedChanges {
var previous interface{}
for _, version := range resource.Versions {
if previous == nil {
previous = version
continue
}
fmt.Println(pretty.Compare(previous, version))
previous = version
}
t.Fatalf(
"expected resource %v to be created/updated %v times, but saw %v instead",
resource.Name,
resource.ExpectedChanges,
len(resource.Versions),
)
}
}
}
func TestPrometheusWhenDeleteCRDCleanUpViaOwnerReference(t *testing.T) {
t.Parallel()
ctx := framework.NewTestCtx(t)
defer ctx.Cleanup(t)
ns := ctx.CreateNamespace(t, framework.KubeClient)
ctx.SetupPrometheusRBAC(t, ns, framework.KubeClient)
name := "test"
p := framework.MakeBasicPrometheus(ns, name, name, 1)
if err := framework.CreatePrometheusAndWaitUntilReady(ns, p); err != nil {
t.Fatal(err)
}
configMapName := fmt.Sprintf("prometheus-%v-rules", p.Name)
_, err := framework.WaitForConfigMapExist(ns, configMapName)
if err != nil {
t.Fatal(err)
}
// Waits for Prometheus pods to vanish
err = framework.DeletePrometheusAndWaitUntilGone(ns, p.Name)
if err != nil {
t.Fatal(err)
}
err = framework.WaitForConfigMapNotExist(ns, configMapName)
if err != nil {
t.Fatal(err)
}
}
func TestPrometheusDiscovery(t *testing.T) {
t.Parallel()
ctx := framework.NewTestCtx(t)
defer ctx.Cleanup(t)
ns := ctx.CreateNamespace(t, framework.KubeClient)
@ -471,7 +799,7 @@ func TestPrometheusDiscovery(t *testing.T) {
svc := framework.MakePrometheusService(prometheusName, group, v1.ServiceTypeClusterIP)
s := framework.MakeBasicServiceMonitor(group)
if _, err := framework.MonClient.ServiceMonitors(ns).Create(s); err != nil {
if _, err := framework.MonClientV1.ServiceMonitors(ns).Create(s); err != nil {
t.Fatal("Creating ServiceMonitor failed: ", err)
}
@ -499,6 +827,8 @@ func TestPrometheusDiscovery(t *testing.T) {
}
func TestPrometheusAlertmanagerDiscovery(t *testing.T) {
t.Parallel()
ctx := framework.NewTestCtx(t)
defer ctx.Cleanup(t)
ns := ctx.CreateNamespace(t, framework.KubeClient)
@ -524,7 +854,7 @@ func TestPrometheusAlertmanagerDiscovery(t *testing.T) {
}
s := framework.MakeBasicServiceMonitor(group)
if _, err := framework.MonClient.ServiceMonitors(ns).Create(s); err != nil {
if _, err := framework.MonClientV1.ServiceMonitors(ns).Create(s); err != nil {
t.Fatalf("Creating ServiceMonitor failed: %v", err)
}
@ -586,7 +916,7 @@ func TestPrometheusDiscoverTargetPort(t *testing.T) {
group := "servicediscovery-test"
svc := framework.MakePrometheusService(prometheusName, group, v1.ServiceTypeClusterIP)
if _, err := framework.MonClient.ServiceMonitors(ns).Create(&monitoringv1.ServiceMonitor{
if _, err := framework.MonClientV1.ServiceMonitors(ns).Create(&monitoringv1.ServiceMonitor{
ObjectMeta: metav1.ObjectMeta{
Name: prometheusName,
Labels: map[string]string{
@ -633,6 +963,8 @@ func TestPrometheusDiscoverTargetPort(t *testing.T) {
}
func TestPromOpMatchPromAndServMonInDiffNSs(t *testing.T) {
t.Parallel()
ctx := framework.NewTestCtx(t)
defer ctx.Cleanup(t)
prometheusNSName := ctx.CreateNamespace(t, framework.KubeClient)
@ -656,7 +988,7 @@ func TestPromOpMatchPromAndServMonInDiffNSs(t *testing.T) {
s := framework.MakeBasicServiceMonitor(group)
if _, err := framework.MonClient.ServiceMonitors(serviceMonitorNSName).Create(s); err != nil {
if _, err := framework.MonClientV1.ServiceMonitors(serviceMonitorNSName).Create(s); err != nil {
t.Fatal("Creating ServiceMonitor failed: ", err)
}

View file

@ -133,7 +133,7 @@ func (f *Framework) CreateAlertmanagerAndWaitUntilReady(ns string, a *monitoring
return errors.Wrap(err, fmt.Sprintf("creating alertmanager config secret %v failed", s.Name))
}
_, err = f.MonClient.Alertmanagers(ns).Create(a)
_, err = f.MonClientV1.Alertmanagers(ns).Create(a)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("creating alertmanager %v failed", a.Name))
}
@ -154,7 +154,7 @@ func (f *Framework) WaitForAlertmanagerReady(ns, name string, replicas int) erro
}
func (f *Framework) UpdateAlertmanagerAndWaitUntilReady(ns string, a *monitoringv1.Alertmanager) error {
_, err := f.MonClient.Alertmanagers(ns).Update(a)
_, err := f.MonClientV1.Alertmanagers(ns).Update(a)
if err != nil {
return err
}
@ -174,12 +174,12 @@ func (f *Framework) UpdateAlertmanagerAndWaitUntilReady(ns string, a *monitoring
}
func (f *Framework) DeleteAlertmanagerAndWaitUntilGone(ns, name string) error {
_, err := f.MonClient.Alertmanagers(ns).Get(name, metav1.GetOptions{})
_, err := f.MonClientV1.Alertmanagers(ns).Get(name, metav1.GetOptions{})
if err != nil {
return errors.Wrap(err, fmt.Sprintf("requesting Alertmanager tpr %v failed", name))
}
if err := f.MonClient.Alertmanagers(ns).Delete(name, nil); err != nil {
if err := f.MonClientV1.Alertmanagers(ns).Delete(name, nil); err != nil {
return errors.Wrap(err, fmt.Sprintf("deleting Alertmanager tpr %v failed", name))
}

View file

@ -0,0 +1,67 @@
// Copyright 2016 The prometheus-operator 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 framework
import (
"time"
"k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
)
func (f *Framework) WaitForConfigMapExist(ns, name string) (*v1.ConfigMap, error) {
var configMap *v1.ConfigMap
err := wait.Poll(2*time.Second, f.DefaultTimeout, func() (bool, error) {
var err error
configMap, err = f.
KubeClient.
CoreV1().
ConfigMaps(ns).
Get(name, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return false, nil
}
if err != nil {
return false, err
}
return true, nil
})
return configMap, err
}
func (f *Framework) WaitForConfigMapNotExist(ns, name string) error {
err := wait.Poll(2*time.Second, f.DefaultTimeout, func() (bool, error) {
var err error
_, err = f.
KubeClient.
CoreV1().
ConfigMaps(ns).
Get(name, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return true, nil
}
if err != nil {
return false, err
}
return false, nil
})
return err
}

View file

@ -16,6 +16,7 @@ package framework
import (
"net/http"
"strings"
"testing"
"time"
@ -28,18 +29,20 @@ import (
"k8s.io/client-go/tools/clientcmd"
monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
monitoringv1alpha1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1alpha1"
"github.com/coreos/prometheus-operator/pkg/k8sutil"
"github.com/pkg/errors"
)
type Framework struct {
KubeClient kubernetes.Interface
MonClient monitoringv1.MonitoringV1Interface
HTTPClient *http.Client
MasterHost string
Namespace *v1.Namespace
OperatorPod *v1.Pod
DefaultTimeout time.Duration
KubeClient kubernetes.Interface
MonClientV1 monitoringv1.MonitoringV1Interface
MonClientV1alpha1 monitoringv1alpha1.MonitoringV1alpha1Interface
HTTPClient *http.Client
MasterHost string
Namespace *v1.Namespace
OperatorPod *v1.Pod
DefaultTimeout time.Duration
}
// Setup setups a test framework and returns it.
@ -59,9 +62,14 @@ func New(ns, kubeconfig, opImage string) (*Framework, error) {
return nil, errors.Wrap(err, "creating http-client failed")
}
mclient, err := monitoringv1.NewForConfig(&monitoringv1.DefaultCrdKinds, monitoringv1.Group, config)
mClientV1, err := monitoringv1.NewForConfig(&monitoringv1.DefaultCrdKinds, monitoringv1.Group, config)
if err != nil {
return nil, errors.Wrap(err, "creating monitoring client failed")
return nil, errors.Wrap(err, "creating v1 monitoring client failed")
}
mClientV1alpha1, err := monitoringv1alpha1.NewForConfig(config)
if err != nil {
return nil, errors.Wrap(err, "creating v1alpha1 monitoring client failed")
}
namespace, err := CreateNamespace(cli, ns)
@ -70,12 +78,13 @@ func New(ns, kubeconfig, opImage string) (*Framework, error) {
}
f := &Framework{
MasterHost: config.Host,
KubeClient: cli,
MonClient: mclient,
HTTPClient: httpc,
Namespace: namespace,
DefaultTimeout: time.Minute,
MasterHost: config.Host,
KubeClient: cli,
MonClientV1: mClientV1,
MonClientV1alpha1: mClientV1alpha1,
HTTPClient: httpc,
Namespace: namespace,
DefaultTimeout: time.Minute,
}
err = f.Setup(opImage)
@ -119,8 +128,21 @@ func (f *Framework) setupPrometheusOperator(opImage string) error {
if opImage != "" {
// Override operator image used, if specified when running tests.
deploy.Spec.Template.Spec.Containers[0].Image = opImage
repoAndTag := strings.Split(opImage, ":")
if len(repoAndTag) != 2 {
return errors.Errorf(
"expected operator image '%v' split by colon to result in two substrings but got '%v'",
opImage,
repoAndTag,
)
}
deploy.Spec.Template.Spec.Containers[0].Args[1] = "--prometheus-config-reloader=" +
"quay.io/coreos/prometheus-config-reloader:" +
repoAndTag[1]
}
deploy.Spec.Template.Spec.Containers[0].Args = append(deploy.Spec.Template.Spec.Containers[0].Args, "--log-level=all")
err = CreateDeployment(f.KubeClient, f.Namespace.Name, deploy)
if err != nil {
return err

View file

@ -48,36 +48,7 @@ func (f *Framework) MakeBasicPrometheus(ns, name, group string, replicas int32)
},
},
ServiceAccountName: "prometheus",
RuleSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"role": "rulefile",
},
},
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceMemory: resource.MustParse("400Mi"),
},
},
},
}
}
func (f *Framework) MakeBasicPrometheusV1alpha1(ns, name, group string, replicas int32) *v1alpha1.Prometheus {
return &v1alpha1.Prometheus{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: v1alpha1.PrometheusSpec{
Replicas: &replicas,
Version: prometheus.DefaultVersion,
ServiceMonitorSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"group": group,
},
},
ServiceAccountName: "prometheus",
RuleSelector: &metav1.LabelSelector{
RuleFileSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"role": "rulefile",
},
@ -177,20 +148,20 @@ func (f *Framework) MakePrometheusService(name, group string, serviceType v1.Ser
}
func (f *Framework) CreatePrometheusAndWaitUntilReady(ns string, p *monitoringv1.Prometheus) error {
_, err := f.MonClient.Prometheuses(ns).Create(p)
_, err := f.MonClientV1.Prometheuses(ns).Create(p)
if err != nil {
return fmt.Errorf("creating %d Prometheus instances failed (%v): %v", p.Spec.Replicas, p.Name, err)
return fmt.Errorf("creating %v Prometheus instances failed (%v): %v", p.Spec.Replicas, p.Name, err)
}
if err := f.WaitForPrometheusReady(p, 5*time.Minute); err != nil {
return fmt.Errorf("waiting for %d Prometheus instances timed out (%v): %v", p.Spec.Replicas, p.Name, err)
return fmt.Errorf("waiting for %v Prometheus instances timed out (%v): %v", p.Spec.Replicas, p.Name, err)
}
return nil
}
func (f *Framework) UpdatePrometheusAndWaitUntilReady(ns string, p *monitoringv1.Prometheus) error {
_, err := f.MonClient.Prometheuses(ns).Update(p)
_, err := f.MonClientV1.Prometheuses(ns).Update(p)
if err != nil {
return err
}
@ -202,7 +173,7 @@ func (f *Framework) UpdatePrometheusAndWaitUntilReady(ns string, p *monitoringv1
}
func (f *Framework) WaitForPrometheusReady(p *monitoringv1.Prometheus, timeout time.Duration) error {
return wait.Poll(2*time.Second, timeout, func() (bool, error) {
err := wait.Poll(2*time.Second, timeout, func() (bool, error) {
st, _, err := prometheus.PrometheusStatus(f.KubeClient, p)
if err != nil {
log.Print(err)
@ -211,19 +182,19 @@ func (f *Framework) WaitForPrometheusReady(p *monitoringv1.Prometheus, timeout t
if st.UpdatedReplicas == *p.Spec.Replicas {
return true, nil
} else {
log.Printf("expected %v Prometheus instances, got %v", *p.Spec.Replicas, st.UpdatedReplicas)
return false, nil
}
})
return errors.Wrapf(err, "waiting for Prometheus %v/%v", p.Namespace, p.Name)
}
func (f *Framework) DeletePrometheusAndWaitUntilGone(ns, name string) error {
_, err := f.MonClient.Prometheuses(ns).Get(name, metav1.GetOptions{})
_, err := f.MonClientV1.Prometheuses(ns).Get(name, metav1.GetOptions{})
if err != nil {
return errors.Wrap(err, fmt.Sprintf("requesting Prometheus tpr %v failed", name))
}
if err := f.MonClient.Prometheuses(ns).Delete(name, nil); err != nil {
if err := f.MonClientV1.Prometheuses(ns).Delete(name, nil); err != nil {
return errors.Wrap(err, fmt.Sprintf("deleting Prometheus tpr %v failed", name))
}
@ -337,7 +308,12 @@ func (f *Framework) WaitForPrometheusFiringAlert(ns, svcName, alertName string)
})
if err != nil {
return errors.Wrap(loopError, err.Error())
return errors.Errorf(
"waiting for alert '%v' to fire: %v: %v",
alertName,
err.Error(),
loopError.Error(),
)
}
return nil
}

View file

@ -0,0 +1,77 @@
// Copyright 2016 The prometheus-operator 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 framework
import (
"fmt"
monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func (f *Framework) MakeBasicRuleFile(ns, name string, groups []monitoringv1.RuleGroup) monitoringv1.RuleFile {
return monitoringv1.RuleFile{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
Labels: map[string]string{
"role": "rulefile",
},
},
Spec: monitoringv1.RuleFileSpec{
Groups: groups,
},
}
}
func (f *Framework) CreateRuleFile(ns string, ar monitoringv1.RuleFile) error {
_, err := f.MonClientV1.RuleFiles(ns).Create(&ar)
if err != nil {
return fmt.Errorf("creating %v RuleFile failed: %v", ar.Name, err)
}
return nil
}
func (f *Framework) MakeAndCreateFiringRuleFile(ns, name, alertName string) (monitoringv1.RuleFile, error) {
groups := []monitoringv1.RuleGroup{
monitoringv1.RuleGroup{
Name: alertName,
Rules: []monitoringv1.Rule{
monitoringv1.Rule{
Alert: alertName,
Expr: "vector(1)",
},
},
},
}
file := f.MakeBasicRuleFile(ns, name, groups)
err := f.CreateRuleFile(ns, file)
if err != nil {
return file, err
}
return file, nil
}
func (f *Framework) UpdateRuleFile(ns string, ar monitoringv1.RuleFile) error {
_, err := f.MonClientV1.RuleFiles(ns).Update(&ar)
if err != nil {
return fmt.Errorf("updating %v RuleFile failed: %v", ar.Name, err)
}
return nil
}

View file

@ -1 +0,0 @@
assets

View file

@ -1,21 +0,0 @@
language: go
go:
- 1.7.5
- 1.8
env:
# Maybe run minikube later?
- K8S_CLIENT_TEST=0
install:
- go get -v ./...
- go get -v github.com/ghodss/yaml # Required for examples.
script:
- make test
- make test-examples
notifications:
email: false

View file

@ -1,7 +0,0 @@
test:
go test -v ./...
test-examples:
@for example in $(shell find examples/ -name '*.go'); do \
go build -v $$example || exit 1; \
done

View file

@ -1,182 +0,0 @@
# A simple Go client for Kubernetes
[![GoDoc](https://godoc.org/github.com/ericchiang/k8s?status.svg)](https://godoc.org/github.com/ericchiang/k8s)
A slimmed down Go client generated using Kubernetes' new [protocol buffer][protobuf] support. This package behaves similarly to [official Kubernetes' Go client][client-go], but only imports two external dependencies.
```go
package main
import (
"context"
"fmt"
"log"
"github.com/ericchiang/k8s"
)
func main() {
client, err := k8s.NewInClusterClient()
if err != nil {
log.Fatal(err)
}
nodes, err := client.CoreV1().ListNodes(context.Background())
if err != nil {
log.Fatal(err)
}
for _, node := range nodes.Items {
fmt.Printf("name=%q schedulable=%t\n", *node.Metadata.Name, !*node.Spec.Unschedulable)
}
}
```
## Requirements
* Go 1.7+ (this package uses "context" features added in 1.7)
* Kubernetes 1.3+ (protobuf support was added in 1.3)
* [github.com/golang/protobuf/proto][go-proto] (protobuf serialization)
* [golang.org/x/net/http2][go-http2] (HTTP/2 support)
## Versioned supported
This client supports every API group version present since 1.3.
## Usage
### Namespace
When performing a list or watch operation, the namespace to list or watch in is provided as an argument.
```go
pods, err := core.ListPods(ctx, "custom-namespace") // Pods from the "custom-namespace"
```
A special value `AllNamespaces` indicates that the list or watch should be performed on all cluster resources.
```go
pods, err := core.ListPods(ctx, k8s.AllNamespaces) // Pods in all namespaces.
```
Both in-cluster and out-of-cluster clients are initialized with a primary namespace. This is the recommended value to use when listing or watching.
```go
client, err := k8s.NewInClusterClient()
if err != nil {
// handle error
}
// List pods in the namespace the client is running in.
pods, err := client.CoreV1().ListPods(ctx, client.Namespace)
```
### Label selectors
Label selectors can be provided to any list operation.
```go
l := new(k8s.LabelSelector)
l.Eq("tier", "production")
l.In("app", "database", "frontend")
pods, err := client.CoreV1().ListPods(ctx, client.Namespace, l.Selector())
```
### Working with resources
Use the generated API types directly to create and modify resources.
```go
import (
"context"
"github.com/ericchiang/k8s"
"github.com/ericchiang/k8s/api/v1"
metav1 "github.com/ericchiang/k8s/apis/meta/v1"
)
func createConfigMap(client *k8s.Client, name string, values map[string]string) error {
cm := &v1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: &name,
Namespace: &client.Namespace,
},
Data: values,
}
// Will return the created configmap as well.
_, err := client.CoreV1().CreateConfigMap(context.TODO(), cm)
return err
}
```
API structs use pointers to `int`, `bool`, and `string` types to differentiate between the zero value and an unsupplied one. This package provides [convenience methods][string] for creating pointers to literals of basic types.
### Creating out-of-cluster clients
Out-of-cluster clients can be constructed by either creating an `http.Client` manually or parsing a [`Config`][config] object. The following is an example of creating a client from a kubeconfig:
```go
import (
"io/ioutil"
"github.com/ericchiang/k8s"
"github.com/ghodss/yaml"
)
// loadClient parses a kubeconfig from a file and returns a Kubernetes
// client. It does not support extensions or client auth providers.
func loadClient(kubeconfigPath string) (*k8s.Client, error) {
data, err := ioutil.ReadFile(kubeconfigPath)
if err != nil {
return nil, fmt.Errorf("read kubeconfig: %v", err)
}
// Unmarshal YAML into a Kubernetes config object.
var config k8s.Config
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("unmarshal kubeconfig: %v", err)
}
return k8s.NewClient(&config)
}
```
### Errors
Errors returned by the Kubernetes API are formatted as [`unversioned.Status`][unversioned-status] objects and surfaced by clients as [`*k8s.APIError`][k8s-error]s. Programs that need to inspect error codes or failure details can use a type cast to access this information.
```go
// createConfigMap creates a configmap in the client's default namespace
// but does not return an error if a configmap of the same name already
// exists.
func createConfigMap(client *k8s.Client, name string, values map[string]string) error {
cm := &v1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: &name,
Namespace: &client.Namespace,
},
Data: values,
}
_, err := client.CoreV1().CreateConfigMap(context.TODO(), cm)
// If an HTTP error was returned by the API server, it will be of type
// *k8s.APIError. This can be used to inspect the status code.
if apiErr, ok := err.(*k8s.APIError); ok {
// Resource already exists. Carry on.
if apiErr.Code == http.StatusConflict {
return nil
}
}
return fmt.Errorf("create configmap: %v", err)
}
```
[client-go]: https://github.com/kubernetes/client-go
[go-proto]: https://godoc.org/github.com/golang/protobuf/proto
[go-http2]: https://godoc.org/golang.org/x/net/http2
[protobuf]: https://developers.google.com/protocol-buffers/
[unversioned-status]: https://godoc.org/github.com/ericchiang/k8s/api/unversioned#Status
[k8s-error]: https://godoc.org/github.com/ericchiang/k8s#APIError
[config]: https://godoc.org/github.com/ericchiang/k8s#Config
[string]: https://godoc.org/github.com/ericchiang/k8s#String

View file

@ -1,399 +0,0 @@
// Code generated by protoc-gen-gogo.
// source: k8s.io/kubernetes/pkg/api/resource/generated.proto
// DO NOT EDIT!
/*
Package resource is a generated protocol buffer package.
It is generated from these files:
k8s.io/kubernetes/pkg/api/resource/generated.proto
It has these top-level messages:
Quantity
*/
package resource
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/ericchiang/k8s/util/intstr"
import io "io"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// Quantity is a fixed-point representation of a number.
// It provides convenient marshaling/unmarshaling in JSON and YAML,
// in addition to String() and Int64() accessors.
//
// The serialization format is:
//
// <quantity> ::= <signedNumber><suffix>
// (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
// <digit> ::= 0 | 1 | ... | 9
// <digits> ::= <digit> | <digit><digits>
// <number> ::= <digits> | <digits>.<digits> | <digits>. | .<digits>
// <sign> ::= "+" | "-"
// <signedNumber> ::= <number> | <sign><number>
// <suffix> ::= <binarySI> | <decimalExponent> | <decimalSI>
// <binarySI> ::= Ki | Mi | Gi | Ti | Pi | Ei
// (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
// <decimalSI> ::= m | "" | k | M | G | T | P | E
// (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
// <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
//
// No matter which of the three exponent forms is used, no quantity may represent
// a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal
// places. Numbers larger or more precise will be capped or rounded up.
// (E.g.: 0.1m will rounded up to 1m.)
// This may be extended in the future if we require larger or smaller quantities.
//
// When a Quantity is parsed from a string, it will remember the type of suffix
// it had, and will use the same type again when it is serialized.
//
// Before serializing, Quantity will be put in "canonical form".
// This means that Exponent/suffix will be adjusted up or down (with a
// corresponding increase or decrease in Mantissa) such that:
// a. No precision is lost
// b. No fractional digits will be emitted
// c. The exponent (or suffix) is as large as possible.
// The sign will be omitted unless the number is negative.
//
// Examples:
// 1.5 will be serialized as "1500m"
// 1.5Gi will be serialized as "1536Mi"
//
// NOTE: We reserve the right to amend this canonical format, perhaps to
// allow 1.5 to be canonical.
// TODO: Remove above disclaimer after all bikeshedding about format is over,
// or after March 2015.
//
// Note that the quantity will NEVER be internally represented by a
// floating point number. That is the whole point of this exercise.
//
// Non-canonical values will still parse as long as they are well formed,
// but will be re-emitted in their canonical form. (So always use canonical
// form, or don't diff.)
//
// This format is intended to make it difficult to use these numbers without
// writing some sort of special handling code in the hopes that that will
// cause implementors to also use a fixed point implementation.
//
// +protobuf=true
// +protobuf.embed=string
// +protobuf.options.marshal=false
// +protobuf.options.(gogoproto.goproto_stringer)=false
// +k8s:openapi-gen=true
type Quantity struct {
String_ *string `protobuf:"bytes,1,opt,name=string" json:"string,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Quantity) Reset() { *m = Quantity{} }
func (m *Quantity) String() string { return proto.CompactTextString(m) }
func (*Quantity) ProtoMessage() {}
func (*Quantity) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} }
func (m *Quantity) GetString_() string {
if m != nil && m.String_ != nil {
return *m.String_
}
return ""
}
func init() {
proto.RegisterType((*Quantity)(nil), "github.com/ericchiang.k8s.api.resource.Quantity")
}
func (m *Quantity) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Quantity) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.String_ != nil {
dAtA[i] = 0xa
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.String_)))
i += copy(dAtA[i:], *m.String_)
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func encodeFixed64Generated(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
dAtA[offset+4] = uint8(v >> 32)
dAtA[offset+5] = uint8(v >> 40)
dAtA[offset+6] = uint8(v >> 48)
dAtA[offset+7] = uint8(v >> 56)
return offset + 8
}
func encodeFixed32Generated(dAtA []byte, offset int, v uint32) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
return offset + 4
}
func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
}
func (m *Quantity) Size() (n int) {
var l int
_ = l
if m.String_ != nil {
l = len(*m.String_)
n += 1 + l + sovGenerated(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovGenerated(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
}
func sozGenerated(x uint64) (n int) {
return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *Quantity) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Quantity: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Quantity: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field String_", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := string(dAtA[iNdEx:postIndex])
m.String_ = &s
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipGenerated(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthGenerated
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipGenerated(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
}
return iNdEx, nil
case 4:
return iNdEx, nil
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
}
panic("unreachable")
}
var (
ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
)
func init() {
proto.RegisterFile("github.com/ericchiang/k8s/api/resource/generated.proto", fileDescriptorGenerated)
}
var fileDescriptorGenerated = []byte{
// 166 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x32, 0xca, 0xb6, 0x28, 0xd6,
0xcb, 0xcc, 0xd7, 0xcf, 0x2e, 0x4d, 0x4a, 0x2d, 0xca, 0x4b, 0x2d, 0x49, 0x2d, 0xd6, 0x2f, 0xc8,
0x4e, 0xd7, 0x4f, 0x2c, 0xc8, 0xd4, 0x2f, 0x4a, 0x2d, 0xce, 0x2f, 0x2d, 0x4a, 0x4e, 0xd5, 0x4f,
0x4f, 0xcd, 0x4b, 0x2d, 0x4a, 0x2c, 0x49, 0x4d, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x52,
0x82, 0xe8, 0xd1, 0x43, 0xe8, 0xd1, 0x2b, 0xc8, 0x4e, 0xd7, 0x4b, 0x2c, 0xc8, 0xd4, 0x83, 0xe9,
0x91, 0x32, 0xc4, 0x6e, 0x6e, 0x69, 0x49, 0x66, 0x8e, 0x7e, 0x66, 0x5e, 0x49, 0x71, 0x49, 0x11,
0xba, 0xb1, 0x4a, 0x4a, 0x5c, 0x1c, 0x81, 0xa5, 0x89, 0x79, 0x25, 0x99, 0x25, 0x95, 0x42, 0x62,
0x5c, 0x6c, 0xc5, 0x25, 0x45, 0x99, 0x79, 0xe9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x50,
0x9e, 0x93, 0xd4, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38,
0xe3, 0xb1, 0x1c, 0x43, 0x14, 0x07, 0xcc, 0x4a, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x81, 0x00,
0xf7, 0xdc, 0xcb, 0x00, 0x00, 0x00,
}

File diff suppressed because it is too large Load diff

View file

@ -1,32 +0,0 @@
package unversioned
import (
"encoding/json"
"time"
)
// JSON marshaling logic for the Time type. Need to make
// third party resources JSON work.
func (t Time) MarshalJSON() ([]byte, error) {
var seconds, nanos int64
if t.Seconds != nil {
seconds = *t.Seconds
}
if t.Nanos != nil {
nanos = int64(*t.Nanos)
}
return json.Marshal(time.Unix(seconds, nanos))
}
func (t *Time) UnmarshalJSON(p []byte) error {
var t1 time.Time
if err := json.Unmarshal(p, &t1); err != nil {
return err
}
seconds := t1.Unix()
nanos := int32(t1.UnixNano())
t.Seconds = &seconds
t.Nanos = &nanos
return nil
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,32 +0,0 @@
package v1
import (
"encoding/json"
"time"
)
// JSON marshaling logic for the Time type. Need to make
// third party resources JSON work.
func (t Time) MarshalJSON() ([]byte, error) {
var seconds, nanos int64
if t.Seconds != nil {
seconds = *t.Seconds
}
if t.Nanos != nil {
nanos = int64(*t.Nanos)
}
return json.Marshal(time.Unix(seconds, nanos))
}
func (t *Time) UnmarshalJSON(p []byte) error {
var t1 time.Time
if err := json.Unmarshal(p, &t1); err != nil {
return err
}
seconds := t1.Unix()
nanos := int32(t1.UnixNano())
t.Seconds = &seconds
t.Nanos = &nanos
return nil
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,777 +0,0 @@
// Code generated by protoc-gen-gogo.
// source: k8s.io/kubernetes/pkg/apis/storage/v1/generated.proto
// DO NOT EDIT!
/*
Package v1 is a generated protocol buffer package.
It is generated from these files:
k8s.io/kubernetes/pkg/apis/storage/v1/generated.proto
It has these top-level messages:
StorageClass
StorageClassList
*/
package v1
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import k8s_io_kubernetes_pkg_apis_meta_v1 "github.com/ericchiang/k8s/apis/meta/v1"
import _ "github.com/ericchiang/k8s/runtime"
import _ "github.com/ericchiang/k8s/runtime/schema"
import _ "github.com/ericchiang/k8s/util/intstr"
import io "io"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// StorageClass describes the parameters for a class of storage for
// which PersistentVolumes can be dynamically provisioned.
//
// StorageClasses are non-namespaced; the name of the storage class
// according to etcd is in ObjectMeta.Name.
type StorageClass struct {
// Standard object's metadata.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
// +optional
Metadata *k8s_io_kubernetes_pkg_apis_meta_v1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata" json:"metadata,omitempty"`
// Provisioner indicates the type of the provisioner.
Provisioner *string `protobuf:"bytes,2,opt,name=provisioner" json:"provisioner,omitempty"`
// Parameters holds the parameters for the provisioner that should
// create volumes of this storage class.
// +optional
Parameters map[string]string `protobuf:"bytes,3,rep,name=parameters" json:"parameters,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StorageClass) Reset() { *m = StorageClass{} }
func (m *StorageClass) String() string { return proto.CompactTextString(m) }
func (*StorageClass) ProtoMessage() {}
func (*StorageClass) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} }
func (m *StorageClass) GetMetadata() *k8s_io_kubernetes_pkg_apis_meta_v1.ObjectMeta {
if m != nil {
return m.Metadata
}
return nil
}
func (m *StorageClass) GetProvisioner() string {
if m != nil && m.Provisioner != nil {
return *m.Provisioner
}
return ""
}
func (m *StorageClass) GetParameters() map[string]string {
if m != nil {
return m.Parameters
}
return nil
}
// StorageClassList is a collection of storage classes.
type StorageClassList struct {
// Standard list metadata
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
// +optional
Metadata *k8s_io_kubernetes_pkg_apis_meta_v1.ListMeta `protobuf:"bytes,1,opt,name=metadata" json:"metadata,omitempty"`
// Items is the list of StorageClasses
Items []*StorageClass `protobuf:"bytes,2,rep,name=items" json:"items,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StorageClassList) Reset() { *m = StorageClassList{} }
func (m *StorageClassList) String() string { return proto.CompactTextString(m) }
func (*StorageClassList) ProtoMessage() {}
func (*StorageClassList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{1} }
func (m *StorageClassList) GetMetadata() *k8s_io_kubernetes_pkg_apis_meta_v1.ListMeta {
if m != nil {
return m.Metadata
}
return nil
}
func (m *StorageClassList) GetItems() []*StorageClass {
if m != nil {
return m.Items
}
return nil
}
func init() {
proto.RegisterType((*StorageClass)(nil), "github.com/ericchiang.k8s.apis.storage.v1.StorageClass")
proto.RegisterType((*StorageClassList)(nil), "github.com/ericchiang.k8s.apis.storage.v1.StorageClassList")
}
func (m *StorageClass) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *StorageClass) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.Metadata != nil {
dAtA[i] = 0xa
i++
i = encodeVarintGenerated(dAtA, i, uint64(m.Metadata.Size()))
n1, err := m.Metadata.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n1
}
if m.Provisioner != nil {
dAtA[i] = 0x12
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.Provisioner)))
i += copy(dAtA[i:], *m.Provisioner)
}
if len(m.Parameters) > 0 {
for k, _ := range m.Parameters {
dAtA[i] = 0x1a
i++
v := m.Parameters[k]
mapSize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v)))
i = encodeVarintGenerated(dAtA, i, uint64(mapSize))
dAtA[i] = 0xa
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(k)))
i += copy(dAtA[i:], k)
dAtA[i] = 0x12
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(v)))
i += copy(dAtA[i:], v)
}
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func (m *StorageClassList) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *StorageClassList) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.Metadata != nil {
dAtA[i] = 0xa
i++
i = encodeVarintGenerated(dAtA, i, uint64(m.Metadata.Size()))
n2, err := m.Metadata.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n2
}
if len(m.Items) > 0 {
for _, msg := range m.Items {
dAtA[i] = 0x12
i++
i = encodeVarintGenerated(dAtA, i, uint64(msg.Size()))
n, err := msg.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n
}
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func encodeFixed64Generated(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
dAtA[offset+4] = uint8(v >> 32)
dAtA[offset+5] = uint8(v >> 40)
dAtA[offset+6] = uint8(v >> 48)
dAtA[offset+7] = uint8(v >> 56)
return offset + 8
}
func encodeFixed32Generated(dAtA []byte, offset int, v uint32) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
return offset + 4
}
func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
}
func (m *StorageClass) Size() (n int) {
var l int
_ = l
if m.Metadata != nil {
l = m.Metadata.Size()
n += 1 + l + sovGenerated(uint64(l))
}
if m.Provisioner != nil {
l = len(*m.Provisioner)
n += 1 + l + sovGenerated(uint64(l))
}
if len(m.Parameters) > 0 {
for k, v := range m.Parameters {
_ = k
_ = v
mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v)))
n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *StorageClassList) Size() (n int) {
var l int
_ = l
if m.Metadata != nil {
l = m.Metadata.Size()
n += 1 + l + sovGenerated(uint64(l))
}
if len(m.Items) > 0 {
for _, e := range m.Items {
l = e.Size()
n += 1 + l + sovGenerated(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovGenerated(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
}
func sozGenerated(x uint64) (n int) {
return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *StorageClass) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: StorageClass: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: StorageClass: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Metadata == nil {
m.Metadata = &k8s_io_kubernetes_pkg_apis_meta_v1.ObjectMeta{}
}
if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Provisioner", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := string(dAtA[iNdEx:postIndex])
m.Provisioner = &s
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Parameters", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
var keykey uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
keykey |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
var stringLenmapkey uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLenmapkey |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLenmapkey := int(stringLenmapkey)
if intStringLenmapkey < 0 {
return ErrInvalidLengthGenerated
}
postStringIndexmapkey := iNdEx + intStringLenmapkey
if postStringIndexmapkey > l {
return io.ErrUnexpectedEOF
}
mapkey := string(dAtA[iNdEx:postStringIndexmapkey])
iNdEx = postStringIndexmapkey
if m.Parameters == nil {
m.Parameters = make(map[string]string)
}
if iNdEx < postIndex {
var valuekey uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
valuekey |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
var stringLenmapvalue uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLenmapvalue |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLenmapvalue := int(stringLenmapvalue)
if intStringLenmapvalue < 0 {
return ErrInvalidLengthGenerated
}
postStringIndexmapvalue := iNdEx + intStringLenmapvalue
if postStringIndexmapvalue > l {
return io.ErrUnexpectedEOF
}
mapvalue := string(dAtA[iNdEx:postStringIndexmapvalue])
iNdEx = postStringIndexmapvalue
m.Parameters[mapkey] = mapvalue
} else {
var mapvalue string
m.Parameters[mapkey] = mapvalue
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *StorageClassList) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: StorageClassList: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: StorageClassList: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Metadata == nil {
m.Metadata = &k8s_io_kubernetes_pkg_apis_meta_v1.ListMeta{}
}
if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Items = append(m.Items, &StorageClass{})
if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipGenerated(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthGenerated
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipGenerated(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
}
return iNdEx, nil
case 4:
return iNdEx, nil
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
}
panic("unreachable")
}
var (
ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
)
func init() {
proto.RegisterFile("github.com/ericchiang/k8s/apis/storage/v1/generated.proto", fileDescriptorGenerated)
}
var fileDescriptorGenerated = []byte{
// 361 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x91, 0xcf, 0x4a, 0xeb, 0x40,
0x14, 0xc6, 0xef, 0xa4, 0x14, 0x6e, 0xa7, 0x17, 0x6e, 0x09, 0x5d, 0x84, 0x2e, 0x42, 0x28, 0x5c,
0xe8, 0xe2, 0x7a, 0x42, 0xaa, 0x42, 0x11, 0xdc, 0x58, 0x04, 0x15, 0x45, 0x89, 0x3b, 0x77, 0xd3,
0xf6, 0x10, 0xc7, 0x34, 0x7f, 0x98, 0x39, 0x09, 0xf4, 0x4d, 0x5c, 0xba, 0xf3, 0x55, 0x5c, 0xfa,
0x08, 0x52, 0x5f, 0x44, 0xd2, 0x94, 0x1a, 0xda, 0x52, 0x8a, 0xbb, 0xf9, 0xf3, 0xfd, 0xbe, 0x39,
0xdf, 0x37, 0xfc, 0x38, 0x1c, 0x68, 0x90, 0x89, 0x1b, 0x66, 0x23, 0x54, 0x31, 0x12, 0x6a, 0x37,
0x0d, 0x03, 0x57, 0xa4, 0x52, 0xbb, 0x9a, 0x12, 0x25, 0x02, 0x74, 0x73, 0xcf, 0x0d, 0x30, 0x46,
0x25, 0x08, 0x27, 0x90, 0xaa, 0x84, 0x12, 0xf3, 0x5f, 0x89, 0xc1, 0x37, 0x06, 0x69, 0x18, 0x40,
0x81, 0xc1, 0x12, 0x83, 0xdc, 0xeb, 0xf4, 0x77, 0xb8, 0x47, 0x48, 0x62, 0x8b, 0x75, 0xe7, 0x60,
0x3b, 0xa3, 0xb2, 0x98, 0x64, 0x84, 0x1b, 0xf2, 0xa3, 0xdd, 0x72, 0x3d, 0x7e, 0xc4, 0x48, 0x6c,
0x50, 0xde, 0x76, 0x2a, 0x23, 0x39, 0x75, 0x65, 0x4c, 0x9a, 0xd4, 0x3a, 0xd2, 0x7d, 0x31, 0xf8,
0x9f, 0xfb, 0x32, 0xda, 0x70, 0x2a, 0xb4, 0x36, 0xaf, 0xf8, 0xef, 0x22, 0xc3, 0x44, 0x90, 0xb0,
0x98, 0xc3, 0x7a, 0xcd, 0x3e, 0xc0, 0x8e, 0x5a, 0x0a, 0x2d, 0xe4, 0x1e, 0xdc, 0x8e, 0x9e, 0x70,
0x4c, 0x37, 0x48, 0xc2, 0x5f, 0xf1, 0xa6, 0xc3, 0x9b, 0xa9, 0x4a, 0x72, 0xa9, 0x65, 0x12, 0xa3,
0xb2, 0x0c, 0x87, 0xf5, 0x1a, 0x7e, 0xf5, 0xc8, 0x1c, 0x73, 0x9e, 0x0a, 0x25, 0x22, 0x24, 0x54,
0xda, 0xaa, 0x39, 0xb5, 0x5e, 0xb3, 0x3f, 0x84, 0xbd, 0xbe, 0x01, 0xaa, 0x63, 0xc3, 0xdd, 0xca,
0xe5, 0x3c, 0x26, 0x35, 0xf3, 0x2b, 0xb6, 0x9d, 0x53, 0xfe, 0x77, 0xed, 0xda, 0x6c, 0xf1, 0x5a,
0x88, 0xb3, 0x45, 0xc0, 0x86, 0x5f, 0x2c, 0xcd, 0x36, 0xaf, 0xe7, 0x62, 0x9a, 0xe1, 0x72, 0xca,
0x72, 0x73, 0x62, 0x0c, 0x58, 0xf7, 0x95, 0xf1, 0x56, 0xf5, 0xad, 0x6b, 0xa9, 0xc9, 0xbc, 0xd8,
0xa8, 0xe9, 0xff, 0x3e, 0x35, 0x15, 0xec, 0x5a, 0x49, 0x97, 0xbc, 0x2e, 0x09, 0x23, 0x6d, 0x19,
0x8b, 0xf4, 0x87, 0x3f, 0x48, 0xef, 0x97, 0x0e, 0x67, 0xed, 0xb7, 0xb9, 0xcd, 0xde, 0xe7, 0x36,
0xfb, 0x98, 0xdb, 0xec, 0xf9, 0xd3, 0xfe, 0xf5, 0x60, 0xe4, 0xde, 0x57, 0x00, 0x00, 0x00, 0xff,
0xff, 0xc7, 0xf1, 0x3e, 0x97, 0x0d, 0x03, 0x00, 0x00,
}

View file

@ -1,779 +0,0 @@
// Code generated by protoc-gen-gogo.
// source: k8s.io/kubernetes/pkg/apis/storage/v1beta1/generated.proto
// DO NOT EDIT!
/*
Package v1beta1 is a generated protocol buffer package.
It is generated from these files:
k8s.io/kubernetes/pkg/apis/storage/v1beta1/generated.proto
It has these top-level messages:
StorageClass
StorageClassList
*/
package v1beta1
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import k8s_io_kubernetes_pkg_apis_meta_v1 "github.com/ericchiang/k8s/apis/meta/v1"
import _ "github.com/ericchiang/k8s/runtime"
import _ "github.com/ericchiang/k8s/runtime/schema"
import _ "github.com/ericchiang/k8s/util/intstr"
import _ "github.com/ericchiang/k8s/api/v1"
import io "io"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// StorageClass describes the parameters for a class of storage for
// which PersistentVolumes can be dynamically provisioned.
//
// StorageClasses are non-namespaced; the name of the storage class
// according to etcd is in ObjectMeta.Name.
type StorageClass struct {
// Standard object's metadata.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
// +optional
Metadata *k8s_io_kubernetes_pkg_apis_meta_v1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata" json:"metadata,omitempty"`
// Provisioner indicates the type of the provisioner.
Provisioner *string `protobuf:"bytes,2,opt,name=provisioner" json:"provisioner,omitempty"`
// Parameters holds the parameters for the provisioner that should
// create volumes of this storage class.
// +optional
Parameters map[string]string `protobuf:"bytes,3,rep,name=parameters" json:"parameters,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StorageClass) Reset() { *m = StorageClass{} }
func (m *StorageClass) String() string { return proto.CompactTextString(m) }
func (*StorageClass) ProtoMessage() {}
func (*StorageClass) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} }
func (m *StorageClass) GetMetadata() *k8s_io_kubernetes_pkg_apis_meta_v1.ObjectMeta {
if m != nil {
return m.Metadata
}
return nil
}
func (m *StorageClass) GetProvisioner() string {
if m != nil && m.Provisioner != nil {
return *m.Provisioner
}
return ""
}
func (m *StorageClass) GetParameters() map[string]string {
if m != nil {
return m.Parameters
}
return nil
}
// StorageClassList is a collection of storage classes.
type StorageClassList struct {
// Standard list metadata
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
// +optional
Metadata *k8s_io_kubernetes_pkg_apis_meta_v1.ListMeta `protobuf:"bytes,1,opt,name=metadata" json:"metadata,omitempty"`
// Items is the list of StorageClasses
Items []*StorageClass `protobuf:"bytes,2,rep,name=items" json:"items,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StorageClassList) Reset() { *m = StorageClassList{} }
func (m *StorageClassList) String() string { return proto.CompactTextString(m) }
func (*StorageClassList) ProtoMessage() {}
func (*StorageClassList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{1} }
func (m *StorageClassList) GetMetadata() *k8s_io_kubernetes_pkg_apis_meta_v1.ListMeta {
if m != nil {
return m.Metadata
}
return nil
}
func (m *StorageClassList) GetItems() []*StorageClass {
if m != nil {
return m.Items
}
return nil
}
func init() {
proto.RegisterType((*StorageClass)(nil), "github.com/ericchiang.k8s.apis.storage.v1beta1.StorageClass")
proto.RegisterType((*StorageClassList)(nil), "github.com/ericchiang.k8s.apis.storage.v1beta1.StorageClassList")
}
func (m *StorageClass) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *StorageClass) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.Metadata != nil {
dAtA[i] = 0xa
i++
i = encodeVarintGenerated(dAtA, i, uint64(m.Metadata.Size()))
n1, err := m.Metadata.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n1
}
if m.Provisioner != nil {
dAtA[i] = 0x12
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.Provisioner)))
i += copy(dAtA[i:], *m.Provisioner)
}
if len(m.Parameters) > 0 {
for k, _ := range m.Parameters {
dAtA[i] = 0x1a
i++
v := m.Parameters[k]
mapSize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v)))
i = encodeVarintGenerated(dAtA, i, uint64(mapSize))
dAtA[i] = 0xa
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(k)))
i += copy(dAtA[i:], k)
dAtA[i] = 0x12
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(v)))
i += copy(dAtA[i:], v)
}
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func (m *StorageClassList) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *StorageClassList) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.Metadata != nil {
dAtA[i] = 0xa
i++
i = encodeVarintGenerated(dAtA, i, uint64(m.Metadata.Size()))
n2, err := m.Metadata.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n2
}
if len(m.Items) > 0 {
for _, msg := range m.Items {
dAtA[i] = 0x12
i++
i = encodeVarintGenerated(dAtA, i, uint64(msg.Size()))
n, err := msg.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n
}
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func encodeFixed64Generated(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
dAtA[offset+4] = uint8(v >> 32)
dAtA[offset+5] = uint8(v >> 40)
dAtA[offset+6] = uint8(v >> 48)
dAtA[offset+7] = uint8(v >> 56)
return offset + 8
}
func encodeFixed32Generated(dAtA []byte, offset int, v uint32) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
return offset + 4
}
func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
}
func (m *StorageClass) Size() (n int) {
var l int
_ = l
if m.Metadata != nil {
l = m.Metadata.Size()
n += 1 + l + sovGenerated(uint64(l))
}
if m.Provisioner != nil {
l = len(*m.Provisioner)
n += 1 + l + sovGenerated(uint64(l))
}
if len(m.Parameters) > 0 {
for k, v := range m.Parameters {
_ = k
_ = v
mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v)))
n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *StorageClassList) Size() (n int) {
var l int
_ = l
if m.Metadata != nil {
l = m.Metadata.Size()
n += 1 + l + sovGenerated(uint64(l))
}
if len(m.Items) > 0 {
for _, e := range m.Items {
l = e.Size()
n += 1 + l + sovGenerated(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovGenerated(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
}
func sozGenerated(x uint64) (n int) {
return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *StorageClass) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: StorageClass: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: StorageClass: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Metadata == nil {
m.Metadata = &k8s_io_kubernetes_pkg_apis_meta_v1.ObjectMeta{}
}
if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Provisioner", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := string(dAtA[iNdEx:postIndex])
m.Provisioner = &s
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Parameters", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
var keykey uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
keykey |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
var stringLenmapkey uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLenmapkey |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLenmapkey := int(stringLenmapkey)
if intStringLenmapkey < 0 {
return ErrInvalidLengthGenerated
}
postStringIndexmapkey := iNdEx + intStringLenmapkey
if postStringIndexmapkey > l {
return io.ErrUnexpectedEOF
}
mapkey := string(dAtA[iNdEx:postStringIndexmapkey])
iNdEx = postStringIndexmapkey
if m.Parameters == nil {
m.Parameters = make(map[string]string)
}
if iNdEx < postIndex {
var valuekey uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
valuekey |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
var stringLenmapvalue uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLenmapvalue |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLenmapvalue := int(stringLenmapvalue)
if intStringLenmapvalue < 0 {
return ErrInvalidLengthGenerated
}
postStringIndexmapvalue := iNdEx + intStringLenmapvalue
if postStringIndexmapvalue > l {
return io.ErrUnexpectedEOF
}
mapvalue := string(dAtA[iNdEx:postStringIndexmapvalue])
iNdEx = postStringIndexmapvalue
m.Parameters[mapkey] = mapvalue
} else {
var mapvalue string
m.Parameters[mapkey] = mapvalue
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *StorageClassList) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: StorageClassList: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: StorageClassList: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Metadata == nil {
m.Metadata = &k8s_io_kubernetes_pkg_apis_meta_v1.ListMeta{}
}
if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Items = append(m.Items, &StorageClass{})
if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipGenerated(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthGenerated
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipGenerated(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
}
return iNdEx, nil
case 4:
return iNdEx, nil
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
}
panic("unreachable")
}
var (
ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
)
func init() {
proto.RegisterFile("github.com/ericchiang/k8s/apis/storage/v1beta1/generated.proto", fileDescriptorGenerated)
}
var fileDescriptorGenerated = []byte{
// 373 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x91, 0xcd, 0x6a, 0xdb, 0x40,
0x14, 0x85, 0x3b, 0x32, 0xa6, 0xf5, 0xb8, 0x50, 0x23, 0xba, 0x50, 0xbd, 0x10, 0xc2, 0x2b, 0x53,
0xdc, 0x19, 0x64, 0xba, 0x30, 0x86, 0x6e, 0x1a, 0x02, 0x26, 0xe4, 0x0f, 0x65, 0x97, 0xdd, 0xd8,
0xbe, 0xc8, 0x13, 0x59, 0x3f, 0xcc, 0x5c, 0x09, 0xfc, 0x26, 0x79, 0x81, 0xec, 0xf2, 0x20, 0x59,
0xe6, 0x11, 0x82, 0xf3, 0x22, 0x41, 0x96, 0x70, 0x84, 0xe5, 0x18, 0x93, 0x9d, 0x34, 0x73, 0xbe,
0x73, 0xef, 0x39, 0x43, 0xc7, 0xc1, 0x48, 0x33, 0x19, 0xf3, 0x20, 0x9d, 0x82, 0x8a, 0x00, 0x41,
0xf3, 0x24, 0xf0, 0xb9, 0x48, 0xa4, 0xe6, 0x1a, 0x63, 0x25, 0x7c, 0xe0, 0x99, 0x3b, 0x05, 0x14,
0x2e, 0xf7, 0x21, 0x02, 0x25, 0x10, 0xe6, 0x2c, 0x51, 0x31, 0xc6, 0xe6, 0xef, 0x82, 0x65, 0xef,
0x2c, 0x4b, 0x02, 0x9f, 0xe5, 0x2c, 0x2b, 0x59, 0x56, 0xb2, 0xdd, 0xe1, 0x81, 0x39, 0x21, 0xa0,
0xe0, 0x59, 0xcd, 0xbf, 0xfb, 0x67, 0x3f, 0xa3, 0xd2, 0x08, 0x65, 0x08, 0x35, 0xf9, 0xdf, 0xc3,
0x72, 0x3d, 0x5b, 0x40, 0x28, 0x6a, 0x94, 0xbb, 0x9f, 0x4a, 0x51, 0x2e, 0xb9, 0x8c, 0x50, 0xa3,
0xaa, 0x21, 0x83, 0x0f, 0xb3, 0xec, 0x49, 0xd1, 0x7b, 0x30, 0xe8, 0xf7, 0x9b, 0xa2, 0x8d, 0x93,
0xa5, 0xd0, 0xda, 0x3c, 0xa3, 0xdf, 0xf2, 0xc4, 0x73, 0x81, 0xc2, 0x22, 0x0e, 0xe9, 0xb7, 0x87,
0x8c, 0x1d, 0x68, 0x32, 0xd7, 0xb2, 0xcc, 0x65, 0x57, 0xd3, 0x3b, 0x98, 0xe1, 0x05, 0xa0, 0xf0,
0xb6, 0xbc, 0xe9, 0xd0, 0x76, 0xa2, 0xe2, 0x4c, 0x6a, 0x19, 0x47, 0xa0, 0x2c, 0xc3, 0x21, 0xfd,
0x96, 0x57, 0x3d, 0x32, 0x17, 0x94, 0x26, 0x42, 0x89, 0x10, 0x10, 0x94, 0xb6, 0x1a, 0x4e, 0xa3,
0xdf, 0x1e, 0x4e, 0xd8, 0xf1, 0x2f, 0xc7, 0xaa, 0xbb, 0xb3, 0xeb, 0xad, 0xd5, 0x69, 0x84, 0x6a,
0xe5, 0x55, 0xbc, 0xbb, 0xff, 0xe8, 0x8f, 0x9d, 0x6b, 0xb3, 0x43, 0x1b, 0x01, 0xac, 0x36, 0x29,
0x5b, 0x5e, 0xfe, 0x69, 0xfe, 0xa4, 0xcd, 0x4c, 0x2c, 0x53, 0x28, 0x57, 0x2d, 0x7e, 0xc6, 0xc6,
0x88, 0xf4, 0x1e, 0x09, 0xed, 0x54, 0x67, 0x9d, 0x4b, 0x8d, 0xe6, 0xa4, 0xd6, 0xd5, 0xe0, 0x98,
0xae, 0x72, 0x76, 0xa7, 0xa9, 0x4b, 0xda, 0x94, 0x08, 0xa1, 0xb6, 0x8c, 0x4d, 0x05, 0xa3, 0xcf,
0x56, 0xe0, 0x15, 0x36, 0xff, 0x7f, 0x3d, 0xad, 0x6d, 0xf2, 0xbc, 0xb6, 0xc9, 0xcb, 0xda, 0x26,
0xf7, 0xaf, 0xf6, 0x97, 0xdb, 0xaf, 0xa5, 0xfc, 0x2d, 0x00, 0x00, 0xff, 0xff, 0x9d, 0x5c, 0x08,
0xd1, 0x54, 0x03, 0x00, 0x00,
}

View file

@ -1,598 +0,0 @@
/*
Package k8s implements a Kubernetes client.
c, err := k8s.NewInClusterClient()
if err != nil {
// handle error
}
extensions := c.ExtensionsV1Beta1()
ingresses, err := extensions.ListIngresses(ctx, c.Namespace)
if err != nil {
// handle error
}
*/
package k8s
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"path"
"strconv"
"strings"
"time"
"golang.org/x/net/http2"
"github.com/ericchiang/k8s/api/unversioned"
"github.com/ericchiang/k8s/runtime"
"github.com/ericchiang/k8s/watch/versioned"
"github.com/golang/protobuf/proto"
)
const (
// AllNamespaces is given to list and watch operations to signify that the code should
// list or watch resources in all namespaces.
AllNamespaces = allNamespaces
// Actual definition is private in case we want to change it later.
allNamespaces = ""
namespaceDefault = "default"
)
// String returns a pointer to a string. Useful for creating API objects
// that take pointers instead of literals.
//
// cm := &v1.ConfigMap{
// Metadata: &v1.ObjectMeta{
// Name: k8s.String("myconfigmap"),
// Namespace: k8s.String("default"),
// },
// Data: map[string]string{
// "foo": "bar",
// },
// }
//
func String(s string) *string { return &s }
// Int is a convinence for converting an int literal to a pointer to an int.
func Int(i int) *int { return &i }
// Bool is a convinence for converting a bool literal to a pointer to a bool.
func Bool(b bool) *bool { return &b }
const (
// Types for watch events.
EventAdded = "ADDED"
EventDeleted = "DELETED"
EventModified = "MODIFIED"
EventError = "ERROR"
)
// Client is a Kuberntes client.
type Client struct {
// The URL of the API server.
Endpoint string
// Default namespaces for objects that don't supply a namespace in
// their object metadata.
Namespace string
// SetHeaders provides a hook for modifying the HTTP headers of all requests.
//
// client, err := k8s.NewClient(config)
// if err != nil {
// // handle error
// }
// client.SetHeaders = func(h http.Header) error {
// h.Set("Authorization", "Bearer "+mytoken)
// return nil
// }
//
SetHeaders func(h http.Header) error
Client *http.Client
}
func (c *Client) newRequest(ctx context.Context, verb, url string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequest(verb, url, body)
if err != nil {
return nil, err
}
if c.SetHeaders != nil {
if err := c.SetHeaders(req.Header); err != nil {
return nil, err
}
}
return req.WithContext(ctx), nil
}
// Option represents optional call parameters, such as label selectors.
type Option interface {
queryParam() (key, val string)
}
type resourceVersionOption string
func (r resourceVersionOption) queryParam() (string, string) {
return "resourceVersion", string(r)
}
// ResourceVersion causes watch operations to only show changes since
// a particular version of a resource.
func ResourceVersion(resourceVersion string) Option {
return resourceVersionOption(resourceVersion)
}
type timeoutSeconds string
func (t timeoutSeconds) queryParam() (string, string) {
return "timeoutSeconds", string(t)
}
// Timeout declares the timeout for list and watch operations. Timeout
// is only accurate to the second.
func Timeout(d time.Duration) Option {
return timeoutSeconds(strconv.FormatInt(int64(d/time.Second), 10))
}
// NewClient initializes a client from a client config.
func NewClient(config *Config) (*Client, error) {
if len(config.Contexts) == 0 {
if config.CurrentContext != "" {
return nil, fmt.Errorf("no contexts with name %q", config.CurrentContext)
}
if n := len(config.Clusters); n == 0 {
return nil, errors.New("no clusters provided")
} else if n > 1 {
return nil, errors.New("multiple clusters but no current context")
}
if n := len(config.AuthInfos); n == 0 {
return nil, errors.New("no users provided")
} else if n > 1 {
return nil, errors.New("multiple users but no current context")
}
return newClient(config.Clusters[0].Cluster, config.AuthInfos[0].AuthInfo, namespaceDefault)
}
var ctx Context
if config.CurrentContext == "" {
if n := len(config.Contexts); n == 0 {
return nil, errors.New("no contexts provided")
} else if n > 1 {
return nil, errors.New("multiple contexts but no current context")
}
ctx = config.Contexts[0].Context
} else {
for _, c := range config.Contexts {
if c.Name == config.CurrentContext {
ctx = c.Context
goto configFound
}
}
return nil, fmt.Errorf("no config named %q", config.CurrentContext)
configFound:
}
if ctx.Cluster == "" {
return nil, fmt.Errorf("context doesn't have a cluster")
}
if ctx.AuthInfo == "" {
return nil, fmt.Errorf("context doesn't have a user")
}
var (
user AuthInfo
cluster Cluster
)
for _, u := range config.AuthInfos {
if u.Name == ctx.AuthInfo {
user = u.AuthInfo
goto userFound
}
}
return nil, fmt.Errorf("no user named %q", ctx.AuthInfo)
userFound:
for _, c := range config.Clusters {
if c.Name == ctx.Cluster {
cluster = c.Cluster
goto clusterFound
}
}
return nil, fmt.Errorf("no cluster named %q", ctx.Cluster)
clusterFound:
namespace := ctx.Namespace
if namespace == "" {
namespace = namespaceDefault
}
return newClient(cluster, user, namespace)
}
// NewInClusterClient returns a client that uses the service account bearer token mounted
// into Kubernetes pods.
func NewInClusterClient() (*Client, error) {
host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
if len(host) == 0 || len(port) == 0 {
return nil, errors.New("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined")
}
namespace, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
if err != nil {
return nil, err
}
cluster := Cluster{
Server: "https://" + host + ":" + port,
CertificateAuthority: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt",
}
user := AuthInfo{TokenFile: "/var/run/secrets/kubernetes.io/serviceaccount/token"}
return newClient(cluster, user, string(namespace))
}
func load(filepath string, data []byte) (out []byte, err error) {
if filepath != "" {
data, err = ioutil.ReadFile(filepath)
}
return data, err
}
func newClient(cluster Cluster, user AuthInfo, namespace string) (*Client, error) {
if cluster.Server == "" {
// NOTE: kubectl defaults to localhost:8080, but it's probably better to just
// be strict.
return nil, fmt.Errorf("no cluster endpoint provided")
}
ca, err := load(cluster.CertificateAuthority, cluster.CertificateAuthorityData)
if err != nil {
return nil, fmt.Errorf("loading certificate authority: %v", err)
}
clientCert, err := load(user.ClientCertificate, user.ClientCertificateData)
if err != nil {
return nil, fmt.Errorf("load client cert: %v", err)
}
clientKey, err := load(user.ClientKey, user.ClientKeyData)
if err != nil {
return nil, fmt.Errorf("load client cert: %v", err)
}
// See https://github.com/gtank/cryptopasta
tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12}
if len(ca) != 0 {
tlsConfig.RootCAs = x509.NewCertPool()
if !tlsConfig.RootCAs.AppendCertsFromPEM(ca) {
return nil, errors.New("certificate authority doesn't contain any certificates")
}
}
if len(clientCert) != 0 {
cert, err := tls.X509KeyPair(clientCert, clientKey)
if err != nil {
return nil, fmt.Errorf("invalid client cert and key pair: %v", err)
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
token := user.Token
if user.TokenFile != "" {
data, err := ioutil.ReadFile(user.TokenFile)
if err != nil {
return nil, fmt.Errorf("load token file: %v", err)
}
token = string(data)
}
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
TLSClientConfig: tlsConfig,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
if err := http2.ConfigureTransport(transport); err != nil {
return nil, err
}
client := &Client{
Endpoint: cluster.Server,
Namespace: namespace,
Client: &http.Client{
Transport: transport,
},
}
if token != "" {
client.SetHeaders = func(h http.Header) error {
h.Set("Authorization", "Bearer "+token)
return nil
}
}
if user.Username != "" && user.Password != "" {
auth := user.Username + ":" + user.Password
auth = "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
client.SetHeaders = func(h http.Header) error {
h.Set("Authorization", auth)
return nil
}
}
return client, nil
}
// APIError is an error from a unexpected status code.
type APIError struct {
// The status object returned by the Kubernetes API,
Status *unversioned.Status
// Status code returned by the HTTP request.
//
// NOTE: For some reason the value set in Status.Code
// doesn't correspond to the HTTP status code. Possibly
// a bug?
Code int
}
func (e *APIError) Error() string {
if e.Status != nil && e.Status.Message != nil && e.Status.Status != nil {
return fmt.Sprintf("kubernetes api: %s %d %s", *e.Status.Status, e.Code, *e.Status.Message)
}
return fmt.Sprintf("%#v", e)
}
func checkStatusCode(c *codec, statusCode int, body []byte) error {
if statusCode/100 == 2 {
return nil
}
return newAPIError(c, statusCode, body)
}
func newAPIError(c *codec, statusCode int, body []byte) error {
status := new(unversioned.Status)
if err := c.unmarshal(body, status); err != nil {
return fmt.Errorf("decode error status: %v", err)
}
return &APIError{status, statusCode}
}
func (c *Client) client() *http.Client {
if c.Client == nil {
return http.DefaultClient
}
return c.Client
}
// The following methods hold the logic for interacting with the Kubernetes API. Generated
// clients are thin wrappers on top of these methods.
//
// This client implements specs in the "API Conventions" developer document, which can be
// found here:
//
// https://github.com/kubernetes/kubernetes/blob/master/docs/devel/api-conventions.md
func (c *Client) urlFor(apiGroup, apiVersion, namespace, resource, name string, options ...Option) string {
basePath := "apis/"
if apiGroup == "" {
basePath = "api/"
}
var p string
if namespace != "" {
p = path.Join(basePath, apiGroup, apiVersion, "namespaces", namespace, resource, name)
} else {
p = path.Join(basePath, apiGroup, apiVersion, resource, name)
}
endpoint := ""
if strings.HasSuffix(c.Endpoint, "/") {
endpoint = c.Endpoint + p
} else {
endpoint = c.Endpoint + "/" + p
}
if len(options) == 0 {
return endpoint
}
v := url.Values{}
for _, option := range options {
key, val := option.queryParam()
v.Set(key, val)
}
return endpoint + "?" + v.Encode()
}
func (c *Client) urlForPath(path string) string {
if strings.HasPrefix(path, "/") {
path = path[1:]
}
if strings.HasSuffix(c.Endpoint, "/") {
return c.Endpoint + path
}
return c.Endpoint + "/" + path
}
func (c *Client) create(ctx context.Context, codec *codec, verb, url string, req, resp interface{}) error {
body, err := codec.marshal(req)
if err != nil {
return err
}
r, err := c.newRequest(ctx, verb, url, bytes.NewReader(body))
if err != nil {
return err
}
r.Header.Set("Content-Type", codec.contentType)
r.Header.Set("Accept", codec.contentType)
re, err := c.client().Do(r)
if err != nil {
return err
}
defer re.Body.Close()
respBody, err := ioutil.ReadAll(re.Body)
if err != nil {
return fmt.Errorf("read body: %v", err)
}
if err := checkStatusCode(codec, re.StatusCode, respBody); err != nil {
return err
}
return codec.unmarshal(respBody, resp)
}
func (c *Client) delete(ctx context.Context, codec *codec, url string) error {
r, err := c.newRequest(ctx, "DELETE", url, nil)
if err != nil {
return err
}
r.Header.Set("Accept", codec.contentType)
re, err := c.client().Do(r)
if err != nil {
return err
}
defer re.Body.Close()
respBody, err := ioutil.ReadAll(re.Body)
if err != nil {
return fmt.Errorf("read body: %v", err)
}
if err := checkStatusCode(codec, re.StatusCode, respBody); err != nil {
return err
}
return nil
}
// get can be used to either get or list a given resource.
func (c *Client) get(ctx context.Context, codec *codec, url string, resp interface{}) error {
r, err := c.newRequest(ctx, "GET", url, nil)
if err != nil {
return err
}
r.Header.Set("Accept", codec.contentType)
re, err := c.client().Do(r)
if err != nil {
return err
}
defer re.Body.Close()
respBody, err := ioutil.ReadAll(re.Body)
if err != nil {
return fmt.Errorf("read body: %v", err)
}
if err := checkStatusCode(codec, re.StatusCode, respBody); err != nil {
return err
}
return codec.unmarshal(respBody, resp)
}
var unknownPrefix = []byte{0x6b, 0x38, 0x73, 0x00}
func parseUnknown(b []byte) (*runtime.Unknown, error) {
if !bytes.HasPrefix(b, unknownPrefix) {
return nil, errors.New("bytes did not start with expected prefix")
}
var u runtime.Unknown
if err := proto.Unmarshal(b[len(unknownPrefix):], &u); err != nil {
return nil, err
}
return &u, nil
}
type event struct {
event *versioned.Event
unknown *runtime.Unknown
}
type watcher struct {
r io.ReadCloser
}
func (w *watcher) Close() error {
return w.r.Close()
}
// Decode the next event from a watch stream.
//
// See: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/protobuf.md#streaming-wire-format
func (w *watcher) next() (*versioned.Event, *runtime.Unknown, error) {
length := make([]byte, 4)
if _, err := io.ReadFull(w.r, length); err != nil {
return nil, nil, err
}
body := make([]byte, int(binary.BigEndian.Uint32(length)))
if _, err := io.ReadFull(w.r, body); err != nil {
return nil, nil, fmt.Errorf("read frame body: %v", err)
}
var event versioned.Event
if err := proto.Unmarshal(body, &event); err != nil {
return nil, nil, err
}
if event.Object == nil {
return nil, nil, fmt.Errorf("event had no underlying object")
}
unknown, err := parseUnknown(event.Object.Raw)
if err != nil {
return nil, nil, err
}
return &event, unknown, nil
}
func (c *Client) watch(ctx context.Context, url string) (*watcher, error) {
if strings.Contains(url, "?") {
url = url + "&watch=true"
} else {
url = url + "?watch=true"
}
r, err := c.newRequest(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
r.Header.Set("Accept", "application/vnd.kubernetes.protobuf;type=watch")
resp, err := c.client().Do(r)
if err != nil {
return nil, err
}
if resp.StatusCode/100 != 2 {
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
return nil, newAPIError(pbCodec, resp.StatusCode, body)
}
w := &watcher{resp.Body}
return w, nil
}

View file

@ -1,405 +0,0 @@
package k8s
import (
"context"
"crypto/rand"
"encoding/hex"
"encoding/json"
"io"
"net/http"
"os"
"os/exec"
"reflect"
"strings"
"testing"
"github.com/ericchiang/k8s/api/v1"
metav1 "github.com/ericchiang/k8s/apis/meta/v1"
)
const skipMsg = `
warning: this package's test run using the default context of your "kubectl" command,
and will create resources on your cluster (mostly configmaps).
If you wish to continue set the following environment variable:
export K8S_CLIENT_TEST=1
To suppress this message, set:
export K8S_CLIENT_TEST=0
`
func newTestClient(t *testing.T) *Client {
if os.Getenv("K8S_CLIENT_TEST") == "0" {
t.Skip("")
}
if os.Getenv("K8S_CLIENT_TEST") != "1" {
t.Skip(skipMsg)
}
cmd := exec.Command("kubectl", "config", "view", "-o", "json")
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("'kubectl config view -o json': %v %s", err, out)
}
config := new(Config)
if err := json.Unmarshal(out, config); err != nil {
t.Fatalf("parse kubeconfig: %v '%s'", err, out)
}
client, err := NewClient(config)
if err != nil {
t.Fatalf("new client: %v", err)
}
return client
}
func newName() string {
b := make([]byte, 12)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
panic(err)
}
return hex.EncodeToString(b)
}
func TestNewTestClient(t *testing.T) {
newTestClient(t)
}
func TestHTTP2(t *testing.T) {
client := newTestClient(t)
req, err := http.NewRequest("GET", client.urlForPath("/api"), nil)
if err != nil {
t.Fatal(err)
}
resp, err := client.Client.Do(req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if !strings.HasPrefix(resp.Proto, "HTTP/2") {
t.Errorf("expected proto=HTTP/2.X, got=", resp.Proto)
}
}
func TestListNodes(t *testing.T) {
client := newTestClient(t)
if _, err := client.CoreV1().ListNodes(context.Background()); err != nil {
t.Fatal("failed to list nodes: %v", err)
}
}
func TestConfigMaps(t *testing.T) {
client := newTestClient(t).CoreV1()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
name := newName()
labelVal := newName()
cm := &v1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: String(name),
Namespace: String("default"),
Labels: map[string]string{
"testLabel": labelVal,
},
},
Data: map[string]string{
"foo": "bar",
},
}
got, err := client.CreateConfigMap(ctx, cm)
if err != nil {
t.Fatalf("create config map: %v", err)
}
got.Data["zam"] = "spam"
_, err = client.UpdateConfigMap(ctx, got)
if err != nil {
t.Fatalf("update config map: %v", err)
}
tests := []struct {
labelVal string
expNum int
}{
{labelVal, 1},
{newName(), 0},
}
for _, test := range tests {
l := new(LabelSelector)
l.Eq("testLabel", test.labelVal)
configMaps, err := client.ListConfigMaps(ctx, "default", l.Selector())
if err != nil {
t.Errorf("failed to list configmaps: %v", err)
continue
}
got := len(configMaps.Items)
if got != test.expNum {
t.Errorf("expected selector to return %d items got %d", test.expNum, got)
}
}
if err := client.DeleteConfigMap(ctx, *cm.Metadata.Name, *cm.Metadata.Namespace); err != nil {
t.Fatalf("delete config map: %v", err)
}
}
func TestWatch(t *testing.T) {
client := newTestClient(t).CoreV1()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
w, err := client.WatchConfigMaps(ctx, "default")
if err != nil {
t.Fatal(err)
}
defer w.Close()
name := newName()
labelVal := newName()
cm := &v1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: String(name),
Namespace: String("default"),
Labels: map[string]string{
"testLabel": labelVal,
},
},
Data: map[string]string{
"foo": "bar",
},
}
got, err := client.CreateConfigMap(ctx, cm)
if err != nil {
t.Fatalf("create config map: %v", err)
}
if event, gotFromWatch, err := w.Next(); err != nil {
t.Errorf("failed to get next watch: %v", err)
} else {
if *event.Type != EventAdded {
t.Errorf("expected event type %q got %q", EventAdded, *event.Type)
}
if !reflect.DeepEqual(got, gotFromWatch) {
t.Errorf("object from add event did not match expected value")
}
}
got.Data["zam"] = "spam"
got, err = client.UpdateConfigMap(ctx, got)
if err != nil {
t.Fatalf("update config map: %v", err)
}
if event, gotFromWatch, err := w.Next(); err != nil {
t.Errorf("failed to get next watch: %v", err)
} else {
if *event.Type != EventModified {
t.Errorf("expected event type %q got %q", EventModified, *event.Type)
}
if !reflect.DeepEqual(got, gotFromWatch) {
t.Errorf("object from modified event did not match expected value")
}
}
tests := []struct {
labelVal string
expNum int
}{
{labelVal, 1},
{newName(), 0},
}
for _, test := range tests {
l := new(LabelSelector)
l.Eq("testLabel", test.labelVal)
configMaps, err := client.ListConfigMaps(ctx, "default", l.Selector())
if err != nil {
t.Errorf("failed to list configmaps: %v", err)
continue
}
got := len(configMaps.Items)
if got != test.expNum {
t.Errorf("expected selector to return %d items got %d", test.expNum, got)
}
}
if err := client.DeleteConfigMap(ctx, *cm.Metadata.Name, *cm.Metadata.Namespace); err != nil {
t.Fatalf("delete config map: %v", err)
}
if event, gotFromWatch, err := w.Next(); err != nil {
t.Errorf("failed to get next watch: %v", err)
} else {
if *event.Type != EventDeleted {
t.Errorf("expected event type %q got %q", EventDeleted, *event.Type)
}
// Resource version will be different after a delete
got.Metadata.ResourceVersion = String("")
gotFromWatch.Metadata.ResourceVersion = String("")
if !reflect.DeepEqual(got, gotFromWatch) {
t.Errorf("object from deleted event did not match expected value")
}
}
}
// TestWatchNamespace ensures that creating a configmap in a non-default namespace is not returned while watching the default namespace
func TestWatchNamespace(t *testing.T) {
client := newTestClient(t).CoreV1()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
defaultWatch, err := client.WatchConfigMaps(ctx, "default")
if err != nil {
t.Fatal(err)
}
defer defaultWatch.Close()
allWatch, err := client.WatchConfigMaps(ctx, AllNamespaces)
if err != nil {
t.Fatal(err)
}
defer allWatch.Close()
nonDefaultNamespaceName := newName()
defaultName := newName()
name := newName()
labelVal := newName()
// Create a configmap in the default namespace so the "default" watch has something to return
defaultCM := &v1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: String(defaultName),
Namespace: String("default"),
Labels: map[string]string{
"testLabel": labelVal,
},
},
Data: map[string]string{
"foo": "bar",
},
}
defaultGot, err := client.CreateConfigMap(ctx, defaultCM)
if err != nil {
t.Fatalf("create config map: %v", err)
}
// Create a non-default Namespace
ns := &v1.Namespace{
Metadata: &metav1.ObjectMeta{
Name: String(nonDefaultNamespaceName),
},
}
if _, err := client.CreateNamespace(ctx, ns); err != nil {
t.Fatalf("create non-default-namespace: %v", err)
}
// Create a configmap in the non-default namespace
nonDefaultCM := &v1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: String(name),
Namespace: String(nonDefaultNamespaceName),
Labels: map[string]string{
"testLabel": labelVal,
},
},
Data: map[string]string{
"foo": "bar",
},
}
nonDefaultGot, err := client.CreateConfigMap(ctx, nonDefaultCM)
if err != nil {
t.Fatalf("create config map: %v", err)
}
// Watching the default namespace should not return the non-default namespace configmap,
// and instead return the previously created configmap in the default namespace
if _, gotFromWatch, err := defaultWatch.Next(); err != nil {
t.Errorf("failed to get next watch: %v", err)
} else {
if reflect.DeepEqual(nonDefaultGot, gotFromWatch) {
t.Errorf("config map in non-default namespace returned while watching default namespace")
}
if !reflect.DeepEqual(defaultGot, gotFromWatch) {
t.Errorf("object from add event did not match expected value")
}
}
// However, watching all-namespaces should contain both the default and non-default namespaced configmaps
if _, gotFromWatch, err := allWatch.Next(); err != nil {
t.Errorf("failed to get next watch: %v", err)
} else {
if !reflect.DeepEqual(defaultGot, gotFromWatch) {
t.Errorf("watching all namespaces did not return the expected configmap")
}
}
if _, gotFromWatch, err := allWatch.Next(); err != nil {
t.Errorf("failed to get next watch: %v", err)
} else {
if !reflect.DeepEqual(nonDefaultGot, gotFromWatch) {
t.Errorf("watching all namespaces did not return the expected configmap")
}
}
// Delete the config map in the default namespace first, then delete the non-default namespace config map.
// Only the former should be noticed by the default-watch.
if err := client.DeleteConfigMap(ctx, *defaultCM.Metadata.Name, *defaultCM.Metadata.Namespace); err != nil {
t.Fatalf("delete config map: %v", err)
}
if err := client.DeleteConfigMap(ctx, *nonDefaultCM.Metadata.Name, *nonDefaultCM.Metadata.Namespace); err != nil {
t.Fatalf("delete config map: %v", err)
}
if event, gotFromWatch, err := defaultWatch.Next(); err != nil {
t.Errorf("failed to get next watch: %v", err)
} else {
if *event.Type != EventDeleted {
t.Errorf("expected event type %q got %q", EventDeleted, *event.Type)
}
// Resource version will be different after a delete
nonDefaultGot.Metadata.ResourceVersion = String("")
gotFromWatch.Metadata.ResourceVersion = String("")
if reflect.DeepEqual(nonDefaultGot, gotFromWatch) {
t.Errorf("should not have received event from non-default namespace while watching default namespace")
}
}
if err := client.DeleteNamespace(ctx, nonDefaultNamespaceName); err != nil {
t.Fatalf("delete namespace: %v", err)
}
}
func TestDefaultNamespace(t *testing.T) {
c := &Config{
Clusters: []NamedCluster{
{
Name: "local",
Cluster: Cluster{
Server: "http://localhost:8080",
},
},
},
AuthInfos: []NamedAuthInfo{
{
Name: "local",
},
},
}
cli, err := NewClient(c)
if err != nil {
t.Fatal(err)
}
if cli.Namespace != "default" {
t.Errorf("expected namespace=%q got=%q", "default", cli.Namespace)
}
}

View file

@ -1,83 +0,0 @@
package k8s
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/ericchiang/k8s/runtime"
"github.com/golang/protobuf/proto"
)
type codec struct {
contentType string
marshal func(interface{}) ([]byte, error)
unmarshal func([]byte, interface{}) error
}
var (
// Kubernetes implements its own custom protobuf format to allow clients (and possibly
// servers) to use either JSON or protocol buffers. The protocol introduces a custom content
// type and magic bytes to signal the use of protobufs, and wraps each object with API group,
// version and resource data.
//
// The protocol spec which this client implements can be found here:
//
// https://github.com/kubernetes/kubernetes/blob/master/docs/proposals/protobuf.md
//
pbCodec = &codec{
contentType: "application/vnd.kubernetes.protobuf",
marshal: marshalPB,
unmarshal: unmarshalPB,
}
jsonCodec = &codec{
contentType: "application/json",
marshal: json.Marshal,
unmarshal: json.Unmarshal,
}
)
var magicBytes = []byte{0x6b, 0x38, 0x73, 0x00}
func unmarshalPB(b []byte, obj interface{}) error {
message, ok := obj.(proto.Message)
if !ok {
return fmt.Errorf("expected obj of type proto.Message, got %T", obj)
}
if len(b) < len(magicBytes) {
return errors.New("payload is not a kubernetes protobuf object")
}
if !bytes.Equal(b[:len(magicBytes)], magicBytes) {
return errors.New("payload is not a kubernetes protobuf object")
}
u := new(runtime.Unknown)
if err := u.Unmarshal(b[len(magicBytes):]); err != nil {
return fmt.Errorf("unmarshal unknown: %v", err)
}
return proto.Unmarshal(u.Raw, message)
}
func marshalPB(obj interface{}) ([]byte, error) {
message, ok := obj.(proto.Message)
if !ok {
return nil, fmt.Errorf("expected obj of type proto.Message, got %T", obj)
}
payload, err := proto.Marshal(message)
if err != nil {
return nil, err
}
// The URL path informs the API server what the API group, version, and resource
// of the object. We don't need to specify it here to talk to the API server.
body, err := (&runtime.Unknown{Raw: payload}).Marshal()
if err != nil {
return nil, err
}
d := make([]byte, len(magicBytes)+len(body))
copy(d[:len(magicBytes)], magicBytes)
copy(d[len(magicBytes):], body)
return d, nil
}

View file

@ -1,170 +0,0 @@
/*
Copyright 2014 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 k8s
import (
"github.com/ericchiang/k8s/runtime"
)
// Where possible, json tags match the cli argument names.
// Top level config objects and all values required for proper functioning are not "omitempty". Any truly optional piece of config is allowed to be omitted.
// Config holds the information needed to build connect to remote kubernetes clusters as a given user
type Config struct {
// Legacy field from pkg/api/types.go TypeMeta.
// TODO(jlowdermilk): remove this after eliminating downstream dependencies.
// +optional
Kind string `json:"kind,omitempty"`
// DEPRECATED: APIVersion is the preferred api version for communicating with the kubernetes cluster (v1, v2, etc).
// Because a cluster can run multiple API groups and potentially multiple versions of each, it no longer makes sense to specify
// a single value for the cluster version.
// This field isn't really needed anyway, so we are deprecating it without replacement.
// It will be ignored if it is present.
// +optional
APIVersion string `json:"apiVersion,omitempty"`
// Preferences holds general information to be use for cli interactions
Preferences Preferences `json:"preferences"`
// Clusters is a map of referencable names to cluster configs
Clusters []NamedCluster `json:"clusters"`
// AuthInfos is a map of referencable names to user configs
AuthInfos []NamedAuthInfo `json:"users"`
// Contexts is a map of referencable names to context configs
Contexts []NamedContext `json:"contexts"`
// CurrentContext is the name of the context that you would like to use by default
CurrentContext string `json:"current-context"`
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
// +optional
Extensions []NamedExtension `json:"extensions,omitempty"`
}
type Preferences struct {
// +optional
Colors bool `json:"colors,omitempty"`
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
// +optional
Extensions []NamedExtension `json:"extensions,omitempty"`
}
// Cluster contains information about how to communicate with a kubernetes cluster
type Cluster struct {
// Server is the address of the kubernetes cluster (https://hostname:port).
Server string `json:"server"`
// APIVersion is the preferred api version for communicating with the kubernetes cluster (v1, v2, etc).
// +optional
APIVersion string `json:"api-version,omitempty"`
// InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure.
// +optional
InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"`
// CertificateAuthority is the path to a cert file for the certificate authority.
// +optional
CertificateAuthority string `json:"certificate-authority,omitempty"`
// CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority
// +optional
CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
// +optional
Extensions []NamedExtension `json:"extensions,omitempty"`
}
// AuthInfo contains information that describes identity information. This is use to tell the kubernetes cluster who you are.
type AuthInfo struct {
// ClientCertificate is the path to a client cert file for TLS.
// +optional
ClientCertificate string `json:"client-certificate,omitempty"`
// ClientCertificateData contains PEM-encoded data from a client cert file for TLS. Overrides ClientCertificate
// +optional
ClientCertificateData []byte `json:"client-certificate-data,omitempty"`
// ClientKey is the path to a client key file for TLS.
// +optional
ClientKey string `json:"client-key,omitempty"`
// ClientKeyData contains PEM-encoded data from a client key file for TLS. Overrides ClientKey
// +optional
ClientKeyData []byte `json:"client-key-data,omitempty"`
// Token is the bearer token for authentication to the kubernetes cluster.
// +optional
Token string `json:"token,omitempty"`
// TokenFile is a pointer to a file that contains a bearer token (as described above). If both Token and TokenFile are present, Token takes precedence.
// +optional
TokenFile string `json:"tokenFile,omitempty"`
// Impersonate is the username to imperonate. The name matches the flag.
// +optional
Impersonate string `json:"as,omitempty"`
// Username is the username for basic authentication to the kubernetes cluster.
// +optional
Username string `json:"username,omitempty"`
// Password is the password for basic authentication to the kubernetes cluster.
// +optional
Password string `json:"password,omitempty"`
// AuthProvider specifies a custom authentication plugin for the kubernetes cluster.
// +optional
AuthProvider *AuthProviderConfig `json:"auth-provider,omitempty"`
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
// +optional
Extensions []NamedExtension `json:"extensions,omitempty"`
}
// Context is a tuple of references to a cluster (how do I communicate with a kubernetes cluster), a user (how do I identify myself), and a namespace (what subset of resources do I want to work with)
type Context struct {
// Cluster is the name of the cluster for this context
Cluster string `json:"cluster"`
// AuthInfo is the name of the authInfo for this context
AuthInfo string `json:"user"`
// Namespace is the default namespace to use on unspecified requests
// +optional
Namespace string `json:"namespace,omitempty"`
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
// +optional
Extensions []NamedExtension `json:"extensions,omitempty"`
}
// NamedCluster relates nicknames to cluster information
type NamedCluster struct {
// Name is the nickname for this Cluster
Name string `json:"name"`
// Cluster holds the cluster information
Cluster Cluster `json:"cluster"`
}
// NamedContext relates nicknames to context information
type NamedContext struct {
// Name is the nickname for this Context
Name string `json:"name"`
// Context holds the context information
Context Context `json:"context"`
}
// NamedAuthInfo relates nicknames to auth information
type NamedAuthInfo struct {
// Name is the nickname for this AuthInfo
Name string `json:"name"`
// AuthInfo holds the auth information
AuthInfo AuthInfo `json:"user"`
}
// NamedExtension relates nicknames to extension information
type NamedExtension struct {
// Name is the nickname for this Extension
Name string `json:"name"`
// Extension holds the extension information
Extension runtime.RawExtension `json:"extension"`
}
// AuthProviderConfig holds the configuration for a specified auth provider.
type AuthProviderConfig struct {
Name string `json:"name"`
Config map[string]string `json:"config"`
}

View file

@ -1,63 +0,0 @@
package k8s
import (
"context"
"path"
"github.com/ericchiang/k8s/api/unversioned"
)
type Version struct {
Major string `json:"major"`
Minor string `json:"minor"`
GitVersion string `json:"gitVersion"`
GitCommit string `json:"gitCommit"`
GitTreeState string `json:"gitTreeState"`
BuildDate string `json:"buildDate"`
GoVersion string `json:"goVersion"`
Compiler string `json:"compiler"`
Platform string `json:"platform"`
}
func (c *Client) Discovery() *Discovery {
return &Discovery{c}
}
// Discovery is a client used to determine the API version and supported
// resources of the server.
type Discovery struct {
client *Client
}
func (d *Discovery) Version(ctx context.Context) (*Version, error) {
var v Version
if err := d.client.get(ctx, jsonCodec, d.client.urlForPath("version"), &v); err != nil {
return nil, err
}
return &v, nil
}
func (d *Discovery) APIGroups(ctx context.Context) (*unversioned.APIGroupList, error) {
var groups unversioned.APIGroupList
if err := d.client.get(ctx, pbCodec, d.client.urlForPath("apis"), &groups); err != nil {
return nil, err
}
return &groups, nil
}
func (d *Discovery) APIGroup(ctx context.Context, name string) (*unversioned.APIGroup, error) {
var group unversioned.APIGroup
if err := d.client.get(ctx, pbCodec, d.client.urlForPath(path.Join("apis", name)), &group); err != nil {
return nil, err
}
return &group, nil
}
func (d *Discovery) APIResources(ctx context.Context, groupName, groupVersion string) (*unversioned.APIResourceList, error) {
var list unversioned.APIResourceList
if err := d.client.get(ctx, pbCodec, d.client.urlForPath(path.Join("apis", groupName, groupVersion)), &list); err != nil {
return nil, err
}
return &list, nil
}

View file

@ -1,28 +0,0 @@
package k8s
import (
"context"
"testing"
)
func TestDiscovery(t *testing.T) {
client := newTestClient(t).Discovery()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if _, err := client.Version(ctx); err != nil {
t.Errorf("list version: %v", err)
}
if _, err := client.APIGroups(ctx); err != nil {
t.Errorf("list api groups: %v", err)
}
if _, err := client.APIGroup(ctx, "extensions"); err != nil {
t.Errorf("list api group: %v", err)
}
if _, err := client.APIResources(ctx, "extensions", "v1beta1"); err != nil {
t.Errorf("list api group resources: %v", err)
}
}

View file

@ -1,38 +0,0 @@
// +build ignore
package configmaps
import (
"context"
"fmt"
"net/http"
"github.com/ericchiang/k8s"
"github.com/ericchiang/k8s/api/v1"
metav1 "github.com/ericchiang/k8s/apis/meta/v1"
)
// createConfigMap creates a configmap in the client's default namespace
// but does not return an error if a configmap of the same name already
// exists.
func createConfigMap(client *k8s.Client, name string, values map[string]string) error {
cm := &v1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: &name,
Namespace: &client.Namespace,
},
Data: values,
}
_, err := client.CoreV1().CreateConfigMap(context.TODO(), cm)
// If an HTTP error was returned by the API server, it will be of type
// *k8s.APIError. This can be used to inspect the status code.
if apiErr, ok := err.(*k8s.APIError); ok {
// Resource already exists. Carry on.
if apiErr.Code == http.StatusConflict {
return nil
}
}
return fmt.Errorf("create configmap: %v", err)
}

View file

@ -1,24 +0,0 @@
// +build ignore
package configmaps
import (
"context"
"github.com/ericchiang/k8s"
"github.com/ericchiang/k8s/api/v1"
metav1 "github.com/ericchiang/k8s/apis/meta/v1"
)
func createConfigMap(client *k8s.Client, name string, values map[string]string) error {
cm := &v1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: &name,
Namespace: &client.Namespace,
},
Data: values,
}
// Will return the created configmap as well.
_, err := client.CoreV1().CreateConfigMap(context.TODO(), cm)
return err
}

View file

@ -1,26 +0,0 @@
// +build ignore
package nodes
import (
"context"
"fmt"
"log"
"github.com/ericchiang/k8s"
)
func listNodes() {
client, err := k8s.NewInClusterClient()
if err != nil {
log.Fatal(err)
}
nodes, err := client.CoreV1().ListNodes(context.Background())
if err != nil {
log.Fatal(err)
}
for _, node := range nodes.Items {
fmt.Printf("name=%q schedulable=%t\n", *node.Metadata.Name, !*node.Spec.Unschedulable)
}
}

View file

@ -1,28 +0,0 @@
// +build ignore
package kubeconfig
import (
"fmt"
"io/ioutil"
"github.com/ericchiang/k8s"
"github.com/ghodss/yaml"
)
// loadClient parses a kubeconfig from a file and returns a Kubernetes
// client. It does not support extensions or client auth providers.
func loadClient(kubeconfigPath string) (*k8s.Client, error) {
data, err := ioutil.ReadFile(kubeconfigPath)
if err != nil {
return nil, fmt.Errorf("read kubeconfig: %v", err)
}
// Unmarshal YAML into a Kubernetes config object.
var config k8s.Config
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("unmarshal kubeconfig: %v", err)
}
return k8s.NewClient(&config)
}

View file

@ -1,382 +0,0 @@
// +build ignore
package main
import (
"bytes"
"errors"
"fmt"
"go/types"
"io/ioutil"
"os"
"os/exec"
"path"
"sort"
"strings"
"text/template"
"golang.org/x/tools/go/loader"
)
func main() {
if err := load(); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(2)
}
}
func isInterface(obj interface{}) (*types.Interface, bool) {
switch obj := obj.(type) {
case *types.TypeName:
return isInterface(obj.Type())
case *types.Named:
return isInterface(obj.Underlying())
case *types.Interface:
return obj, true
default:
return nil, false
}
}
type Resource struct {
Name string
Namespaced bool
HasList bool
Pluralized string
}
type byName []Resource
func (n byName) Len() int { return len(n) }
func (n byName) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func (n byName) Less(i, j int) bool { return n[i].Name < n[j].Name }
type Package struct {
Name string
APIGroup string
APIVersion string
ImportPath string
ImportName string
Resources []Resource
}
type byGroup []Package
func (r byGroup) Len() int { return len(r) }
func (r byGroup) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r byGroup) Less(i, j int) bool {
if r[i].APIGroup != r[j].APIGroup {
return r[i].APIGroup < r[j].APIGroup
}
return r[i].APIVersion < r[j].APIVersion
}
// Incorrect but this is basically what Kubernetes does.
func pluralize(s string) string {
switch {
case strings.HasSuffix(s, "points"):
// NOTE: the k8s "endpoints" resource is already pluralized
return s
case strings.HasSuffix(s, "s"):
return s + "es"
case strings.HasSuffix(s, "y"):
return s[:len(s)-1] + "ies"
default:
return s + "s"
}
}
var tmpl = template.Must(template.New("").Funcs(template.FuncMap{
"pluralize": pluralize,
}).Parse(`
// {{ .Name }} returns a client for interacting with the {{ .APIGroup }}/{{ .APIVersion }} API group.
func (c *Client) {{ .Name }}() *{{ .Name }} {
return &{{ .Name }}{c}
}
// {{ .Name }} is a client for interacting with the {{ .APIGroup }}/{{ .APIVersion }} API group.
type {{ .Name }} struct {
client *Client
}
{{ range $i, $r := .Resources }}
func (c *{{ $.Name }}) Create{{ $r.Name }}(ctx context.Context, obj *{{ $.ImportName }}.{{ $r.Name }}) (*{{ $.ImportName }}.{{ $r.Name }}, error) {
md := obj.GetMetadata()
if md.Name != nil && *md.Name == "" {
return nil, fmt.Errorf("no name for given object")
}
ns := ""
if md.Namespace != nil {
ns = *md.Namespace
}
if !{{ $r.Namespaced }} && ns != ""{
return nil, fmt.Errorf("resource isn't namespaced")
}
if {{ $r.Namespaced }} {
if ns == "" {
return nil, fmt.Errorf("no resource namespace provided")
}
md.Namespace = &ns
}
url := c.client.urlFor("{{ $.APIGroup }}", "{{ $.APIVersion }}", ns, "{{ $r.Pluralized }}", "")
resp := new({{ $.ImportName }}.{{ $r.Name }})
err := c.client.create(ctx, pbCodec, "POST", url, obj, resp)
if err != nil {
return nil, err
}
return resp, nil
}
func (c *{{ $.Name }}) Update{{ $r.Name }}(ctx context.Context, obj *{{ $.ImportName }}.{{ $r.Name }}) (*{{ $.ImportName }}.{{ $r.Name }}, error) {
md := obj.GetMetadata()
if md.Name != nil && *md.Name == "" {
return nil, fmt.Errorf("no name for given object")
}
ns := ""
if md.Namespace != nil {
ns = *md.Namespace
}
if !{{ $r.Namespaced }} && ns != "" {
return nil, fmt.Errorf("resource isn't namespaced")
}
if {{ $r.Namespaced }} {
if ns == "" {
return nil, fmt.Errorf("no resource namespace provided")
}
md.Namespace = &ns
}
url := c.client.urlFor("{{ $.APIGroup }}", "{{ $.APIVersion }}", *md.Namespace, "{{ $r.Pluralized }}", *md.Name)
resp := new({{ $.ImportName }}.{{ $r.Name }})
err := c.client.create(ctx, pbCodec, "PUT", url, obj, resp)
if err != nil {
return nil, err
}
return resp, nil
}
func (c *{{ $.Name }}) Delete{{ $r.Name }}(ctx context.Context, name string{{ if $r.Namespaced }}, namespace string{{ end }}) error {
if name == "" {
return fmt.Errorf("create: no name for given object")
}
url := c.client.urlFor("{{ $.APIGroup }}", "{{ $.APIVersion }}", {{ if $r.Namespaced }}namespace{{ else }}AllNamespaces{{ end }}, "{{ $r.Pluralized }}", name)
return c.client.delete(ctx, pbCodec, url)
}
func (c *{{ $.Name }}) Get{{ $r.Name }}(ctx context.Context, name{{ if $r.Namespaced }}, namespace{{ end }} string) (*{{ $.ImportName }}.{{ $r.Name }}, error) {
if name == "" {
return nil, fmt.Errorf("create: no name for given object")
}
url := c.client.urlFor("{{ $.APIGroup }}", "{{ $.APIVersion }}", {{ if $r.Namespaced }}namespace{{ else }}AllNamespaces{{ end }}, "{{ $r.Pluralized }}", name)
resp := new({{ $.ImportName }}.{{ $r.Name }})
if err := c.client.get(ctx, pbCodec, url, resp); err != nil {
return nil, err
}
return resp, nil
}
{{- if $r.HasList }}
type {{ $.Name }}{{ $r.Name }}Watcher struct {
watcher *watcher
}
func (w *{{ $.Name }}{{ $r.Name }}Watcher) Next() (*versioned.Event, *{{ $.ImportName }}.{{ $r.Name }}, error) {
event, unknown, err := w.watcher.next()
if err != nil {
return nil, nil, err
}
resp := new({{ $.ImportName }}.{{ $r.Name }})
if err := proto.Unmarshal(unknown.Raw, resp); err != nil {
return nil, nil, err
}
return event, resp, nil
}
func (w *{{ $.Name }}{{ $r.Name }}Watcher) Close() error {
return w.watcher.Close()
}
func (c *{{ $.Name }}) Watch{{ $r.Name | pluralize }}(ctx context.Context{{ if $r.Namespaced }}, namespace string{{ end }}, options ...Option) (*{{ $.Name }}{{ $r.Name }}Watcher, error) {
url := c.client.urlFor("{{ $.APIGroup }}", "{{ $.APIVersion }}", {{ if $r.Namespaced }}namespace{{ else }}AllNamespaces{{ end }}, "{{ $r.Pluralized }}", "", options...)
watcher, err := c.client.watch(ctx, url)
if err != nil {
return nil, err
}
return &{{ $.Name }}{{ $r.Name }}Watcher{watcher}, nil
}
func (c *{{ $.Name }}) List{{ $r.Name | pluralize }}(ctx context.Context{{ if $r.Namespaced }}, namespace string{{ end }}, options ...Option) (*{{ $.ImportName }}.{{ $r.Name }}List, error) {
url := c.client.urlFor("{{ $.APIGroup }}", "{{ $.APIVersion }}", {{ if $r.Namespaced }}namespace{{ else }}AllNamespaces{{ end }}, "{{ $r.Pluralized }}", "", options...)
resp := new({{ $.ImportName }}.{{ $r.Name }}List)
if err := c.client.get(ctx, pbCodec, url, resp); err != nil {
return nil, err
}
return resp, nil
}{{ end }}
{{ end }}
`))
var (
apiGroupName = map[string]string{
"authentication": "authentication.k8s.io",
"authorization": "authorization.k8s.io",
"certificates": "certificates.k8s.io",
"rbac": "rbac.authorization.k8s.io",
"storage": "storage.k8s.io",
}
notNamespaced = map[string]bool{
"ClusterRole": true,
"ClusterRoleBinding": true,
"ComponentStatus": true,
"Node": true,
"Namespace": true,
"PersistentVolume": true,
"PodSecurityPolicy": true,
"ThirdPartyResource": true,
"CertificateSigningRequest": true,
"TokenReview": true,
"SubjectAccessReview": true,
"SelfSubjectAccessReview": true,
"ImageReview": true,
"StorageClass": true,
}
)
func clientName(apiGroup, apiVersion string) string {
switch apiGroup {
case "":
apiGroup = "Core"
case "rbac":
apiGroup = "RBAC"
default:
apiGroup = strings.Title(apiGroup)
}
r := strings.NewReplacer("alpha", "Alpha", "beta", "Beta")
return apiGroup + r.Replace(strings.Title(apiVersion))
}
func load() error {
out, err := exec.Command("go", "list", "./...").CombinedOutput()
if err != nil {
return fmt.Errorf("go list: %v %s", err, out)
}
var conf loader.Config
if _, err := conf.FromArgs(strings.Fields(string(out)), false); err != nil {
return fmt.Errorf("from args: %v", err)
}
prog, err := conf.Load()
if err != nil {
return fmt.Errorf("load: %v", err)
}
thisPkg, ok := prog.Imported["github.com/ericchiang/k8s"]
if !ok {
return errors.New("could not find this package")
}
// Types defined in tpr.go. It's hacky, but to "load" interfaces as their
// go/types equilvalent, we either have to:
//
// * Define them in code somewhere (what we're doing here).
// * Manually construct them using go/types (blah).
// * Parse them from an inlined string (doesn't work in combination with other pkgs).
//
var interfaces []*types.Interface
for _, s := range []string{"object", "after16Object"} {
obj := thisPkg.Pkg.Scope().Lookup(s)
if obj == nil {
return errors.New("failed to lookup object interface")
}
intr, ok := isInterface(obj)
if !ok {
return errors.New("failed to convert to interface")
}
interfaces = append(interfaces, intr)
}
var pkgs []Package
for name, pkgInfo := range prog.Imported {
pkg := Package{
APIVersion: path.Base(name),
APIGroup: path.Base(path.Dir(name)),
ImportPath: name,
}
pkg.ImportName = pkg.APIGroup + pkg.APIVersion
if pkg.APIGroup == "api" {
pkg.APIGroup = ""
}
pkg.Name = clientName(pkg.APIGroup, pkg.APIVersion)
if name, ok := apiGroupName[pkg.APIGroup]; ok {
pkg.APIGroup = name
}
for _, obj := range pkgInfo.Defs {
tn, ok := obj.(*types.TypeName)
if !ok {
continue
}
impl := false
for _, intr := range interfaces {
impl = impl || types.Implements(types.NewPointer(tn.Type()), intr)
}
if !impl {
continue
}
if tn.Name() == "JobTemplateSpec" {
continue
}
pkg.Resources = append(pkg.Resources, Resource{
Name: tn.Name(),
Pluralized: pluralize(strings.ToLower(tn.Name())),
HasList: pkgInfo.Pkg.Scope().Lookup(tn.Name()+"List") != nil,
Namespaced: !notNamespaced[tn.Name()],
})
}
pkgs = append(pkgs, pkg)
}
sort.Sort(byGroup(pkgs))
buff := new(bytes.Buffer)
buff.WriteString("package k8s\n\n")
buff.WriteString("import (\n")
buff.WriteString("\t\"context\"\n")
buff.WriteString("\t\"fmt\"\n\n")
for _, pkg := range pkgs {
if len(pkg.Resources) == 0 {
continue
}
fmt.Fprintf(buff, "\t%s \"%s\"\n", pkg.ImportName, pkg.ImportPath)
}
fmt.Fprintf(buff, "\t%q\n", "github.com/ericchiang/k8s/watch/versioned")
fmt.Fprintf(buff, "\t%q\n", "github.com/golang/protobuf/proto")
buff.WriteString(")\n")
for _, pkg := range pkgs {
sort.Sort(byName(pkg.Resources))
for _, resource := range pkg.Resources {
fmt.Println(pkg.APIGroup, pkg.APIVersion, resource.Name)
}
if len(pkg.Resources) != 0 {
if err := tmpl.Execute(buff, pkg); err != nil {
return fmt.Errorf("execute: %v", err)
}
}
}
return ioutil.WriteFile("types.go", buff.Bytes(), 0644)
}

View file

@ -1,110 +0,0 @@
#!/bin/bash
set -ex
# Clean up any existing build.
rm -rf assets/k8s.io
mkdir -p assets/k8s.io/kubernetes
VERSIONS=( "1.4.7" "1.5.1" "1.6.0-rc.1" )
for VERSION in ${VERSIONS[@]}; do
if [ ! -f assets/v${VERSION}.zip ]; then
wget https://github.com/kubernetes/kubernetes/archive/v${VERSION}.zip -O assets/v${VERSION}.zip
fi
# Copy source tree to assets/k8s.io/kubernetes. Newer versions overwrite existing ones.
unzip -q assets/v${VERSION}.zip -d assets/
cp -r assets/kubernetes-${VERSION}/* assets/k8s.io/kubernetes
rm -rf assets/kubernetes-${VERSION}
done
# Rewrite API machinery files to their equivalent.
apimachinery=assets/k8s.io/kubernetes/staging/src/k8s.io/apimachinery/
for file in $( find $apimachinery -type f -name '*.proto' ); do
path=assets/k8s.io/kubernetes/${file#$apimachinery}
mkdir -p $(dirname $path)
mv $file $path
done
# Remove any existing generated code.
rm -rf api apis config.go runtime util types.go watch
# Generate Go code from proto definitions.
PKG=$PWD
cd assets
protobuf=$( find k8s.io/kubernetes/pkg/{api,apis,util,runtime,watch} -name '*.proto' )
# Remote this ununused import:
# https://github.com/kubernetes/kubernetes/blob/v1.6.0-rc.1/pkg/api/v1/generated.proto#L29
sed -i '/"k8s\.io\/apiserver\/pkg\/apis\/example\/v1\/generated.proto"/d' $protobuf
# Rewrite all of the API machineary out of staging.
sed -i 's|"k8s.io/apimachinery/|"k8s.io/kubernetes/|g' $protobuf
sed -i 's/k8s\.io.apimachinery/k8s\.io.kubernetes/g' $protobuf
for file in $protobuf; do
echo $file
# Generate protoc definitions at the base of this repo.
protoc --gofast_out=$PKG $file
done
cd -
mv k8s.io/kubernetes/pkg/* .
rm -rf k8s.io
# Copy kubeconfig structs.
client_dir="client/unversioned/clientcmd/api/v1"
cp assets/k8s.io/kubernetes/pkg/${client_dir}/types.go config.go
sed -i 's|package v1|package k8s|g' config.go
# Rewrite imports for the generated fiels.
sed -i 's|"k8s.io/kubernetes/pkg|"github.com/ericchiang/k8s|g' $( find {api,apis,config.go,util,runtime,watch} -name '*.go' )
sed -i 's|"k8s.io.kubernetes.pkg.|"github.com/ericchiang.k8s.|g' $( find {api,apis,config.go,util,runtime,watch} -name '*.go' )
# Clean up assets.
rm -rf assets/k8s.io
# Generate HTTP clients from Go structs.
go run gen.go
# Fix JSON marshaling for types need by third party resources.
cat << EOF >> api/unversioned/time.go
package unversioned
import (
"encoding/json"
"time"
)
// JSON marshaling logic for the Time type. Need to make
// third party resources JSON work.
func (t Time) MarshalJSON() ([]byte, error) {
var seconds, nanos int64
if t.Seconds != nil {
seconds = *t.Seconds
}
if t.Nanos != nil {
nanos = int64(*t.Nanos)
}
return json.Marshal(time.Unix(seconds, nanos))
}
func (t *Time) UnmarshalJSON(p []byte) error {
var t1 time.Time
if err := json.Unmarshal(p, &t1); err != nil {
return err
}
seconds := t1.Unix()
nanos := int32(t1.UnixNano())
t.Seconds = &seconds
t.Nanos = &nanos
return nil
}
EOF
gofmt -w api/unversioned/time.go
cp api/unversioned/time.go apis/meta/v1
sed -i 's|package unversioned|package v1|g' apis/meta/v1/time.go

View file

@ -1,93 +0,0 @@
package k8s
import (
"regexp"
"strings"
)
const (
qnameCharFmt string = "[A-Za-z0-9]"
qnameExtCharFmt string = "[-A-Za-z0-9_./]"
qualifiedNameFmt string = "(" + qnameCharFmt + qnameExtCharFmt + "*)?" + qnameCharFmt
qualifiedNameMaxLength int = 63
labelValueFmt string = "(" + qualifiedNameFmt + ")?"
)
var labelValueRegexp = regexp.MustCompile("^" + labelValueFmt + "$")
// LabelSelector represents a Kubernetes label selector.
//
// Any values that don't conform to Kubernetes label value restrictions
// will be silently dropped.
//
// l := new(k8s.LabelSelector)
// l.Eq("component", "frontend")
// l.In("type", "prod", "staging")
//
type LabelSelector struct {
stmts []string
}
type labelSelectorOption string
func (l labelSelectorOption) queryParam() (string, string) {
return "labelSelector", string(l)
}
func (l *LabelSelector) Selector() Option {
return labelSelectorOption(l.encode())
}
func (l *LabelSelector) encode() string {
return strings.Join(l.stmts, ",")
}
func validLabelValue(s string) bool {
if len(s) > 63 || len(s) == 0 {
return false
}
return labelValueRegexp.MatchString(s)
}
// Eq selects labels which have the key and the key has the provide value.
func (l *LabelSelector) Eq(key, val string) {
if !validLabelValue(key) || !validLabelValue(val) {
return
}
l.stmts = append(l.stmts, key+"="+val)
}
// NotEq selects labels where the key is present and has a different value
// than the value provided.
func (l *LabelSelector) NotEq(key, val string) {
if !validLabelValue(key) || !validLabelValue(val) {
return
}
l.stmts = append(l.stmts, key+"!="+val)
}
// In selects labels which have the key and the key has one of the provided values.
func (l *LabelSelector) In(key string, vals ...string) {
if !validLabelValue(key) || len(vals) == 0 {
return
}
for _, val := range vals {
if !validLabelValue(val) {
return
}
}
l.stmts = append(l.stmts, key+" in ("+strings.Join(vals, ", ")+")")
}
// NotIn selects labels which have the key and the key is not one of the provided values.
func (l *LabelSelector) NotIn(key string, vals ...string) {
if !validLabelValue(key) || len(vals) == 0 {
return
}
for _, val := range vals {
if !validLabelValue(val) {
return
}
}
l.stmts = append(l.stmts, key+" notin ("+strings.Join(vals, ", ")+")")
}

View file

@ -1,52 +0,0 @@
package k8s
import "testing"
func TestLabelSelector(t *testing.T) {
tests := []struct {
f func(l *LabelSelector)
want string
}{
{
f: func(l *LabelSelector) {
l.Eq("component", "frontend")
},
want: "component=frontend",
},
{
f: func(l *LabelSelector) {
l.Eq("kubernetes.io/role", "master")
},
want: "kubernetes.io/role=master",
},
{
f: func(l *LabelSelector) {
l.In("type", "prod", "staging")
l.Eq("component", "frontend")
},
want: "type in (prod, staging),component=frontend",
},
{
f: func(l *LabelSelector) {
l.NotIn("type", "prod", "staging")
l.NotEq("component", "frontend")
},
want: "type notin (prod, staging),component!=frontend",
},
{
f: func(l *LabelSelector) {
l.Eq("foo", "I am not a valid label value")
},
want: "",
},
}
for i, test := range tests {
l := new(LabelSelector)
test.f(l)
got := l.encode()
if test.want != got {
t.Errorf("case %d: want=%q, got=%q", i, test.want, got)
}
}
}

View file

@ -1,905 +0,0 @@
// Code generated by protoc-gen-gogo.
// source: k8s.io/kubernetes/pkg/runtime/generated.proto
// DO NOT EDIT!
/*
Package runtime is a generated protocol buffer package.
It is generated from these files:
k8s.io/kubernetes/pkg/runtime/generated.proto
It has these top-level messages:
RawExtension
TypeMeta
Unknown
*/
package runtime
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/ericchiang/k8s/util/intstr"
import io "io"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// RawExtension is used to hold extensions in external versions.
//
// To use this, make a field which has RawExtension as its type in your external, versioned
// struct, and Object in your internal struct. You also need to register your
// various plugin types.
//
// // Internal package:
// type MyAPIObject struct {
// runtime.TypeMeta `json:",inline"`
// MyPlugin runtime.Object `json:"myPlugin"`
// }
// type PluginA struct {
// AOption string `json:"aOption"`
// }
//
// // External package:
// type MyAPIObject struct {
// runtime.TypeMeta `json:",inline"`
// MyPlugin runtime.RawExtension `json:"myPlugin"`
// }
// type PluginA struct {
// AOption string `json:"aOption"`
// }
//
// // On the wire, the JSON will look something like this:
// {
// "kind":"MyAPIObject",
// "apiVersion":"v1",
// "myPlugin": {
// "kind":"PluginA",
// "aOption":"foo",
// },
// }
//
// So what happens? Decode first uses json or yaml to unmarshal the serialized data into
// your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked.
// The next step is to copy (using pkg/conversion) into the internal struct. The runtime
// package's DefaultScheme has conversion functions installed which will unpack the
// JSON stored in RawExtension, turning it into the correct object type, and storing it
// in the Object. (TODO: In the case where the object is of an unknown type, a
// runtime.Unknown object will be created and stored.)
//
// +k8s:deepcopy-gen=true
// +protobuf=true
// +k8s:openapi-gen=true
type RawExtension struct {
// Raw is the underlying serialization of this object.
//
// TODO: Determine how to detect ContentType and ContentEncoding of 'Raw' data.
Raw []byte `protobuf:"bytes,1,opt,name=raw" json:"raw,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *RawExtension) Reset() { *m = RawExtension{} }
func (m *RawExtension) String() string { return proto.CompactTextString(m) }
func (*RawExtension) ProtoMessage() {}
func (*RawExtension) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} }
func (m *RawExtension) GetRaw() []byte {
if m != nil {
return m.Raw
}
return nil
}
// TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type,
// like this:
// type MyAwesomeAPIObject struct {
// runtime.TypeMeta `json:",inline"`
// ... // other fields
// }
// func (obj *MyAwesomeAPIObject) SetGroupVersionKind(gvk *metav1.GroupVersionKind) { metav1.UpdateTypeMeta(obj,gvk) }; GroupVersionKind() *GroupVersionKind
//
// TypeMeta is provided here for convenience. You may use it directly from this package or define
// your own with the same fields.
//
// +k8s:deepcopy-gen=true
// +protobuf=true
// +k8s:openapi-gen=true
type TypeMeta struct {
// +optional
ApiVersion *string `protobuf:"bytes,1,opt,name=apiVersion" json:"apiVersion,omitempty"`
// +optional
Kind *string `protobuf:"bytes,2,opt,name=kind" json:"kind,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *TypeMeta) Reset() { *m = TypeMeta{} }
func (m *TypeMeta) String() string { return proto.CompactTextString(m) }
func (*TypeMeta) ProtoMessage() {}
func (*TypeMeta) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{1} }
func (m *TypeMeta) GetApiVersion() string {
if m != nil && m.ApiVersion != nil {
return *m.ApiVersion
}
return ""
}
func (m *TypeMeta) GetKind() string {
if m != nil && m.Kind != nil {
return *m.Kind
}
return ""
}
// Unknown allows api objects with unknown types to be passed-through. This can be used
// to deal with the API objects from a plug-in. Unknown objects still have functioning
// TypeMeta features-- kind, version, etc.
// TODO: Make this object have easy access to field based accessors and settors for
// metadata and field mutatation.
//
// +k8s:deepcopy-gen=true
// +protobuf=true
// +k8s:openapi-gen=true
type Unknown struct {
TypeMeta *TypeMeta `protobuf:"bytes,1,opt,name=typeMeta" json:"typeMeta,omitempty"`
// Raw will hold the complete serialized object which couldn't be matched
// with a registered type. Most likely, nothing should be done with this
// except for passing it through the system.
Raw []byte `protobuf:"bytes,2,opt,name=raw" json:"raw,omitempty"`
// ContentEncoding is encoding used to encode 'Raw' data.
// Unspecified means no encoding.
ContentEncoding *string `protobuf:"bytes,3,opt,name=contentEncoding" json:"contentEncoding,omitempty"`
// ContentType is serialization method used to serialize 'Raw'.
// Unspecified means ContentTypeJSON.
ContentType *string `protobuf:"bytes,4,opt,name=contentType" json:"contentType,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Unknown) Reset() { *m = Unknown{} }
func (m *Unknown) String() string { return proto.CompactTextString(m) }
func (*Unknown) ProtoMessage() {}
func (*Unknown) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{2} }
func (m *Unknown) GetTypeMeta() *TypeMeta {
if m != nil {
return m.TypeMeta
}
return nil
}
func (m *Unknown) GetRaw() []byte {
if m != nil {
return m.Raw
}
return nil
}
func (m *Unknown) GetContentEncoding() string {
if m != nil && m.ContentEncoding != nil {
return *m.ContentEncoding
}
return ""
}
func (m *Unknown) GetContentType() string {
if m != nil && m.ContentType != nil {
return *m.ContentType
}
return ""
}
func init() {
proto.RegisterType((*RawExtension)(nil), "github.com/ericchiang.k8s.runtime.RawExtension")
proto.RegisterType((*TypeMeta)(nil), "github.com/ericchiang.k8s.runtime.TypeMeta")
proto.RegisterType((*Unknown)(nil), "github.com/ericchiang.k8s.runtime.Unknown")
}
func (m *RawExtension) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RawExtension) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.Raw != nil {
dAtA[i] = 0xa
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Raw)))
i += copy(dAtA[i:], m.Raw)
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func (m *TypeMeta) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *TypeMeta) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.ApiVersion != nil {
dAtA[i] = 0xa
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.ApiVersion)))
i += copy(dAtA[i:], *m.ApiVersion)
}
if m.Kind != nil {
dAtA[i] = 0x12
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.Kind)))
i += copy(dAtA[i:], *m.Kind)
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func (m *Unknown) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Unknown) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.TypeMeta != nil {
dAtA[i] = 0xa
i++
i = encodeVarintGenerated(dAtA, i, uint64(m.TypeMeta.Size()))
n1, err := m.TypeMeta.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n1
}
if m.Raw != nil {
dAtA[i] = 0x12
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Raw)))
i += copy(dAtA[i:], m.Raw)
}
if m.ContentEncoding != nil {
dAtA[i] = 0x1a
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.ContentEncoding)))
i += copy(dAtA[i:], *m.ContentEncoding)
}
if m.ContentType != nil {
dAtA[i] = 0x22
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.ContentType)))
i += copy(dAtA[i:], *m.ContentType)
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func encodeFixed64Generated(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
dAtA[offset+4] = uint8(v >> 32)
dAtA[offset+5] = uint8(v >> 40)
dAtA[offset+6] = uint8(v >> 48)
dAtA[offset+7] = uint8(v >> 56)
return offset + 8
}
func encodeFixed32Generated(dAtA []byte, offset int, v uint32) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
return offset + 4
}
func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
}
func (m *RawExtension) Size() (n int) {
var l int
_ = l
if m.Raw != nil {
l = len(m.Raw)
n += 1 + l + sovGenerated(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *TypeMeta) Size() (n int) {
var l int
_ = l
if m.ApiVersion != nil {
l = len(*m.ApiVersion)
n += 1 + l + sovGenerated(uint64(l))
}
if m.Kind != nil {
l = len(*m.Kind)
n += 1 + l + sovGenerated(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *Unknown) Size() (n int) {
var l int
_ = l
if m.TypeMeta != nil {
l = m.TypeMeta.Size()
n += 1 + l + sovGenerated(uint64(l))
}
if m.Raw != nil {
l = len(m.Raw)
n += 1 + l + sovGenerated(uint64(l))
}
if m.ContentEncoding != nil {
l = len(*m.ContentEncoding)
n += 1 + l + sovGenerated(uint64(l))
}
if m.ContentType != nil {
l = len(*m.ContentType)
n += 1 + l + sovGenerated(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovGenerated(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
}
func sozGenerated(x uint64) (n int) {
return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *RawExtension) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RawExtension: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RawExtension: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Raw", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + byteLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Raw = append(m.Raw[:0], dAtA[iNdEx:postIndex]...)
if m.Raw == nil {
m.Raw = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *TypeMeta) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: TypeMeta: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: TypeMeta: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ApiVersion", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := string(dAtA[iNdEx:postIndex])
m.ApiVersion = &s
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := string(dAtA[iNdEx:postIndex])
m.Kind = &s
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Unknown) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Unknown: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Unknown: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field TypeMeta", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.TypeMeta == nil {
m.TypeMeta = &TypeMeta{}
}
if err := m.TypeMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Raw", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + byteLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Raw = append(m.Raw[:0], dAtA[iNdEx:postIndex]...)
if m.Raw == nil {
m.Raw = []byte{}
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ContentEncoding", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := string(dAtA[iNdEx:postIndex])
m.ContentEncoding = &s
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ContentType", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := string(dAtA[iNdEx:postIndex])
m.ContentType = &s
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipGenerated(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthGenerated
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipGenerated(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
}
return iNdEx, nil
case 4:
return iNdEx, nil
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
}
panic("unreachable")
}
var (
ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
)
func init() {
proto.RegisterFile("github.com/ericchiang/k8s/runtime/generated.proto", fileDescriptorGenerated)
}
var fileDescriptorGenerated = []byte{
// 275 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x7c, 0x8f, 0xc1, 0x4a, 0xc3, 0x40,
0x10, 0x86, 0xdd, 0xb6, 0xd0, 0x3a, 0x2d, 0x28, 0x7b, 0x8a, 0x82, 0x21, 0xe4, 0x62, 0x2f, 0x6e,
0xd0, 0x93, 0x27, 0x0f, 0x4a, 0x8f, 0x5e, 0x82, 0x7a, 0xf0, 0x16, 0x9b, 0x21, 0x2c, 0xab, 0xb3,
0x61, 0x33, 0x21, 0xfa, 0x26, 0x3e, 0x84, 0x0f, 0xe2, 0xd1, 0x47, 0x90, 0xf8, 0x22, 0x92, 0x35,
0xad, 0xa5, 0x88, 0xb7, 0xe1, 0xdf, 0x6f, 0xfe, 0xfd, 0x06, 0x4e, 0xcc, 0x79, 0xa5, 0xb4, 0x4d,
0x4c, 0xfd, 0x80, 0x8e, 0x90, 0xb1, 0x4a, 0x4a, 0x53, 0x24, 0xae, 0x26, 0xd6, 0x4f, 0x98, 0x14,
0x48, 0xe8, 0x32, 0xc6, 0x5c, 0x95, 0xce, 0xb2, 0x95, 0x47, 0x3f, 0xb8, 0xfa, 0xc5, 0x55, 0x69,
0x0a, 0xd5, 0xe3, 0x87, 0xa7, 0x7f, 0xb7, 0xd5, 0xac, 0x1f, 0x13, 0x4d, 0x5c, 0xb1, 0xdb, 0x6e,
0x8c, 0x23, 0x98, 0xa5, 0x59, 0xb3, 0x78, 0x66, 0xa4, 0x4a, 0x5b, 0x92, 0xfb, 0x30, 0x74, 0x59,
0x13, 0x88, 0x48, 0xcc, 0x67, 0x69, 0x37, 0xc6, 0x17, 0x30, 0xb9, 0x79, 0x29, 0xf1, 0x1a, 0x39,
0x93, 0x21, 0x40, 0x56, 0xea, 0x3b, 0x74, 0x1d, 0xeb, 0xa1, 0xdd, 0x74, 0x23, 0x91, 0x12, 0x46,
0x46, 0x53, 0x1e, 0x0c, 0xfc, 0x8b, 0x9f, 0xe3, 0x37, 0x01, 0xe3, 0x5b, 0x32, 0x64, 0x1b, 0x92,
0x57, 0x30, 0xe1, 0xbe, 0xcb, 0x6f, 0x4f, 0xcf, 0x8e, 0xd5, 0xbf, 0x27, 0xa9, 0xd5, 0xd7, 0xe9,
0x7a, 0x71, 0xa5, 0x38, 0x58, 0x2b, 0xca, 0x39, 0xec, 0x2d, 0x2d, 0x31, 0x12, 0x2f, 0x68, 0x69,
0x73, 0x4d, 0x45, 0x30, 0xf4, 0x06, 0xdb, 0xb1, 0x8c, 0x60, 0xda, 0x47, 0x5d, 0x71, 0x30, 0xf2,
0xd4, 0x66, 0x74, 0x79, 0xf0, 0xde, 0x86, 0xe2, 0xa3, 0x0d, 0xc5, 0x67, 0x1b, 0x8a, 0xd7, 0xaf,
0x70, 0xe7, 0x7e, 0xdc, 0xbb, 0x7c, 0x07, 0x00, 0x00, 0xff, 0xff, 0x56, 0xe9, 0xf9, 0xae, 0xad,
0x01, 0x00, 0x00,
}

View file

@ -1,46 +0,0 @@
// Code generated by protoc-gen-gogo.
// source: k8s.io/kubernetes/pkg/runtime/schema/generated.proto
// DO NOT EDIT!
/*
Package schema is a generated protocol buffer package.
It is generated from these files:
k8s.io/kubernetes/pkg/runtime/schema/generated.proto
It has these top-level messages:
*/
package schema
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/ericchiang/k8s/util/intstr"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
func init() {
proto.RegisterFile("github.com/ericchiang/k8s/runtime/schema/generated.proto", fileDescriptorGenerated)
}
var fileDescriptorGenerated = []byte{
// 136 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x32, 0xc9, 0xb6, 0x28, 0xd6,
0xcb, 0xcc, 0xd7, 0xcf, 0x2e, 0x4d, 0x4a, 0x2d, 0xca, 0x4b, 0x2d, 0x49, 0x2d, 0xd6, 0x2f, 0xc8,
0x4e, 0xd7, 0x2f, 0x2a, 0xcd, 0x2b, 0xc9, 0xcc, 0x4d, 0xd5, 0x2f, 0x4e, 0xce, 0x48, 0xcd, 0x4d,
0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0x4a, 0x2c, 0x49, 0x4d, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9,
0x17, 0x52, 0x81, 0xe8, 0xd2, 0x43, 0xe8, 0xd2, 0x2b, 0xc8, 0x4e, 0xd7, 0x83, 0xea, 0xd2, 0x83,
0xe8, 0x92, 0x32, 0xc4, 0x6e, 0x76, 0x69, 0x49, 0x66, 0x8e, 0x7e, 0x66, 0x5e, 0x49, 0x71, 0x49,
0x11, 0xba, 0xc1, 0x4e, 0x12, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91,
0x1c, 0xe3, 0x8c, 0xc7, 0x72, 0x0c, 0x51, 0x6c, 0x10, 0xc3, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff,
0xea, 0x33, 0x0e, 0xbb, 0xa9, 0x00, 0x00, 0x00,
}

View file

@ -1,166 +0,0 @@
package k8s
import (
"context"
"errors"
"github.com/ericchiang/k8s/api/v1"
metav1 "github.com/ericchiang/k8s/apis/meta/v1"
)
// ThirdPartyResources is a client used for interacting with user
// defined API groups. It uses JSON encoding instead of protobufs
// which are unsupported for these APIs.
//
// Users are expected to define their own third party resources.
//
// const metricsResource = "metrics"
//
// // First, define a third party resources with TypeMeta
// // and ObjectMeta fields.
// type Metric struct {
// *unversioned.TypeMeta `json:",inline"`
// *v1.ObjectMeta `json:"metadata,omitempty"`
//
// Timestamp time.Time `json:"timestamp"`
// Value []byte `json:"value"`
// }
//
// // Define a list wrapper.
// type MetricsList struct {
// *unversioned.TypeMeta `json:",inline"`
// *unversioned.ListMeta `json:"metadata,omitempty"`
//
// Items []Metric `json:"items"`
// }
//
// Register the new resource by creating a ThirdPartyResource type.
//
// // Create a ThirdPartyResource
// tpr := &v1beta1.ThirdPartyResource{
// Metadata: &v1.ObjectMeta{
// Name: k8s.String("metric.metrics.example.com"),
// },
// Description: k8s.String("A custom third party resource"),
// Versions: []*v1beta1.APIVersion{
// {Name: k8s.String("v1")},
// },
// }
// _, err := client.ExtensionsV1Beta1().CreateThirdPartyResource(ctx, trp)
// if err != nil {
// // handle error
// }
//
// After creating the resource type, create a ThirdPartyResources client then
// use interact with it like any other API group. For example to create a third
// party resource:
//
// metricsClient := client.ThirdPartyResources("metrics.example.com", "v1")
//
// metric := &Metric{
// ObjectMeta: &v1.ObjectMeta{
// Name: k8s.String("foo"),
// },
// Timestamp: time.Now(),
// Value: 42,
// }
//
// err = metricsClient.Create(ctx, metricsResource, client.Namespace, metric, metric)
// if err != nil {
// // handle error
// }
//
// List a set of third party resources:
//
// var metrics MetricsList
// metricsClient.List(ctx, metricsResource, &metrics)
//
// Or delete:
//
// tprClient.Delete(ctx, metricsResource, client.Namespace, *metric.Name)
//
type ThirdPartyResources struct {
c *Client
apiGroup string
apiVersion string
}
// ThirdPartyResources returns a client for interacting with a ThirdPartyResource
// API group.
func (c *Client) ThirdPartyResources(apiGroup, apiVersion string) *ThirdPartyResources {
return &ThirdPartyResources{c, apiGroup, apiVersion}
}
func checkResource(apiGroup, apiVersion, resource, namespace, name string) error {
if apiGroup == "" {
return errors.New("no api group provided")
}
if apiVersion == "" {
return errors.New("no api version provided")
}
if resource == "" {
return errors.New("no resource version provided")
}
if namespace == "" {
return errors.New("no namespace provided")
}
if name == "" {
return errors.New("no resource name provided")
}
return nil
}
// object and after16Object are used by go/types to detect types that are likely
// to be Kubernetes resources. Types that implement this resources are likely
// resource.
//
// They're defined here but only used in gen.go.
type object interface {
GetMetadata() *v1.ObjectMeta
}
// after16Object uses the new ObjectMeta's home.
type after16Object interface {
GetMetadata() *metav1.ObjectMeta
}
func (t *ThirdPartyResources) Create(ctx context.Context, resource, namespace string, req, resp interface{}) error {
if err := checkResource(t.apiGroup, t.apiVersion, resource, namespace, "not required"); err != nil {
return err
}
url := t.c.urlFor(t.apiGroup, t.apiVersion, namespace, resource, "")
return t.c.create(ctx, jsonCodec, "POST", url, req, resp)
}
func (t *ThirdPartyResources) Update(ctx context.Context, resource, namespace, name string, req, resp interface{}) error {
if err := checkResource(t.apiGroup, t.apiVersion, resource, namespace, "not required"); err != nil {
return err
}
url := t.c.urlFor(t.apiGroup, t.apiVersion, namespace, resource, name)
return t.c.create(ctx, jsonCodec, "PUT", url, req, resp)
}
func (t *ThirdPartyResources) Get(ctx context.Context, resource, namespace, name string, resp interface{}) error {
if err := checkResource(t.apiGroup, t.apiVersion, resource, namespace, name); err != nil {
return err
}
url := t.c.urlFor(t.apiGroup, t.apiVersion, namespace, resource, name)
return t.c.get(ctx, jsonCodec, url, resp)
}
func (t *ThirdPartyResources) Delete(ctx context.Context, resource, namespace, name string) error {
if err := checkResource(t.apiGroup, t.apiVersion, resource, namespace, name); err != nil {
return err
}
url := t.c.urlFor(t.apiGroup, t.apiVersion, namespace, resource, name)
return t.c.delete(ctx, jsonCodec, url)
}
func (t *ThirdPartyResources) List(ctx context.Context, resource, namespace string, resp interface{}) error {
if err := checkResource(t.apiGroup, t.apiVersion, resource, namespace, "name not required"); err != nil {
return err
}
url := t.c.urlFor(t.apiGroup, t.apiVersion, namespace, resource, "")
return t.c.get(ctx, jsonCodec, url, resp)
}

View file

@ -1,74 +0,0 @@
package k8s
import (
"context"
"net/http"
"testing"
"time"
"github.com/ericchiang/k8s/api/unversioned"
"github.com/ericchiang/k8s/apis/extensions/v1beta1"
metav1 "github.com/ericchiang/k8s/apis/meta/v1"
)
func TestTPRs(t *testing.T) {
client := newTestClient(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
type Metric struct {
*unversioned.TypeMeta `json:",inline"`
*metav1.ObjectMeta `json:"metadata,omitempty"`
Timestamp time.Time `json:"timestamp"`
Value int64 `json:"value"`
}
type MetricsList struct {
*unversioned.TypeMeta `json:",inline"`
*unversioned.ListMeta `json:"metadata,omitempty"`
Items []Metric `json:"items"`
}
// Create a ThirdPartyResource
tpr := &v1beta1.ThirdPartyResource{
Metadata: &metav1.ObjectMeta{
Name: String("metric.example.com"),
},
Description: String("A value and a timestamp"),
Versions: []*v1beta1.APIVersion{
{Name: String("v1")},
},
}
_, err := client.ExtensionsV1Beta1().CreateThirdPartyResource(ctx, tpr)
if err != nil {
if apiErr, ok := err.(*APIError); !ok || apiErr.Code != http.StatusConflict {
t.Fatalf("create third party resource: %v", err)
}
}
metric := &Metric{
ObjectMeta: &metav1.ObjectMeta{
Name: String("foo"),
},
Timestamp: time.Now(),
Value: 42,
}
myClient := client.ThirdPartyResources("example.com", "v1")
err = myClient.Create(ctx, "metrics", "default", metric, metric)
if err != nil {
t.Errorf("create third party resource: %v", err)
}
var metrics MetricsList
if err := myClient.List(ctx, "metrics", "default", &metrics); err != nil {
t.Errorf("list third party resource: %v", err)
}
if err := myClient.Delete(ctx, "metrics", "default", "foo"); err != nil {
t.Fatalf("delete tpr: %v", err)
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,418 +0,0 @@
// Code generated by protoc-gen-gogo.
// source: k8s.io/kubernetes/pkg/util/intstr/generated.proto
// DO NOT EDIT!
/*
Package intstr is a generated protocol buffer package.
It is generated from these files:
k8s.io/kubernetes/pkg/util/intstr/generated.proto
It has these top-level messages:
IntOrString
*/
package intstr
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import io "io"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// IntOrString is a type that can hold an int32 or a string. When used in
// JSON or YAML marshalling and unmarshalling, it produces or consumes the
// inner type. This allows you to have, for example, a JSON field that can
// accept a name or number.
// TODO: Rename to Int32OrString
//
// +protobuf=true
// +protobuf.options.(gogoproto.goproto_stringer)=false
// +k8s:openapi-gen=true
type IntOrString struct {
Type *int64 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"`
IntVal *int32 `protobuf:"varint,2,opt,name=intVal" json:"intVal,omitempty"`
StrVal *string `protobuf:"bytes,3,opt,name=strVal" json:"strVal,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *IntOrString) Reset() { *m = IntOrString{} }
func (m *IntOrString) String() string { return proto.CompactTextString(m) }
func (*IntOrString) ProtoMessage() {}
func (*IntOrString) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} }
func (m *IntOrString) GetType() int64 {
if m != nil && m.Type != nil {
return *m.Type
}
return 0
}
func (m *IntOrString) GetIntVal() int32 {
if m != nil && m.IntVal != nil {
return *m.IntVal
}
return 0
}
func (m *IntOrString) GetStrVal() string {
if m != nil && m.StrVal != nil {
return *m.StrVal
}
return ""
}
func init() {
proto.RegisterType((*IntOrString)(nil), "github.com/ericchiang.k8s.util.intstr.IntOrString")
}
func (m *IntOrString) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *IntOrString) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.Type != nil {
dAtA[i] = 0x8
i++
i = encodeVarintGenerated(dAtA, i, uint64(*m.Type))
}
if m.IntVal != nil {
dAtA[i] = 0x10
i++
i = encodeVarintGenerated(dAtA, i, uint64(*m.IntVal))
}
if m.StrVal != nil {
dAtA[i] = 0x1a
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.StrVal)))
i += copy(dAtA[i:], *m.StrVal)
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func encodeFixed64Generated(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
dAtA[offset+4] = uint8(v >> 32)
dAtA[offset+5] = uint8(v >> 40)
dAtA[offset+6] = uint8(v >> 48)
dAtA[offset+7] = uint8(v >> 56)
return offset + 8
}
func encodeFixed32Generated(dAtA []byte, offset int, v uint32) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
return offset + 4
}
func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
}
func (m *IntOrString) Size() (n int) {
var l int
_ = l
if m.Type != nil {
n += 1 + sovGenerated(uint64(*m.Type))
}
if m.IntVal != nil {
n += 1 + sovGenerated(uint64(*m.IntVal))
}
if m.StrVal != nil {
l = len(*m.StrVal)
n += 1 + l + sovGenerated(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovGenerated(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
}
func sozGenerated(x uint64) (n int) {
return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *IntOrString) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: IntOrString: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: IntOrString: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
}
var v int64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
m.Type = &v
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field IntVal", wireType)
}
var v int32
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= (int32(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
m.IntVal = &v
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field StrVal", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := string(dAtA[iNdEx:postIndex])
m.StrVal = &s
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipGenerated(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthGenerated
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipGenerated(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
}
return iNdEx, nil
case 4:
return iNdEx, nil
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
}
panic("unreachable")
}
var (
ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
)
func init() {
proto.RegisterFile("github.com/ericchiang/k8s/util/intstr/generated.proto", fileDescriptorGenerated)
}
var fileDescriptorGenerated = []byte{
// 180 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x32, 0xcc, 0xb6, 0x28, 0xd6,
0xcb, 0xcc, 0xd7, 0xcf, 0x2e, 0x4d, 0x4a, 0x2d, 0xca, 0x4b, 0x2d, 0x49, 0x2d, 0xd6, 0x2f, 0xc8,
0x4e, 0xd7, 0x2f, 0x2d, 0xc9, 0xcc, 0xd1, 0xcf, 0xcc, 0x2b, 0x29, 0x2e, 0x29, 0xd2, 0x4f, 0x4f,
0xcd, 0x4b, 0x2d, 0x4a, 0x2c, 0x49, 0x4d, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x52, 0x84,
0x68, 0xd1, 0x43, 0x68, 0xd1, 0x2b, 0xc8, 0x4e, 0xd7, 0x03, 0x69, 0xd1, 0x83, 0x68, 0x51, 0x0a,
0xe4, 0xe2, 0xf6, 0xcc, 0x2b, 0xf1, 0x2f, 0x0a, 0x2e, 0x29, 0xca, 0xcc, 0x4b, 0x17, 0x12, 0xe2,
0x62, 0x29, 0xa9, 0x2c, 0x48, 0x95, 0x60, 0x54, 0x60, 0xd4, 0x60, 0x0e, 0x02, 0xb3, 0x85, 0xc4,
0xb8, 0xd8, 0x32, 0xf3, 0x4a, 0xc2, 0x12, 0x73, 0x24, 0x98, 0x14, 0x18, 0x35, 0x58, 0x83, 0xa0,
0x3c, 0x90, 0x78, 0x71, 0x49, 0x11, 0x48, 0x9c, 0x59, 0x81, 0x51, 0x83, 0x33, 0x08, 0xca, 0x73,
0x92, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x67, 0x3c,
0x96, 0x63, 0x88, 0x62, 0x83, 0x58, 0x06, 0x08, 0x00, 0x00, 0xff, 0xff, 0xcd, 0x20, 0xf2, 0x02,
0xc3, 0x00, 0x00, 0x00,
}

View file

@ -1,405 +0,0 @@
// Code generated by protoc-gen-gogo.
// source: k8s.io/kubernetes/pkg/watch/versioned/generated.proto
// DO NOT EDIT!
/*
Package versioned is a generated protocol buffer package.
It is generated from these files:
k8s.io/kubernetes/pkg/watch/versioned/generated.proto
It has these top-level messages:
Event
*/
package versioned
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import k8s_io_kubernetes_pkg_runtime "github.com/ericchiang/k8s/runtime"
import _ "github.com/ericchiang/k8s/util/intstr"
import io "io"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// Event represents a single event to a watched resource.
//
// +protobuf=true
// +k8s:openapi-gen=true
type Event struct {
Type *string `protobuf:"bytes,1,opt,name=type" json:"type,omitempty"`
// Object is:
// * If Type is Added or Modified: the new state of the object.
// * If Type is Deleted: the state of the object immediately before deletion.
// * If Type is Error: *api.Status is recommended; other types may make sense
// depending on context.
Object *k8s_io_kubernetes_pkg_runtime.RawExtension `protobuf:"bytes,2,opt,name=object" json:"object,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Event) Reset() { *m = Event{} }
func (m *Event) String() string { return proto.CompactTextString(m) }
func (*Event) ProtoMessage() {}
func (*Event) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} }
func (m *Event) GetType() string {
if m != nil && m.Type != nil {
return *m.Type
}
return ""
}
func (m *Event) GetObject() *k8s_io_kubernetes_pkg_runtime.RawExtension {
if m != nil {
return m.Object
}
return nil
}
func init() {
proto.RegisterType((*Event)(nil), "github.com/ericchiang.k8s.watch.versioned.Event")
}
func (m *Event) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Event) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.Type != nil {
dAtA[i] = 0xa
i++
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.Type)))
i += copy(dAtA[i:], *m.Type)
}
if m.Object != nil {
dAtA[i] = 0x12
i++
i = encodeVarintGenerated(dAtA, i, uint64(m.Object.Size()))
n1, err := m.Object.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n1
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func encodeFixed64Generated(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
dAtA[offset+4] = uint8(v >> 32)
dAtA[offset+5] = uint8(v >> 40)
dAtA[offset+6] = uint8(v >> 48)
dAtA[offset+7] = uint8(v >> 56)
return offset + 8
}
func encodeFixed32Generated(dAtA []byte, offset int, v uint32) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
return offset + 4
}
func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
}
func (m *Event) Size() (n int) {
var l int
_ = l
if m.Type != nil {
l = len(*m.Type)
n += 1 + l + sovGenerated(uint64(l))
}
if m.Object != nil {
l = m.Object.Size()
n += 1 + l + sovGenerated(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovGenerated(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
}
func sozGenerated(x uint64) (n int) {
return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *Event) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Event: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Event: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := string(dAtA[iNdEx:postIndex])
m.Type = &s
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Object", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Object == nil {
m.Object = &k8s_io_kubernetes_pkg_runtime.RawExtension{}
}
if err := m.Object.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipGenerated(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthGenerated
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipGenerated(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
}
return iNdEx, nil
case 4:
return iNdEx, nil
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
}
panic("unreachable")
}
var (
ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
)
func init() {
proto.RegisterFile("github.com/ericchiang/k8s/watch/versioned/generated.proto", fileDescriptorGenerated)
}
var fileDescriptorGenerated = []byte{
// 214 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x32, 0xcd, 0xb6, 0x28, 0xd6,
0xcb, 0xcc, 0xd7, 0xcf, 0x2e, 0x4d, 0x4a, 0x2d, 0xca, 0x4b, 0x2d, 0x49, 0x2d, 0xd6, 0x2f, 0xc8,
0x4e, 0xd7, 0x2f, 0x4f, 0x2c, 0x49, 0xce, 0xd0, 0x2f, 0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0x4b,
0x4d, 0xd1, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0x4a, 0x2c, 0x49, 0x4d, 0xd1, 0x2b, 0x28, 0xca, 0x2f,
0xc9, 0x17, 0x52, 0x85, 0x68, 0xd3, 0x43, 0x68, 0xd3, 0x2b, 0xc8, 0x4e, 0xd7, 0x03, 0x6b, 0xd3,
0x83, 0x6b, 0x93, 0xd2, 0xc5, 0x6e, 0x7a, 0x51, 0x69, 0x5e, 0x49, 0x66, 0x6e, 0x2a, 0xba, 0xa9,
0x52, 0x86, 0xd8, 0x95, 0x97, 0x96, 0x64, 0xe6, 0xe8, 0x67, 0xe6, 0x95, 0x14, 0x97, 0x14, 0xa1,
0x6b, 0x51, 0x4a, 0xe0, 0x62, 0x75, 0x2d, 0x4b, 0xcd, 0x2b, 0x11, 0x12, 0xe2, 0x62, 0x29, 0xa9,
0x2c, 0x48, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0xb3, 0x85, 0x9c, 0xb9, 0xd8, 0xf2,
0x93, 0xb2, 0x52, 0x93, 0x4b, 0x24, 0x98, 0x14, 0x18, 0x35, 0xb8, 0x8d, 0xb4, 0xf5, 0xb0, 0x3b,
0x1b, 0xea, 0x1e, 0xbd, 0xa0, 0xc4, 0x72, 0xd7, 0x8a, 0x92, 0xd4, 0x3c, 0x90, 0xeb, 0x83, 0xa0,
0x5a, 0x9d, 0xa4, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6,
0x19, 0x8f, 0xe5, 0x18, 0xa2, 0x38, 0xe1, 0x1e, 0x04, 0x04, 0x00, 0x00, 0xff, 0xff, 0xb4, 0x05,
0x8c, 0x51, 0x3f, 0x01, 0x00, 0x00,
}

6
vendor/github.com/kylelemons/godebug/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,6 @@
language: go
go:
- 1.7
- 1.8
- tip

64
vendor/github.com/kylelemons/godebug/README.md generated vendored Normal file
View file

@ -0,0 +1,64 @@
Pretty Printing for Go
======================
[![godebug build status][ciimg]][ci]
Have you ever wanted to get a pretty-printed version of a Go data structure,
complete with indentation? I have found this especially useful in unit tests
and in debugging my code, and thus godebug was born!
[ciimg]: https://travis-ci.org/kylelemons/godebug.svg?branch=master
[ci]: https://travis-ci.org/kylelemons/godebug
Quick Examples
--------------
By default, pretty will write out a very compact representation of a data structure.
From the [Print example][printex]:
```
{Name: "Spaceship Heart of Gold",
Crew: {Arthur Dent: "Along for the Ride",
Ford Prefect: "A Hoopy Frood",
Trillian: "Human",
Zaphod Beeblebrox: "Galactic President"},
Androids: 1,
Stolen: true}
```
It can also produce a much more verbose, one-item-per-line representation suitable for
[computing diffs][diffex]. See the documentation for more examples and customization.
[printex]: https://godoc.org/github.com/kylelemons/godebug/pretty#example-Print
[diffex]: https://godoc.org/github.com/kylelemons/godebug/pretty#example-Compare
Documentation
-------------
Documentation for this package is available at [godoc.org][doc]:
* Pretty: [![godoc for godebug/pretty][prettyimg]][prettydoc]
* Diff: [![godoc for godebug/diff][diffimg]][diffdoc]
[doc]: https://godoc.org/
[prettyimg]: https://godoc.org/github.com/kylelemons/godebug/pretty?status.png
[prettydoc]: https://godoc.org/github.com/kylelemons/godebug/pretty
[diffimg]: https://godoc.org/github.com/kylelemons/godebug/diff?status.png
[diffdoc]: https://godoc.org/github.com/kylelemons/godebug/diff
Installation
------------
These packages are available via `go get`:
```bash
$ go get -u github.com/kylelemons/godebug/{pretty,diff}
```
Other Packages
--------------
If `godebug/pretty` is not granular enough, I highly recommend
checking out [go-spew][spew].
[spew]: http://godoc.org/github.com/davecgh/go-spew/spew

186
vendor/github.com/kylelemons/godebug/diff/diff.go generated vendored Normal file
View file

@ -0,0 +1,186 @@
// Copyright 2013 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 diff implements a linewise diff algorithm.
package diff
import (
"bytes"
"fmt"
"strings"
)
// Chunk represents a piece of the diff. A chunk will not have both added and
// deleted lines. Equal lines are always after any added or deleted lines.
// A Chunk may or may not have any lines in it, especially for the first or last
// chunk in a computation.
type Chunk struct {
Added []string
Deleted []string
Equal []string
}
func (c *Chunk) empty() bool {
return len(c.Added) == 0 && len(c.Deleted) == 0 && len(c.Equal) == 0
}
// Diff returns a string containing a line-by-line unified diff of the linewise
// changes required to make A into B. Each line is prefixed with '+', '-', or
// ' ' to indicate if it should be added, removed, or is correct respectively.
func Diff(A, B string) string {
aLines := strings.Split(A, "\n")
bLines := strings.Split(B, "\n")
chunks := DiffChunks(aLines, bLines)
buf := new(bytes.Buffer)
for _, c := range chunks {
for _, line := range c.Added {
fmt.Fprintf(buf, "+%s\n", line)
}
for _, line := range c.Deleted {
fmt.Fprintf(buf, "-%s\n", line)
}
for _, line := range c.Equal {
fmt.Fprintf(buf, " %s\n", line)
}
}
return strings.TrimRight(buf.String(), "\n")
}
// DiffChunks uses an O(D(N+M)) shortest-edit-script algorithm
// to compute the edits required from A to B and returns the
// edit chunks.
func DiffChunks(a, b []string) []Chunk {
// algorithm: http://www.xmailserver.org/diff2.pdf
// We'll need these quantities a lot.
alen, blen := len(a), len(b) // M, N
// At most, it will require len(a) deletions and len(b) additions
// to transform a into b.
maxPath := alen + blen // MAX
if maxPath == 0 {
// degenerate case: two empty lists are the same
return nil
}
// Store the endpoint of the path for diagonals.
// We store only the a index, because the b index on any diagonal
// (which we know during the loop below) is aidx-diag.
// endpoint[maxPath] represents the 0 diagonal.
//
// Stated differently:
// endpoint[d] contains the aidx of a furthest reaching path in diagonal d
endpoint := make([]int, 2*maxPath+1) // V
saved := make([][]int, 0, 8) // Vs
save := func() {
dup := make([]int, len(endpoint))
copy(dup, endpoint)
saved = append(saved, dup)
}
var editDistance int // D
dLoop:
for editDistance = 0; editDistance <= maxPath; editDistance++ {
// The 0 diag(onal) represents equality of a and b. Each diagonal to
// the left is numbered one lower, to the right is one higher, from
// -alen to +blen. Negative diagonals favor differences from a,
// positive diagonals favor differences from b. The edit distance to a
// diagonal d cannot be shorter than d itself.
//
// The iterations of this loop cover either odds or evens, but not both,
// If odd indices are inputs, even indices are outputs and vice versa.
for diag := -editDistance; diag <= editDistance; diag += 2 { // k
var aidx int // x
switch {
case diag == -editDistance:
// This is a new diagonal; copy from previous iter
aidx = endpoint[maxPath-editDistance+1] + 0
case diag == editDistance:
// This is a new diagonal; copy from previous iter
aidx = endpoint[maxPath+editDistance-1] + 1
case endpoint[maxPath+diag+1] > endpoint[maxPath+diag-1]:
// diagonal d+1 was farther along, so use that
aidx = endpoint[maxPath+diag+1] + 0
default:
// diagonal d-1 was farther (or the same), so use that
aidx = endpoint[maxPath+diag-1] + 1
}
// On diagonal d, we can compute bidx from aidx.
bidx := aidx - diag // y
// See how far we can go on this diagonal before we find a difference.
for aidx < alen && bidx < blen && a[aidx] == b[bidx] {
aidx++
bidx++
}
// Store the end of the current edit chain.
endpoint[maxPath+diag] = aidx
// If we've found the end of both inputs, we're done!
if aidx >= alen && bidx >= blen {
save() // save the final path
break dLoop
}
}
save() // save the current path
}
if editDistance == 0 {
return nil
}
chunks := make([]Chunk, editDistance+1)
x, y := alen, blen
for d := editDistance; d > 0; d-- {
endpoint := saved[d]
diag := x - y
insert := diag == -d || (diag != d && endpoint[maxPath+diag-1] < endpoint[maxPath+diag+1])
x1 := endpoint[maxPath+diag]
var x0, xM, kk int
if insert {
kk = diag + 1
x0 = endpoint[maxPath+kk]
xM = x0
} else {
kk = diag - 1
x0 = endpoint[maxPath+kk]
xM = x0 + 1
}
y0 := x0 - kk
var c Chunk
if insert {
c.Added = b[y0:][:1]
} else {
c.Deleted = a[x0:][:1]
}
if xM < x1 {
c.Equal = a[xM:][:x1-xM]
}
x, y = x0, y0
chunks[d] = c
}
if x > 0 {
chunks[0].Equal = a[:x]
}
if chunks[0].empty() {
chunks = chunks[1:]
}
if len(chunks) == 0 {
return nil
}
return chunks
}

166
vendor/github.com/kylelemons/godebug/diff/diff_test.go generated vendored Normal file
View file

@ -0,0 +1,166 @@
// Copyright 2013 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 diff
import (
"fmt"
"reflect"
"strings"
"testing"
)
func TestDiff(t *testing.T) {
tests := []struct {
desc string
A, B []string
chunks []Chunk
}{
{
desc: "nil",
},
{
desc: "empty",
A: []string{},
B: []string{},
},
{
desc: "same",
A: []string{"foo"},
B: []string{"foo"},
},
{
desc: "a empty",
A: []string{},
},
{
desc: "b empty",
B: []string{},
},
{
desc: "b nil",
A: []string{"foo"},
chunks: []Chunk{
0: {Deleted: []string{"foo"}},
},
},
{
desc: "a nil",
B: []string{"foo"},
chunks: []Chunk{
0: {Added: []string{"foo"}},
},
},
{
desc: "start with change",
A: []string{"a", "b", "c"},
B: []string{"A", "b", "c"},
chunks: []Chunk{
0: {Deleted: []string{"a"}},
1: {Added: []string{"A"}, Equal: []string{"b", "c"}},
},
},
{
desc: "constitution",
A: []string{
"We the People of the United States, in Order to form a more perfect Union,",
"establish Justice, insure domestic Tranquility, provide for the common defence,",
"and secure the Blessings of Liberty to ourselves",
"and our Posterity, do ordain and establish this Constitution for the United",
"States of America.",
},
B: []string{
"We the People of the United States, in Order to form a more perfect Union,",
"establish Justice, insure domestic Tranquility, provide for the common defence,",
"promote the general Welfare, and secure the Blessings of Liberty to ourselves",
"and our Posterity, do ordain and establish this Constitution for the United",
"States of America.",
},
chunks: []Chunk{
0: {
Equal: []string{
"We the People of the United States, in Order to form a more perfect Union,",
"establish Justice, insure domestic Tranquility, provide for the common defence,",
},
},
1: {
Deleted: []string{
"and secure the Blessings of Liberty to ourselves",
},
},
2: {
Added: []string{
"promote the general Welfare, and secure the Blessings of Liberty to ourselves",
},
Equal: []string{
"and our Posterity, do ordain and establish this Constitution for the United",
"States of America.",
},
},
},
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
got := DiffChunks(test.A, test.B)
if got, want := len(got), len(test.chunks); got != want {
t.Errorf("edit distance = %v, want %v", got-1, want-1)
return
}
for i := range got {
got, want := got[i], test.chunks[i]
if got, want := got.Added, want.Added; !reflect.DeepEqual(got, want) {
t.Errorf("chunks[%d]: Added = %v, want %v", i, got, want)
}
if got, want := got.Deleted, want.Deleted; !reflect.DeepEqual(got, want) {
t.Errorf("chunks[%d]: Deleted = %v, want %v", i, got, want)
}
if got, want := got.Equal, want.Equal; !reflect.DeepEqual(got, want) {
t.Errorf("chunks[%d]: Equal = %v, want %v", i, got, want)
}
}
})
}
}
func ExampleDiff() {
constitution := strings.TrimSpace(`
We the People of the United States, in Order to form a more perfect Union,
establish Justice, insure domestic Tranquility, provide for the common defence,
promote the general Welfare, and secure the Blessings of Liberty to ourselves
and our Posterity, do ordain and establish this Constitution for the United
States of America.
`)
got := strings.TrimSpace(`
:wq
We the People of the United States, in Order to form a more perfect Union,
establish Justice, insure domestic Tranquility, provide for the common defence,
and secure the Blessings of Liberty to ourselves
and our Posterity, do ordain and establish this Constitution for the United
States of America.
`)
fmt.Println(Diff(got, constitution))
// Output:
// -:wq
// We the People of the United States, in Order to form a more perfect Union,
// establish Justice, insure domestic Tranquility, provide for the common defence,
// -and secure the Blessings of Liberty to ourselves
// +promote the general Welfare, and secure the Blessings of Liberty to ourselves
// and our Posterity, do ordain and establish this Constitution for the United
// States of America.
}

View file

@ -0,0 +1,5 @@
*.test
*.bench
*.golden
*.txt
*.prof

25
vendor/github.com/kylelemons/godebug/pretty/doc.go generated vendored Normal file
View file

@ -0,0 +1,25 @@
// Copyright 2013 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 pretty pretty-prints Go structures.
//
// This package uses reflection to examine a Go value and can
// print out in a nice, aligned fashion. It supports three
// modes (normal, compact, and extended) for advanced use.
//
// See the Reflect and Print examples for what the output looks like.
package pretty
// TODO:
// - Catch cycles

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