From b229d56eeea53e485b3aabed61c1b31fc68a566a Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Sat, 6 Jul 2019 00:44:52 +0000 Subject: [PATCH] Wish i could get this to be inline --- Makefile | 23 +- cmd/manager/main.go | 2 +- cmd/troubleshoot/cli/collect.go | 32 + cmd/troubleshoot/cli/root.go | 39 ++ cmd/troubleshoot/main.go | 7 + ...roubleshoot.replicated.com_collectors.yaml | 589 ++++++++++++++++++ config/default/manager_auth_proxy_patch.yaml | 4 +- .../troubleshoot_v1beta1_collector.yaml | 22 +- go.sum | 10 + .../troubleshoot/v1beta1/collector_shared.go | 72 +++ .../troubleshoot/v1beta1/collector_types.go | 6 +- .../v1beta1/zz_generated.deepcopy.go | 238 ++++++- .../collector/collector_controller.go | 57 -- .../collector/collector_controller_test.go | 63 -- 14 files changed, 1023 insertions(+), 141 deletions(-) create mode 100644 cmd/troubleshoot/cli/collect.go create mode 100644 cmd/troubleshoot/cli/root.go create mode 100644 cmd/troubleshoot/main.go create mode 100644 pkg/apis/troubleshoot/v1beta1/collector_shared.go diff --git a/Makefile b/Makefile index dcfb84b2..c836a200 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,18 @@ all: test manager test: generate fmt vet manifests go test ./pkg/... ./cmd/... -coverprofile cover.out -# Build manager binary +.PHONY: manager manager: generate fmt vet go build -o bin/manager github.com/replicatedhq/troubleshoot/cmd/manager +.PHONY: troubleshoot +troubleshoot: generate fmt vet + go build -o bin/troubleshoot github.com/replicatedhq/troubleshoot/cmd/troubleshoot + +.PHONY: preflight +preflight: generate fmt vet + go build -o bin/preflight github.com/replicatedhq/troubleshoot/cmd/preflight + # Run against the configured Kubernetes cluster in ~/.kube/config run: generate fmt vet go run ./cmd/manager/main.go @@ -26,22 +34,21 @@ deploy: manifests kustomize build config/default | kubectl apply -f - # Generate manifests e.g. CRD, RBAC etc. -manifests: controller-gen +manifests: controller-gen paths=./pkg/apis/... -# Run go fmt against code +.PHONY: fmt fmt: go fmt ./pkg/... ./cmd/... -# Run go vet against code +.PHONY: vet vet: go vet ./pkg/... ./cmd/... - .PHONY: generate -generate: controller-gen client-gen - controller-gen object:headerFile=./hack/boilerplate.go.txt paths=./api/... - client-gen go run ../../vendor/k8s.io/code-generator/cmd/client-gen/main.go --output-package=github.com/replicatedhq/troubleshoot/pkg/client --clientset-name troubleshootclientset --input-base github.com/replicatedhq/troubleshoot/pkg/apis --input troubleshoot/v1beta1 -h ./hack/boilerplate.go.txt +generate: controller-gen # client-gen + controller-gen object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/... + # client-gen --output-package=github.com/replicatedhq/troubleshoot/pkg/client --clientset-name troubleshootclientset --input-base github.com/replicatedhq/troubleshoot/pkg/apis --input troubleshoot/v1beta1 -h ./hack/boilerplate.go.txt # Build the docker image docker-build: test diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 63721515..b792dd5a 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -32,7 +32,7 @@ import ( func main() { var metricsAddr string - flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") + flag.StringVar(&metricsAddr, "metrics-addr", ":8088", "The address the metric endpoint binds to.") flag.Parse() logf.SetLogger(logf.ZapLogger(false)) log := logf.Log.WithName("entrypoint") diff --git a/cmd/troubleshoot/cli/collect.go b/cmd/troubleshoot/cli/collect.go new file mode 100644 index 00000000..6048fe0d --- /dev/null +++ b/cmd/troubleshoot/cli/collect.go @@ -0,0 +1,32 @@ +package cli + +import ( + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func Collect() *cobra.Command { + cmd := &cobra.Command{ + Use: "collect", + Short: "collect a support bundle from a cluster", + Long: `...`, + PreRun: func(cmd *cobra.Command, args []string) { + viper.BindPFlag("collectors", cmd.Flags().Lookup("collectors")) + viper.BindPFlag("namespace", cmd.Flags().Lookup("namespace")) + viper.BindPFlag("kubecontext", cmd.Flags().Lookup("kubecontext")) + }, + RunE: func(cmd *cobra.Command, args []string) error { + + return nil + }, + } + + cmd.Flags().String("collectors", "", "name of the collectors to use") + cmd.Flags().String("namespace", "", "namespace the collectors can be found in") + + cmd.Flags().String("kubecontext", "", "the kubecontext to use when connecting") + + viper.BindPFlags(cmd.Flags()) + + return cmd +} diff --git a/cmd/troubleshoot/cli/root.go b/cmd/troubleshoot/cli/root.go new file mode 100644 index 00000000..46766564 --- /dev/null +++ b/cmd/troubleshoot/cli/root.go @@ -0,0 +1,39 @@ +package cli + +import ( + "fmt" + "os" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func RootCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "troubleshoot", + Short: "Generate and manage support bundles", + Long: `A support bundle is an archive of files, output, metrics and state +from a server that can be used to assist when troubleshooting a server.`, + SilenceUsage: true, + } + + cobra.OnInitialize(initConfig) + + cmd.AddCommand(Collect()) + + viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + return cmd +} + +func InitAndExecute() { + if err := RootCmd().Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func initConfig() { + viper.SetEnvPrefix("TROUBLESHOT") + viper.AutomaticEnv() +} diff --git a/cmd/troubleshoot/main.go b/cmd/troubleshoot/main.go new file mode 100644 index 00000000..3b6ec909 --- /dev/null +++ b/cmd/troubleshoot/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/replicatedhq/troubleshoot/cmd/troubleshoot/cli" + +func main() { + cli.InitAndExecute() +} diff --git a/config/crd/troubleshoot.replicated.com_collectors.yaml b/config/crd/troubleshoot.replicated.com_collectors.yaml index 439df71a..0ec51734 100644 --- a/config/crd/troubleshoot.replicated.com_collectors.yaml +++ b/config/crd/troubleshoot.replicated.com_collectors.yaml @@ -390,6 +390,595 @@ spec: type: string type: object spec: + properties: + collectors: + items: + properties: + kubernetes.api-versions: + properties: + defer: + type: boolean + description: + type: string + include_empty: + type: boolean + meta: + properties: + labels: + additionalProperties: + type: string + type: object + name: + type: string + type: object + output_dir: + type: string + scrub: + properties: + regex: + type: string + replace: + type: string + required: + - regex + - replace + type: object + timeout_seconds: + type: integer + type: object + kubernetes.cluster-info: + properties: + defer: + type: boolean + description: + type: string + include_empty: + type: boolean + meta: + properties: + labels: + additionalProperties: + type: string + type: object + name: + type: string + type: object + output_dir: + type: string + scrub: + properties: + regex: + type: string + replace: + type: string + required: + - regex + - replace + type: object + timeout_seconds: + type: integer + type: object + kubernetes.container-cp: + properties: + container: + type: string + defer: + type: boolean + description: + type: string + include_empty: + type: boolean + meta: + properties: + labels: + additionalProperties: + type: string + type: object + name: + type: string + type: object + namespace: + type: string + output_dir: + type: string + pod: + type: string + pod_list_options: + 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 + continue: + description: "The continue option should be set when + retrieving more results from the server. Since this + value is server defined, clients may only use the + continue value from a previous query result with identical + query parameters (except for the value of continue) + and the server may reject a continue value it does + not recognize. If the specified continue value is + no longer valid whether due to expiration (generally + five to fifteen minutes) or a configuration change + on the server, the server will respond with a 410 + ResourceExpired error together with a continue token. + If the client needs a consistent list, it must restart + their list without the continue field. Otherwise, + the client may send another list request with the + token received with the 410 error, the server will + respond with a list starting from the next key, but + from the latest snapshot, which is inconsistent from + the previous list results - objects that are created, + modified, or deleted after the first list request + will be included in the response, as long as their + keys are after the \"next key\". \n This field is + not supported when watch is true. Clients may start + a watch from the last resourceVersion value returned + by the server and not miss any modifications." + type: string + fieldSelector: + description: A selector to restrict the list of returned + objects by their fields. Defaults to everything. + 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 + labelSelector: + description: A selector to restrict the list of returned + objects by their labels. Defaults to everything. + type: string + limit: + description: "limit is a maximum number of responses + to return for a list call. If more items exist, the + server will set the `continue` field on the list metadata + to a value that can be used with the same initial + query to retrieve the next set of results. Setting + a limit may return fewer than the requested amount + of items (up to zero items) in the event all requested + objects are filtered out and clients should only use + the presence of the continue field to determine whether + more results are available. Servers may choose not + to support the limit argument and will return all + of the available results. If limit is specified and + the continue field is empty, clients may assume that + no more results are available. This field is not supported + if watch is true. \n The server guarantees that the + objects returned when using continue will be identical + to issuing a single list call without a limit - that + is, no objects created, modified, or deleted after + the first request is issued will be included in any + subsequent continued requests. This is sometimes referred + to as a consistent snapshot, and ensures that a client + that is using limit to receive smaller chunks of a + very large result can ensure they see all possible + objects. If objects are updated during a chunked list + the version of the object that was present at the + time the first list result was calculated is returned." + format: int64 + type: integer + resourceVersion: + description: 'When specified with a watch call, shows + changes that occur after that particular version of + a resource. Defaults to changes from the beginning + of history. When specified for list: - if unset, then + the result is returned from remote storage based on + quorum-read flag; - if it''s 0, then we simply return + what we currently have in cache, no guarantee; - if + set to non zero, then the result is at least as fresh + as given rv.' + type: string + timeoutSeconds: + description: Timeout for the list/watch call. This limits + the duration of the call, regardless of any activity + or inactivity. + format: int64 + type: integer + watch: + description: Watch for changes to the described resources + and return them as a stream of add, update, and remove + notifications. Specify resourceVersion. + type: boolean + type: object + scrub: + properties: + regex: + type: string + replace: + type: string + required: + - regex + - replace + type: object + src_path: + type: string + timeout_seconds: + type: integer + type: object + kubernetes.logs: + properties: + defer: + type: boolean + description: + type: string + include_empty: + type: boolean + list_options: + 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 + continue: + description: "The continue option should be set when + retrieving more results from the server. Since this + value is server defined, clients may only use the + continue value from a previous query result with identical + query parameters (except for the value of continue) + and the server may reject a continue value it does + not recognize. If the specified continue value is + no longer valid whether due to expiration (generally + five to fifteen minutes) or a configuration change + on the server, the server will respond with a 410 + ResourceExpired error together with a continue token. + If the client needs a consistent list, it must restart + their list without the continue field. Otherwise, + the client may send another list request with the + token received with the 410 error, the server will + respond with a list starting from the next key, but + from the latest snapshot, which is inconsistent from + the previous list results - objects that are created, + modified, or deleted after the first list request + will be included in the response, as long as their + keys are after the \"next key\". \n This field is + not supported when watch is true. Clients may start + a watch from the last resourceVersion value returned + by the server and not miss any modifications." + type: string + fieldSelector: + description: A selector to restrict the list of returned + objects by their fields. Defaults to everything. + 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 + labelSelector: + description: A selector to restrict the list of returned + objects by their labels. Defaults to everything. + type: string + limit: + description: "limit is a maximum number of responses + to return for a list call. If more items exist, the + server will set the `continue` field on the list metadata + to a value that can be used with the same initial + query to retrieve the next set of results. Setting + a limit may return fewer than the requested amount + of items (up to zero items) in the event all requested + objects are filtered out and clients should only use + the presence of the continue field to determine whether + more results are available. Servers may choose not + to support the limit argument and will return all + of the available results. If limit is specified and + the continue field is empty, clients may assume that + no more results are available. This field is not supported + if watch is true. \n The server guarantees that the + objects returned when using continue will be identical + to issuing a single list call without a limit - that + is, no objects created, modified, or deleted after + the first request is issued will be included in any + subsequent continued requests. This is sometimes referred + to as a consistent snapshot, and ensures that a client + that is using limit to receive smaller chunks of a + very large result can ensure they see all possible + objects. If objects are updated during a chunked list + the version of the object that was present at the + time the first list result was calculated is returned." + format: int64 + type: integer + resourceVersion: + description: 'When specified with a watch call, shows + changes that occur after that particular version of + a resource. Defaults to changes from the beginning + of history. When specified for list: - if unset, then + the result is returned from remote storage based on + quorum-read flag; - if it''s 0, then we simply return + what we currently have in cache, no guarantee; - if + set to non zero, then the result is at least as fresh + as given rv.' + type: string + timeoutSeconds: + description: Timeout for the list/watch call. This limits + the duration of the call, regardless of any activity + or inactivity. + format: int64 + type: integer + watch: + description: Watch for changes to the described resources + and return them as a stream of add, update, and remove + notifications. Specify resourceVersion. + type: boolean + type: object + meta: + properties: + labels: + additionalProperties: + type: string + type: object + name: + type: string + type: object + namespace: + type: string + output_dir: + type: string + pod: + type: string + pod_log_options: + 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 + container: + description: The container for which to stream logs. + Defaults to only container if there is one container + in the pod. + type: string + follow: + description: Follow the log stream of the pod. Defaults + to false. + type: boolean + 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 + limitBytes: + description: If set, the number of bytes to read from + the server before terminating the log output. This + may not display a complete final line of logging, + and may return slightly more or slightly less than + the specified limit. + format: int64 + type: integer + previous: + description: Return previous terminated container logs. + Defaults to false. + type: boolean + sinceSeconds: + description: A relative time in seconds before the current + time from which to show logs. If this value precedes + the time a pod was started, only logs since the pod + start will be returned. If this value is in the future, + no logs will be returned. Only one of sinceSeconds + or sinceTime may be specified. + format: int64 + type: integer + sinceTime: + description: An RFC3339 timestamp from which to show + logs. If this value precedes the time a pod was started, + only logs since the pod start will be returned. If + this value is in the future, no logs will be returned. + Only one of sinceSeconds or sinceTime may be specified. + format: date-time + type: string + tailLines: + description: If set, the number of lines from the end + of the logs to show. If not specified, logs are shown + from the creation of the container or sinceSeconds + or sinceTime + format: int64 + type: integer + timestamps: + description: If true, add an RFC3339 or RFC3339Nano + timestamp at the beginning of every line of log output. + Defaults to false. + type: boolean + type: object + scrub: + properties: + regex: + type: string + replace: + type: string + required: + - regex + - replace + type: object + timeout_seconds: + type: integer + type: object + kubernetes.resource-list: + properties: + defer: + type: boolean + description: + type: string + group_version: + type: string + include_empty: + type: boolean + kind: + type: string + meta: + properties: + labels: + additionalProperties: + type: string + type: object + name: + type: string + type: object + namespace: + type: string + output_dir: + type: string + resource_list_options: + 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 + continue: + description: "The continue option should be set when + retrieving more results from the server. Since this + value is server defined, clients may only use the + continue value from a previous query result with identical + query parameters (except for the value of continue) + and the server may reject a continue value it does + not recognize. If the specified continue value is + no longer valid whether due to expiration (generally + five to fifteen minutes) or a configuration change + on the server, the server will respond with a 410 + ResourceExpired error together with a continue token. + If the client needs a consistent list, it must restart + their list without the continue field. Otherwise, + the client may send another list request with the + token received with the 410 error, the server will + respond with a list starting from the next key, but + from the latest snapshot, which is inconsistent from + the previous list results - objects that are created, + modified, or deleted after the first list request + will be included in the response, as long as their + keys are after the \"next key\". \n This field is + not supported when watch is true. Clients may start + a watch from the last resourceVersion value returned + by the server and not miss any modifications." + type: string + fieldSelector: + description: A selector to restrict the list of returned + objects by their fields. Defaults to everything. + 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 + labelSelector: + description: A selector to restrict the list of returned + objects by their labels. Defaults to everything. + type: string + limit: + description: "limit is a maximum number of responses + to return for a list call. If more items exist, the + server will set the `continue` field on the list metadata + to a value that can be used with the same initial + query to retrieve the next set of results. Setting + a limit may return fewer than the requested amount + of items (up to zero items) in the event all requested + objects are filtered out and clients should only use + the presence of the continue field to determine whether + more results are available. Servers may choose not + to support the limit argument and will return all + of the available results. If limit is specified and + the continue field is empty, clients may assume that + no more results are available. This field is not supported + if watch is true. \n The server guarantees that the + objects returned when using continue will be identical + to issuing a single list call without a limit - that + is, no objects created, modified, or deleted after + the first request is issued will be included in any + subsequent continued requests. This is sometimes referred + to as a consistent snapshot, and ensures that a client + that is using limit to receive smaller chunks of a + very large result can ensure they see all possible + objects. If objects are updated during a chunked list + the version of the object that was present at the + time the first list result was calculated is returned." + format: int64 + type: integer + resourceVersion: + description: 'When specified with a watch call, shows + changes that occur after that particular version of + a resource. Defaults to changes from the beginning + of history. When specified for list: - if unset, then + the result is returned from remote storage based on + quorum-read flag; - if it''s 0, then we simply return + what we currently have in cache, no guarantee; - if + set to non zero, then the result is at least as fresh + as given rv.' + type: string + timeoutSeconds: + description: Timeout for the list/watch call. This limits + the duration of the call, regardless of any activity + or inactivity. + format: int64 + type: integer + watch: + description: Watch for changes to the described resources + and return them as a stream of add, update, and remove + notifications. Specify resourceVersion. + type: boolean + type: object + scrub: + properties: + regex: + type: string + replace: + type: string + required: + - regex + - replace + type: object + timeout_seconds: + type: integer + required: + - kind + type: object + kubernetes.version: + properties: + defer: + type: boolean + description: + type: string + include_empty: + type: boolean + meta: + properties: + labels: + additionalProperties: + type: string + type: object + name: + type: string + type: object + output_dir: + type: string + scrub: + properties: + regex: + type: string + replace: + type: string + required: + - regex + - replace + type: object + timeout_seconds: + type: integer + type: object + type: object + type: array + required: + - collectors type: object status: type: object diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml index cbcc6d0f..d9e08ecf 100644 --- a/config/default/manager_auth_proxy_patch.yaml +++ b/config/default/manager_auth_proxy_patch.yaml @@ -13,7 +13,7 @@ spec: image: gcr.io/kubebuilder/kube-rbac-proxy:v0.4.0 args: - "--secure-listen-address=0.0.0.0:8443" - - "--upstream=http://127.0.0.1:8080/" + - "--upstream=http://127.0.0.1:8089/" - "--logtostderr=true" - "--v=10" ports: @@ -21,4 +21,4 @@ spec: name: https - name: manager args: - - "--metrics-addr=127.0.0.1:8080" + - "--metrics-addr=127.0.0.1:8088" diff --git a/config/samples/troubleshoot_v1beta1_collector.yaml b/config/samples/troubleshoot_v1beta1_collector.yaml index bcf9699c..7a5a43cc 100644 --- a/config/samples/troubleshoot_v1beta1_collector.yaml +++ b/config/samples/troubleshoot_v1beta1_collector.yaml @@ -1,9 +1,23 @@ apiVersion: troubleshoot.replicated.com/v1beta1 kind: Collector metadata: - labels: - controller-tools.k8s.io: "1.0" name: collector-sample spec: - # Add fields here - foo: bar + collectors: + - kubernetes.resource-list: + output_dir: /kubernetes/resources/jobs + kind: jobs + namespace: "" + - kubernetes.resource-list: + output_dir: /kubernetes/resources/deployments + kind: deployments + namespace: "" + - kubernetes.resource-list: + output_dir: /kubernetes/resources/pods + kind: pods + namespace: "" + - kubernetes.resource-list: + output_dir: /kubernetes/resources/events + kind: events + namespace: "" + diff --git a/go.sum b/go.sum index 64a6df0d..35e43c84 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,7 @@ github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= @@ -129,6 +130,7 @@ github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCO github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -152,12 +154,14 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -183,6 +187,7 @@ github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 h1:zNBQb37RGLmJybyMcs github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/petar/GoLLRB v0.0.0-20190514000832-33fb24c13b99/go.mod h1:HUpKUBZnpzkdx0kD/+Yfuft+uD3zHGtXF/XJB14TUr4= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -229,16 +234,21 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/pkg/apis/troubleshoot/v1beta1/collector_shared.go b/pkg/apis/troubleshoot/v1beta1/collector_shared.go new file mode 100644 index 00000000..85ececd3 --- /dev/null +++ b/pkg/apis/troubleshoot/v1beta1/collector_shared.go @@ -0,0 +1,72 @@ +package v1beta1 + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type Meta struct { + Name string `json:"name,omitempty"` + Labels map[string]string `json:"labels,omitempty"` +} + +type Scrub struct { + Regex string `json:"regex"` + Replace string `json:"replace"` +} + +type SpecShared struct { + Description string `json:"description,omitempty"` + OutputDir string `json:"output_dir,omitempty"` + TimeoutSeconds int `json:"timeout_seconds,omitempty"` + Scrub *Scrub `json:"scrub,omitempty"` + IncludeEmpty bool `json:"include_empty,omitempty"` + Meta *Meta `json:"meta,omitempty"` + Defer bool `json:"defer,omitempty"` +} + +type KubernetesAPIVersionsOptions struct { + SpecShared `json:",inline,omitempty"` +} + +type KubernetesClusterInfoOptions struct { + SpecShared `json:",inline,omitempty"` +} + +type KubernetesVersionOptions struct { + SpecShared `json:",inline,omitempty"` +} + +type KubernetesLogsOptions struct { + SpecShared `json:",inline,omitempty"` + Pod string `json:"pod,omitempty"` + Namespace string `json:"namespace,omitempty"` + PodLogOptions *v1.PodLogOptions `json:"pod_log_options,omitempty"` + ListOptions *metav1.ListOptions `json:"list_options,omitempty"` +} + +type KubernetesContainerCpOptions struct { + SpecShared `json:",inline,omitempty"` + Pod string `json:"pod,omitempty"` + PodListOptions *metav1.ListOptions `json:"pod_list_options,omitempty"` + Container string `json:"container,omitempty"` + Namespace string `json:"namespace,omitempty"` + SrcPath string `json:"src_path,omitempty"` +} + +type KubernetesResourceListOptions struct { + SpecShared `json:",inline,omitempty"` + Kind string `json:"kind"` + GroupVersion string `json:"group_version,omitempty"` + Namespace string `json:"namespace,omitempty"` + ListOptions *metav1.ListOptions `json:"resource_list_options,omitempty"` +} + +type Collect struct { + KubernetesAPIVersions *KubernetesAPIVersionsOptions `json:"kubernetes.api-versions,omitempty"` + KubernetesClusterInfo *KubernetesClusterInfoOptions `json:"kubernetes.cluster-info,omitempty"` + KubernetesContainerCp *KubernetesContainerCpOptions `json:"kubernetes.container-cp,omitempty"` + KubernetesLogs *KubernetesLogsOptions `json:"kubernetes.logs,omitempty"` + KubernetesResourceList *KubernetesResourceListOptions `json:"kubernetes.resource-list,omitempty"` + KubernetesVersion *KubernetesVersionOptions `json:"kubernetes.version,omitempty"` +} diff --git a/pkg/apis/troubleshoot/v1beta1/collector_types.go b/pkg/apis/troubleshoot/v1beta1/collector_types.go index 60af6022..7313e71d 100644 --- a/pkg/apis/troubleshoot/v1beta1/collector_types.go +++ b/pkg/apis/troubleshoot/v1beta1/collector_types.go @@ -20,13 +20,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - // CollectorSpec defines the desired state of Collector type CollectorSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file + Collectors []*Collect `json:"collectors"` } // CollectorStatus defines the observed state of Collector diff --git a/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go b/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go index 55d155fb..725a3c88 100644 --- a/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go @@ -5,6 +5,8 @@ package v1beta1 import ( + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -186,12 +188,57 @@ func (in *AnalyzerStatus) DeepCopy() *AnalyzerStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Collect) DeepCopyInto(out *Collect) { + *out = *in + if in.KubernetesAPIVersions != nil { + in, out := &in.KubernetesAPIVersions, &out.KubernetesAPIVersions + *out = new(KubernetesAPIVersionsOptions) + (*in).DeepCopyInto(*out) + } + if in.KubernetesClusterInfo != nil { + in, out := &in.KubernetesClusterInfo, &out.KubernetesClusterInfo + *out = new(KubernetesClusterInfoOptions) + (*in).DeepCopyInto(*out) + } + if in.KubernetesContainerCp != nil { + in, out := &in.KubernetesContainerCp, &out.KubernetesContainerCp + *out = new(KubernetesContainerCpOptions) + (*in).DeepCopyInto(*out) + } + if in.KubernetesLogs != nil { + in, out := &in.KubernetesLogs, &out.KubernetesLogs + *out = new(KubernetesLogsOptions) + (*in).DeepCopyInto(*out) + } + if in.KubernetesResourceList != nil { + in, out := &in.KubernetesResourceList, &out.KubernetesResourceList + *out = new(KubernetesResourceListOptions) + (*in).DeepCopyInto(*out) + } + if in.KubernetesVersion != nil { + in, out := &in.KubernetesVersion, &out.KubernetesVersion + *out = new(KubernetesVersionOptions) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Collect. +func (in *Collect) DeepCopy() *Collect { + if in == nil { + return nil + } + out := new(Collect) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Collector) DeepCopyInto(out *Collector) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status } @@ -337,6 +384,17 @@ func (in *CollectorList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CollectorSpec) DeepCopyInto(out *CollectorSpec) { *out = *in + if in.Collectors != nil { + in, out := &in.Collectors, &out.Collectors + *out = make([]*Collect, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Collect) + (*in).DeepCopyInto(*out) + } + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CollectorSpec. @@ -364,6 +422,144 @@ func (in *CollectorStatus) DeepCopy() *CollectorStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesAPIVersionsOptions) DeepCopyInto(out *KubernetesAPIVersionsOptions) { + *out = *in + in.SpecShared.DeepCopyInto(&out.SpecShared) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesAPIVersionsOptions. +func (in *KubernetesAPIVersionsOptions) DeepCopy() *KubernetesAPIVersionsOptions { + if in == nil { + return nil + } + out := new(KubernetesAPIVersionsOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesClusterInfoOptions) DeepCopyInto(out *KubernetesClusterInfoOptions) { + *out = *in + in.SpecShared.DeepCopyInto(&out.SpecShared) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesClusterInfoOptions. +func (in *KubernetesClusterInfoOptions) DeepCopy() *KubernetesClusterInfoOptions { + if in == nil { + return nil + } + out := new(KubernetesClusterInfoOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesContainerCpOptions) DeepCopyInto(out *KubernetesContainerCpOptions) { + *out = *in + in.SpecShared.DeepCopyInto(&out.SpecShared) + if in.PodListOptions != nil { + in, out := &in.PodListOptions, &out.PodListOptions + *out = new(metav1.ListOptions) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesContainerCpOptions. +func (in *KubernetesContainerCpOptions) DeepCopy() *KubernetesContainerCpOptions { + if in == nil { + return nil + } + out := new(KubernetesContainerCpOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesLogsOptions) DeepCopyInto(out *KubernetesLogsOptions) { + *out = *in + in.SpecShared.DeepCopyInto(&out.SpecShared) + if in.PodLogOptions != nil { + in, out := &in.PodLogOptions, &out.PodLogOptions + *out = new(v1.PodLogOptions) + (*in).DeepCopyInto(*out) + } + if in.ListOptions != nil { + in, out := &in.ListOptions, &out.ListOptions + *out = new(metav1.ListOptions) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesLogsOptions. +func (in *KubernetesLogsOptions) DeepCopy() *KubernetesLogsOptions { + if in == nil { + return nil + } + out := new(KubernetesLogsOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesResourceListOptions) DeepCopyInto(out *KubernetesResourceListOptions) { + *out = *in + in.SpecShared.DeepCopyInto(&out.SpecShared) + if in.ListOptions != nil { + in, out := &in.ListOptions, &out.ListOptions + *out = new(metav1.ListOptions) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesResourceListOptions. +func (in *KubernetesResourceListOptions) DeepCopy() *KubernetesResourceListOptions { + if in == nil { + return nil + } + out := new(KubernetesResourceListOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesVersionOptions) DeepCopyInto(out *KubernetesVersionOptions) { + *out = *in + in.SpecShared.DeepCopyInto(&out.SpecShared) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesVersionOptions. +func (in *KubernetesVersionOptions) DeepCopy() *KubernetesVersionOptions { + if in == nil { + return nil + } + out := new(KubernetesVersionOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Meta) DeepCopyInto(out *Meta) { + *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 + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Meta. +func (in *Meta) DeepCopy() *Meta { + if in == nil { + return nil + } + out := new(Meta) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Preflight) DeepCopyInto(out *Preflight) { *out = *in @@ -541,3 +737,43 @@ func (in *PreflightStatus) DeepCopy() *PreflightStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Scrub) DeepCopyInto(out *Scrub) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Scrub. +func (in *Scrub) DeepCopy() *Scrub { + if in == nil { + return nil + } + out := new(Scrub) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SpecShared) DeepCopyInto(out *SpecShared) { + *out = *in + if in.Scrub != nil { + in, out := &in.Scrub, &out.Scrub + *out = new(Scrub) + **out = **in + } + if in.Meta != nil { + in, out := &in.Meta, &out.Meta + *out = new(Meta) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SpecShared. +func (in *SpecShared) DeepCopy() *SpecShared { + if in == nil { + return nil + } + out := new(SpecShared) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/controller/collector/collector_controller.go b/pkg/controller/collector/collector_controller.go index 6588d32e..d3508de9 100644 --- a/pkg/controller/collector/collector_controller.go +++ b/pkg/controller/collector/collector_controller.go @@ -18,18 +18,13 @@ package collector import ( "context" - "reflect" troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1" appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -92,8 +87,6 @@ type ReconcileCollector struct { // Reconcile reads that state of the cluster for a Collector object and makes changes based on the state read // and what is in the Collector.Spec -// TODO(user): Modify this Reconcile function to implement your Controller logic. The scaffolding writes -// a Deployment as an example // Automatically generate RBAC rules to allow the Controller to read and write Deployments // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch @@ -113,55 +106,5 @@ func (r *ReconcileCollector) Reconcile(request reconcile.Request) (reconcile.Res return reconcile.Result{}, err } - // TODO(user): Change this to be the object type created by your controller - // Define the desired Deployment object - deploy := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: instance.Name + "-deployment", - Namespace: instance.Namespace, - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"deployment": instance.Name + "-deployment"}, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"deployment": instance.Name + "-deployment"}}, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "nginx", - Image: "nginx", - }, - }, - }, - }, - }, - } - if err := controllerutil.SetControllerReference(instance, deploy, r.scheme); err != nil { - return reconcile.Result{}, err - } - - // TODO(user): Change this for the object type created by your controller - // Check if the Deployment already exists - found := &appsv1.Deployment{} - err = r.Get(context.TODO(), types.NamespacedName{Name: deploy.Name, Namespace: deploy.Namespace}, found) - if err != nil && errors.IsNotFound(err) { - log.Info("Creating Deployment", "namespace", deploy.Namespace, "name", deploy.Name) - err = r.Create(context.TODO(), deploy) - return reconcile.Result{}, err - } else if err != nil { - return reconcile.Result{}, err - } - - // TODO(user): Change this for the object type created by your controller - // Update the found object and write the result back if there are any changes - if !reflect.DeepEqual(deploy.Spec, found.Spec) { - found.Spec = deploy.Spec - log.Info("Updating Deployment", "namespace", deploy.Namespace, "name", deploy.Name) - err = r.Update(context.TODO(), found) - if err != nil { - return reconcile.Result{}, err - } - } return reconcile.Result{}, nil } diff --git a/pkg/controller/collector/collector_controller_test.go b/pkg/controller/collector/collector_controller_test.go index c2cff6f6..d64299ca 100644 --- a/pkg/controller/collector/collector_controller_test.go +++ b/pkg/controller/collector/collector_controller_test.go @@ -18,71 +18,8 @@ package collector import ( "testing" - "time" - - "github.com/onsi/gomega" - troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1" - "golang.org/x/net/context" - appsv1 "k8s.io/api/apps/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -var c client.Client - -var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: "foo", Namespace: "default"}} -var depKey = types.NamespacedName{Name: "foo-deployment", Namespace: "default"} - -const timeout = time.Second * 5 - func TestReconcile(t *testing.T) { - g := gomega.NewGomegaWithT(t) - instance := &troubleshootv1beta1.Collector{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}} - - // Setup the Manager and Controller. Wrap the Controller Reconcile function so it writes each request to a - // channel when it is finished. - mgr, err := manager.New(cfg, manager.Options{}) - g.Expect(err).NotTo(gomega.HaveOccurred()) - c = mgr.GetClient() - - recFn, requests := SetupTestReconcile(newReconciler(mgr)) - g.Expect(add(mgr, recFn)).NotTo(gomega.HaveOccurred()) - - stopMgr, mgrStopped := StartTestManager(mgr, g) - - defer func() { - close(stopMgr) - mgrStopped.Wait() - }() - - // Create the Collector object and expect the Reconcile and Deployment to be created - err = c.Create(context.TODO(), instance) - // The instance object may not be a valid object because it might be missing some required fields. - // Please modify the instance object by adding required fields and then remove the following if statement. - if apierrors.IsInvalid(err) { - t.Logf("failed to create object, got an invalid object error: %v", err) - return - } - g.Expect(err).NotTo(gomega.HaveOccurred()) - defer c.Delete(context.TODO(), instance) - g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest))) - - deploy := &appsv1.Deployment{} - g.Eventually(func() error { return c.Get(context.TODO(), depKey, deploy) }, timeout). - Should(gomega.Succeed()) - - // Delete the Deployment and expect Reconcile to be called for Deployment deletion - g.Expect(c.Delete(context.TODO(), deploy)).NotTo(gomega.HaveOccurred()) - g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest))) - g.Eventually(func() error { return c.Get(context.TODO(), depKey, deploy) }, timeout). - Should(gomega.Succeed()) - - // Manually delete Deployment since GC isn't enabled in the test control plane - g.Eventually(func() error { return c.Delete(context.TODO(), deploy) }, timeout). - Should(gomega.MatchError("deployments.apps \"foo-deployment\" not found")) }