From e9a52070e6e8d60f4b60271ff54d9dff1833147f Mon Sep 17 00:00:00 2001 From: asim Date: Mon, 8 Jul 2024 18:44:42 +0100 Subject: [PATCH] k8s not needed --- util/kubernetes/api/api_test.go | 180 ------------- util/kubernetes/api/request.go | 270 ------------------- util/kubernetes/api/response.go | 99 ------- util/kubernetes/client/client.go | 392 ---------------------------- util/kubernetes/client/options.go | 107 -------- util/kubernetes/client/templates.go | 167 ------------ util/kubernetes/client/types.go | 209 --------------- util/kubernetes/client/util.go | 104 -------- util/kubernetes/client/util_test.go | 47 ---- util/kubernetes/client/watch.go | 124 --------- 10 files changed, 1699 deletions(-) delete mode 100644 util/kubernetes/api/api_test.go delete mode 100644 util/kubernetes/api/request.go delete mode 100644 util/kubernetes/api/response.go delete mode 100644 util/kubernetes/client/client.go delete mode 100644 util/kubernetes/client/options.go delete mode 100644 util/kubernetes/client/templates.go delete mode 100644 util/kubernetes/client/types.go delete mode 100644 util/kubernetes/client/util.go delete mode 100644 util/kubernetes/client/util_test.go delete mode 100644 util/kubernetes/client/watch.go diff --git a/util/kubernetes/api/api_test.go b/util/kubernetes/api/api_test.go deleted file mode 100644 index 294cafb26c..0000000000 --- a/util/kubernetes/api/api_test.go +++ /dev/null @@ -1,180 +0,0 @@ -package api - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "reflect" - "testing" -) - -type testcase struct { - Token string - ReqFn func(opts *Options) *Request - Method string - URI string - Body interface{} - Header map[string]string - Assert func(req *http.Request) bool -} - -type assertFn func(req *http.Request) bool - -var tests = []testcase{ - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts).Get().Resource("service") - }, - Method: "GET", - URI: "/api/v1/namespaces/default/services/", - }, - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts).Get().Resource("service").Name("foo") - }, - Method: "GET", - URI: "/api/v1/namespaces/default/services/foo", - }, - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts).Get().Resource("service").Namespace("test").Name("bar") - }, - Method: "GET", - URI: "/api/v1/namespaces/test/services/bar", - }, - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts).Get().Resource("deployment").Name("foo") - }, - Method: "GET", - URI: "/apis/apps/v1/namespaces/default/deployments/foo", - }, - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts).Get().Resource("deployment").Namespace("test").Name("foo") - }, - Method: "GET", - URI: "/apis/apps/v1/namespaces/test/deployments/foo", - }, - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts).Get().Resource("pod").Params(&Params{LabelSelector: map[string]string{"foo": "bar"}}) - }, - Method: "GET", - URI: "/api/v1/namespaces/default/pods/?labelSelector=foo%3Dbar", - }, - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts).Post().Resource("service").Name("foo").Body(map[string]string{"foo": "bar"}) - }, - Method: "POST", - URI: "/api/v1/namespaces/default/services/foo", - Body: map[string]string{"foo": "bar"}, - }, - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts).Post().Resource("deployment").Namespace("test").Name("foo").Body(map[string]string{"foo": "bar"}) - }, - Method: "POST", - URI: "/apis/apps/v1/namespaces/test/deployments/foo", - Body: map[string]string{"foo": "bar"}, - }, - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts).Put().Resource("endpoint").Name("baz").Body(map[string]string{"bam": "bar"}) - }, - Method: "PUT", - URI: "/api/v1/namespaces/default/endpoints/baz", - Body: map[string]string{"bam": "bar"}, - }, - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts).Patch().Resource("endpoint").Name("baz").Body(map[string]string{"bam": "bar"}) - }, - Method: "PATCH", - URI: "/api/v1/namespaces/default/endpoints/baz", - Body: map[string]string{"bam": "bar"}, - }, - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts).Patch().Resource("endpoint").Name("baz").SetHeader("foo", "bar") - }, - Method: "PATCH", - URI: "/api/v1/namespaces/default/endpoints/baz", - Header: map[string]string{"foo": "bar"}, - }, - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts).Patch().Resource("deployment").Name("baz").SetHeader("foo", "bar") - }, - Method: "PATCH", - URI: "/apis/apps/v1/namespaces/default/deployments/baz", - Header: map[string]string{"foo": "bar"}, - }, - { - ReqFn: func(opts *Options) *Request { - return NewRequest(opts). - Get(). - Resource("pod"). - SubResource("log"). - Name("foolog") - }, - Method: "GET", - URI: "/api/v1/namespaces/default/pods/foolog/log", - }, -} - -var wrappedHandler = func(test *testcase, t *testing.T) http.HandlerFunc { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - auth := r.Header.Get("Authorization") - if len(test.Token) > 0 && (len(auth) == 0 || auth != "Bearer "+test.Token) { - t.Errorf("test case token (%s) did not match expected token (%s)", "Bearer "+test.Token, auth) - } - - if len(test.Method) > 0 && test.Method != r.Method { - t.Errorf("test case Method (%s) did not match expected Method (%s)", test.Method, r.Method) - } - - if len(test.URI) > 0 && test.URI != r.URL.RequestURI() { - t.Errorf("test case URI (%s) did not match expected URI (%s)", test.URI, r.URL.RequestURI()) - } - - if test.Body != nil { - var res map[string]string - decoder := json.NewDecoder(r.Body) - if err := decoder.Decode(&res); err != nil { - t.Errorf("decoding body failed: %v", err) - } - if !reflect.DeepEqual(res, test.Body) { - t.Error("body did not match") - } - } - - if test.Header != nil { - for k, v := range test.Header { - if r.Header.Get(k) != v { - t.Error("header did not exist") - } - } - } - - w.WriteHeader(http.StatusOK) - }) -} - -func TestRequest(t *testing.T) { - for _, test := range tests { - ts := httptest.NewServer(wrappedHandler(&test, t)) - req := test.ReqFn(&Options{ - Host: ts.URL, - Client: &http.Client{}, - BearerToken: &test.Token, - Namespace: "default", - }) - res := req.Do() - if res.Error() != nil { - t.Errorf("request failed with %v", res.Error()) - } - ts.Close() - } -} diff --git a/util/kubernetes/api/request.go b/util/kubernetes/api/request.go deleted file mode 100644 index 227754b018..0000000000 --- a/util/kubernetes/api/request.go +++ /dev/null @@ -1,270 +0,0 @@ -package api - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "net/url" - - "go-micro.dev/v5/logger" -) - -// Request is used to construct a http request for the k8s API. -type Request struct { - // the request context - context context.Context - body io.Reader - - err error - client *http.Client - header http.Header - params url.Values - resourceName *string - subResource *string - method string - host string - namespace string - - resource string -} - -// Params is the object to pass in to set parameters -// on a request. -type Params struct { - LabelSelector map[string]string - Annotations map[string]string - Additional map[string]string -} - -// verb sets method. -func (r *Request) verb(method string) *Request { - r.method = method - return r -} - -func (r *Request) Context(ctx context.Context) { - r.context = ctx -} - -// Get request. -func (r *Request) Get() *Request { - return r.verb("GET") -} - -// Post request. -func (r *Request) Post() *Request { - return r.verb("POST") -} - -// Put request. -func (r *Request) Put() *Request { - return r.verb("PUT") -} - -// Patch request. -func (r *Request) Patch() *Request { - return r.verb("PATCH") -} - -// Delete request. -func (r *Request) Delete() *Request { - return r.verb("DELETE") -} - -// Namespace is to set the namespace to operate on. -func (r *Request) Namespace(s string) *Request { - if len(s) > 0 { - r.namespace = s - } - return r -} - -// Resource is the type of resource the operation is -// for, such as "services", "endpoints" or "pods". -func (r *Request) Resource(s string) *Request { - r.resource = s - return r -} - -// SubResource sets a subresource on a resource, -// e.g. pods/log for pod logs. -func (r *Request) SubResource(s string) *Request { - r.subResource = &s - return r -} - -// Name is for targeting a specific resource by id. -func (r *Request) Name(s string) *Request { - r.resourceName = &s - return r -} - -// Body pass in a body to set, this is for POST, PUT and PATCH requests. -func (r *Request) Body(in interface{}) *Request { - b := new(bytes.Buffer) - // if we're not sending YAML request, we encode to JSON - if r.header.Get("Content-Type") != "application/yaml" { - if err := json.NewEncoder(b).Encode(&in); err != nil { - r.err = err - return r - } - r.body = b - return r - } - - // if application/yaml is set, we assume we get a raw bytes so we just copy over - body, ok := in.(io.Reader) - if !ok { - r.err = errors.New("invalid data") - return r - } - // copy over data to the bytes buffer - if _, err := io.Copy(b, body); err != nil { - r.err = err - return r - } - - r.body = b - return r -} - -func (r *Request) Params(p *Params) *Request { - for k, v := range p.LabelSelector { - // create new key=value pair - value := fmt.Sprintf("%s=%s", k, v) - // check if there's an existing value - if label := r.params.Get("labelSelector"); len(label) > 0 { - value = fmt.Sprintf("%s,%s", label, value) - } - // set and overwrite the value - r.params.Set("labelSelector", value) - } - for k, v := range p.Additional { - r.params.Set(k, v) - } - - return r -} - -// SetHeader sets a header on a request with -// a `key` and `value`. -func (r *Request) SetHeader(key, value string) *Request { - r.header.Add(key, value) - return r -} - -// request builds the http.Request from the options. -func (r *Request) request() (*http.Request, error) { - var url string - switch r.resource { - case "namespace": - // /api/v1/namespaces/ - url = fmt.Sprintf("%s/api/v1/namespaces/", r.host) - case "deployment": - // /apis/apps/v1/namespaces/{namespace}/deployments/{name} - url = fmt.Sprintf("%s/apis/apps/v1/namespaces/%s/%ss/", r.host, r.namespace, r.resource) - default: - // /api/v1/namespaces/{namespace}/{resource} - url = fmt.Sprintf("%s/api/v1/namespaces/%s/%ss/", r.host, r.namespace, r.resource) - } - - // append resourceName if it is present - if r.resourceName != nil { - url += *r.resourceName - if r.subResource != nil { - url += "/" + *r.subResource - } - } - - // append any query params - if len(r.params) > 0 { - url += "?" + r.params.Encode() - } - - var req *http.Request - var err error - - // build request - if r.context != nil { - req, err = http.NewRequestWithContext(r.context, r.method, url, r.body) - } else { - req, err = http.NewRequest(r.method, url, r.body) - } - if err != nil { - return nil, err - } - - // set headers on request - req.Header = r.header - return req, nil -} - -// Do builds and triggers the request. -func (r *Request) Do() *Response { - if r.err != nil { - return &Response{ - err: r.err, - } - } - - req, err := r.request() - if err != nil { - return &Response{ - err: err, - } - } - - logger.Debugf("[Kubernetes] %v %v", req.Method, req.URL.String()) - res, err := r.client.Do(req) - if err != nil { - return &Response{ - err: err, - } - } - - // return res, err - return newResponse(res, err) -} - -// Raw performs a Raw HTTP request to the Kubernetes API. -func (r *Request) Raw() (*http.Response, error) { - req, err := r.request() - if err != nil { - return nil, err - } - - res, err := r.client.Do(req) - if err != nil { - return nil, err - } - return res, nil -} - -// Options ... -type Options struct { - BearerToken *string - Client *http.Client - Host string - Namespace string -} - -// NewRequest creates a k8s api request. -func NewRequest(opts *Options) *Request { - req := &Request{ - header: make(http.Header), - params: make(url.Values), - client: opts.Client, - namespace: opts.Namespace, - host: opts.Host, - } - - if opts.BearerToken != nil { - req.SetHeader("Authorization", "Bearer "+*opts.BearerToken) - } - - return req -} diff --git a/util/kubernetes/api/response.go b/util/kubernetes/api/response.go deleted file mode 100644 index 1c574ae410..0000000000 --- a/util/kubernetes/api/response.go +++ /dev/null @@ -1,99 +0,0 @@ -package api - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "net/http" -) - -// Errors ... -var ( - ErrNotFound = errors.New("kubernetes: resource not found") - ErrDecode = errors.New("kubernetes: error decoding") - ErrUnknown = errors.New("kubernetes: unknown error") -) - -// Status is an object that is returned when a request -// failed or delete succeeded. -type Status struct { - Kind string `json:"kind"` - Status string `json:"status"` - Message string `json:"message"` - Reason string `json:"reason"` - Code int `json:"code"` -} - -// Response ... -type Response struct { - res *http.Response - err error - - body []byte -} - -// Error returns an error. -func (r *Response) Error() error { - return r.err -} - -// StatusCode returns status code for response. -func (r *Response) StatusCode() int { - return r.res.StatusCode -} - -// Into decode body into `data`. -func (r *Response) Into(data interface{}) error { - if r.err != nil { - return r.err - } - - defer r.res.Body.Close() - decoder := json.NewDecoder(r.res.Body) - if err := decoder.Decode(&data); err != nil { - return fmt.Errorf("%v: %v", ErrDecode, err) - } - - return r.err -} - -func (r *Response) Close() error { - if r.res != nil { - return r.res.Body.Close() - } - return nil -} - -func newResponse(res *http.Response, err error) *Response { - r := &Response{ - res: res, - err: err, - } - - if err != nil { - return r - } - - if r.res.StatusCode == http.StatusOK || - r.res.StatusCode == http.StatusCreated || - r.res.StatusCode == http.StatusNoContent { - // Non error status code - return r - } - - if r.res.StatusCode == http.StatusNotFound { - r.err = ErrNotFound - return r - } - - b, err := io.ReadAll(r.res.Body) - if err == nil { - r.err = errors.New(string(b)) - return r - } - - r.err = ErrUnknown - - return r -} diff --git a/util/kubernetes/client/client.go b/util/kubernetes/client/client.go deleted file mode 100644 index 58941101a5..0000000000 --- a/util/kubernetes/client/client.go +++ /dev/null @@ -1,392 +0,0 @@ -// Package client provides an implementation of a restricted subset of kubernetes API client -package client - -import ( - "bytes" - "crypto/tls" - "errors" - "io" - "net/http" - "os" - "path" - "regexp" - "strings" - - "go-micro.dev/v5/logger" - "go-micro.dev/v5/util/kubernetes/api" -) - -var ( - // path to kubernetes service account token. - serviceAccountPath = "/var/run/secrets/kubernetes.io/serviceaccount" - // ErrReadNamespace is returned when the names could not be read from service account. - ErrReadNamespace = errors.New("could not read namespace from service account secret") - // DefaultImage is default micro image. - DefaultImage = "micro/go-micro" - // DefaultNamespace is the default k8s namespace. - DefaultNamespace = "default" -) - -// Client ... -type client struct { - opts *api.Options -} - -// Kubernetes client. -type Client interface { - // Create creates new API resource - Create(*Resource, ...CreateOption) error - // Get queries API resrouces - Get(*Resource, ...GetOption) error - // Update patches existing API object - Update(*Resource, ...UpdateOption) error - // Delete deletes API resource - Delete(*Resource, ...DeleteOption) error - // List lists API resources - List(*Resource, ...ListOption) error - // Log gets log for a pod - Log(*Resource, ...LogOption) (io.ReadCloser, error) - // Watch for events - Watch(*Resource, ...WatchOption) (Watcher, error) -} - -// Create creates new API object. -func (c *client) Create(r *Resource, opts ...CreateOption) error { - options := CreateOptions{ - Namespace: c.opts.Namespace, - } - for _, o := range opts { - o(&options) - } - - b := new(bytes.Buffer) - if err := renderTemplate(r.Kind, b, r.Value); err != nil { - return err - } - resp := api.NewRequest(c.opts). - Post(). - SetHeader("Content-Type", "application/yaml"). - Namespace(options.Namespace). - Resource(r.Kind). - Body(b). - Do() - resp.Close() - return resp.Error() -} - -var ( - nameRegex = regexp.MustCompile("[^a-zA-Z0-9]+") -) - -// SerializeResourceName removes all spacial chars from a string so it -// can be used as a k8s resource name. -func SerializeResourceName(ns string) string { - return nameRegex.ReplaceAllString(ns, "-") -} - -// Get queries API objects and stores the result in r. -func (c *client) Get(r *Resource, opts ...GetOption) error { - options := GetOptions{ - Namespace: c.opts.Namespace, - } - for _, o := range opts { - o(&options) - } - - return api.NewRequest(c.opts). - Get(). - Resource(r.Kind). - Namespace(options.Namespace). - Params(&api.Params{LabelSelector: options.Labels}). - Do(). - Into(r.Value) -} - -// Log returns logs for a pod. -func (c *client) Log(r *Resource, opts ...LogOption) (io.ReadCloser, error) { - options := LogOptions{ - Namespace: c.opts.Namespace, - } - for _, o := range opts { - o(&options) - } - - req := api.NewRequest(c.opts). - Get(). - Resource(r.Kind). - SubResource("log"). - Name(r.Name). - Namespace(options.Namespace) - - if options.Params != nil { - req.Params(&api.Params{Additional: options.Params}) - } - - resp, err := req.Raw() - if err != nil { - return nil, err - } - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - resp.Body.Close() - return nil, errors.New(resp.Request.URL.String() + ": " + resp.Status) - } - return resp.Body, nil -} - -// Update updates API object. -func (c *client) Update(r *Resource, opts ...UpdateOption) error { - options := UpdateOptions{ - Namespace: c.opts.Namespace, - } - for _, o := range opts { - o(&options) - } - - req := api.NewRequest(c.opts). - Patch(). - SetHeader("Content-Type", "application/strategic-merge-patch+json"). - Resource(r.Kind). - Name(r.Name). - Namespace(options.Namespace) - - switch r.Kind { - case "service": - req.Body(r.Value.(*Service)) - case "deployment": - req.Body(r.Value.(*Deployment)) - case "pod": - req.Body(r.Value.(*Pod)) - default: - return errors.New("unsupported resource") - } - resp := req.Do() - resp.Close() - return resp.Error() -} - -// Delete removes API object. -func (c *client) Delete(r *Resource, opts ...DeleteOption) error { - options := DeleteOptions{ - Namespace: c.opts.Namespace, - } - for _, o := range opts { - o(&options) - } - resp := api.NewRequest(c.opts). - Delete(). - Resource(r.Kind). - Name(r.Name). - Namespace(options.Namespace). - Do() - resp.Close() - return resp.Error() -} - -// List lists API objects and stores the result in r. -func (c *client) List(r *Resource, opts ...ListOption) error { - options := ListOptions{ - Namespace: c.opts.Namespace, - } - for _, o := range opts { - o(&options) - } - - return c.Get(r, GetNamespace(options.Namespace)) -} - -// Watch returns an event stream. -func (c *client) Watch(r *Resource, opts ...WatchOption) (Watcher, error) { - options := WatchOptions{ - Namespace: c.opts.Namespace, - } - for _, o := range opts { - o(&options) - } - - // set the watch param - params := &api.Params{Additional: map[string]string{ - "watch": "true", - }} - - // get options params - if options.Params != nil { - for k, v := range options.Params { - params.Additional[k] = v - } - } - - req := api.NewRequest(c.opts). - Get(). - Resource(r.Kind). - Name(r.Name). - Namespace(options.Namespace). - Params(params) - - return newWatcher(req) -} - -// NewService returns default micro kubernetes service definition. -func NewService(name, version, typ, namespace string) *Service { - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Logf(logger.TraceLevel, "kubernetes default service: name: %s, version: %s", name, version) - } - - Labels := map[string]string{ - "name": name, - "version": version, - "micro": typ, - } - - svcName := name - if len(version) > 0 { - // API service object name joins name and version over "-" - svcName = strings.Join([]string{name, version}, "-") - } - - if len(namespace) == 0 { - namespace = DefaultNamespace - } - - Metadata := &Metadata{ - Name: svcName, - Namespace: SerializeResourceName(namespace), - Version: version, - Labels: Labels, - } - - Spec := &ServiceSpec{ - Type: "ClusterIP", - Selector: Labels, - Ports: []ServicePort{{ - Name: "service-port", - Protocol: "", - Port: 8080, - }}, - } - - return &Service{ - Metadata: Metadata, - Spec: Spec, - } -} - -// NewService returns default micro kubernetes deployment definition. -func NewDeployment(name, version, typ, namespace string) *Deployment { - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Logf(logger.TraceLevel, "kubernetes default deployment: name: %s, version: %s", name, version) - } - - Labels := map[string]string{ - "name": name, - "version": version, - "micro": typ, - } - - depName := name - if len(version) > 0 { - // API deployment object name joins name and version over "-" - depName = strings.Join([]string{name, version}, "-") - } - - if len(namespace) == 0 { - namespace = DefaultNamespace - } - - Metadata := &Metadata{ - Name: depName, - Namespace: SerializeResourceName(namespace), - Version: version, - Labels: Labels, - Annotations: map[string]string{}, - } - - // enable go modules by default - env := EnvVar{ - Name: "GO111MODULE", - Value: "on", - } - - Spec := &DeploymentSpec{ - Replicas: 1, - Selector: &LabelSelector{ - MatchLabels: Labels, - }, - Template: &Template{ - Metadata: Metadata, - PodSpec: &PodSpec{ - ServiceAccountName: namespace, - Containers: []Container{{ - Name: name, - Image: DefaultImage, - Env: []EnvVar{env}, - Command: []string{"go", "run", "."}, - Ports: []ContainerPort{{ - Name: "service-port", - ContainerPort: 8080, - }}, - }}, - }, - }, - } - - return &Deployment{ - Metadata: Metadata, - Spec: Spec, - } -} - -// NewLocalClient returns a client that can be used with `kubectl proxy`. -func NewLocalClient(hosts ...string) *client { - if len(hosts) == 0 { - hosts[0] = "http://localhost:8001" - } - return &client{ - opts: &api.Options{ - Client: http.DefaultClient, - Host: hosts[0], - Namespace: "default", - }, - } -} - -// NewClusterClient creates a Kubernetes client for use from within a k8s pod. -func NewClusterClient() *client { - host := "https://" + os.Getenv("KUBERNETES_SERVICE_HOST") + ":" + os.Getenv("KUBERNETES_SERVICE_PORT") - - s, err := os.Stat(serviceAccountPath) - if err != nil { - logger.Fatal(err) - } - if s == nil || !s.IsDir() { - logger.Fatal(errors.New("service account not found")) - } - - token, err := os.ReadFile(path.Join(serviceAccountPath, "token")) - if err != nil { - logger.Fatal(err) - } - t := string(token) - - crt, err := CertPoolFromFile(path.Join(serviceAccountPath, "ca.crt")) - if err != nil { - logger.Fatal(err) - } - - c := &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - RootCAs: crt, - }, - DisableCompression: true, - }, - } - - return &client{ - opts: &api.Options{ - Client: c, - Host: host, - BearerToken: &t, - Namespace: DefaultNamespace, - }, - } -} diff --git a/util/kubernetes/client/options.go b/util/kubernetes/client/options.go deleted file mode 100644 index 9e60f549b6..0000000000 --- a/util/kubernetes/client/options.go +++ /dev/null @@ -1,107 +0,0 @@ -package client - -type CreateOptions struct { - Namespace string -} - -type GetOptions struct { - Labels map[string]string - Namespace string -} -type UpdateOptions struct { - Namespace string -} -type DeleteOptions struct { - Namespace string -} -type ListOptions struct { - Namespace string -} - -type LogOptions struct { - Params map[string]string - Namespace string -} - -type WatchOptions struct { - Params map[string]string - Namespace string -} - -type CreateOption func(*CreateOptions) -type GetOption func(*GetOptions) -type UpdateOption func(*UpdateOptions) -type DeleteOption func(*DeleteOptions) -type ListOption func(*ListOptions) -type LogOption func(*LogOptions) -type WatchOption func(*WatchOptions) - -// LogParams provides additional params for logs. -func LogParams(p map[string]string) LogOption { - return func(l *LogOptions) { - l.Params = p - } -} - -// WatchParams used for watch params. -func WatchParams(p map[string]string) WatchOption { - return func(w *WatchOptions) { - w.Params = p - } -} - -// CreateNamespace sets the namespace for creating a resource. -func CreateNamespace(ns string) CreateOption { - return func(o *CreateOptions) { - o.Namespace = SerializeResourceName(ns) - } -} - -// GetNamespace sets the namespace for getting a resource. -func GetNamespace(ns string) GetOption { - return func(o *GetOptions) { - o.Namespace = SerializeResourceName(ns) - } -} - -// GetLabels sets the labels for when getting a resource. -func GetLabels(ls map[string]string) GetOption { - return func(o *GetOptions) { - o.Labels = ls - } -} - -// UpdateNamespace sets the namespace for updating a resource. -func UpdateNamespace(ns string) UpdateOption { - return func(o *UpdateOptions) { - o.Namespace = SerializeResourceName(ns) - } -} - -// DeleteNamespace sets the namespace for deleting a resource. -func DeleteNamespace(ns string) DeleteOption { - return func(o *DeleteOptions) { - o.Namespace = SerializeResourceName(ns) - } -} - -// ListNamespace sets the namespace for listing resources. -func ListNamespace(ns string) ListOption { - return func(o *ListOptions) { - o.Namespace = SerializeResourceName(ns) - } -} - -// LogNamespace sets the namespace for logging a resource. -func LogNamespace(ns string) LogOption { - return func(o *LogOptions) { - o.Namespace = SerializeResourceName(ns) - } -} - -// WatchNamespace sets the namespace for watching a resource. -func WatchNamespace(ns string) WatchOption { - return func(o *WatchOptions) { - o.Namespace = SerializeResourceName(ns) - } -} diff --git a/util/kubernetes/client/templates.go b/util/kubernetes/client/templates.go deleted file mode 100644 index 92f80d0130..0000000000 --- a/util/kubernetes/client/templates.go +++ /dev/null @@ -1,167 +0,0 @@ -package client - -var templates = map[string]string{ - "deployment": deploymentTmpl, - "service": serviceTmpl, - "namespace": namespaceTmpl, - "secret": secretTmpl, - "serviceaccount": serviceAccountTmpl, -} - -// stripped image pull policy always -// imagePullPolicy: Always. -var deploymentTmpl = ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: "{{ .Metadata.Name }}" - namespace: "{{ .Metadata.Namespace }}" - labels: - {{- with .Metadata.Labels }} - {{- range $key, $value := . }} - {{ $key }}: "{{ $value }}" - {{- end }} - {{- end }} - annotations: - {{- with .Metadata.Annotations }} - {{- range $key, $value := . }} - {{ $key }}: "{{ $value }}" - {{- end }} - {{- end }} -spec: - replicas: {{ .Spec.Replicas }} - selector: - matchLabels: - {{- with .Spec.Selector.MatchLabels }} - {{- range $key, $value := . }} - {{ $key }}: "{{ $value }}" - {{- end }} - {{- end }} - template: - metadata: - labels: - {{- with .Spec.Template.Metadata.Labels }} - {{- range $key, $value := . }} - {{ $key }}: "{{ $value }}" - {{- end }} - {{- end }} - annotations: - {{- with .Spec.Template.Metadata.Annotations }} - {{- range $key, $value := . }} - {{ $key }}: "{{ $value }}" - {{- end }} - {{- end }} - spec: - serviceAccountName: {{ .Spec.Template.PodSpec.ServiceAccountName }} - containers: - {{- with .Spec.Template.PodSpec.Containers }} - {{- range . }} - - name: {{ .Name }} - env: - {{- with .Env }} - {{- range . }} - - name: "{{ .Name }}" - value: "{{ .Value }}" - {{- end }} - {{- end }} - args: - {{- range .Args }} - - {{.}} - {{- end }} - command: - {{- range .Command }} - - {{.}} - {{- end }} - image: {{ .Image }} - ports: - {{- with .Ports }} - {{- range . }} - - containerPort: {{ .ContainerPort }} - name: {{ .Name }} - {{- end}} - {{- end}} - {{- end }} - {{- end}} -` - -var serviceTmpl = ` -apiVersion: v1 -kind: Service -metadata: - name: "{{ .Metadata.Name }}" - namespace: "{{ .Metadata.Namespace }}" - labels: - {{- with .Metadata.Labels }} - {{- range $key, $value := . }} - {{ $key }}: "{{ $value }}" - {{- end }} - {{- end }} -spec: - selector: - {{- with .Spec.Selector }} - {{- range $key, $value := . }} - {{ $key }}: "{{ $value }}" - {{- end }} - {{- end }} - ports: - {{- with .Spec.Ports }} - {{- range . }} - - name: "{{ .Name }}" - port: {{ .Port }} - protocol: {{ .Protocol }} - {{- end }} - {{- end }} -` - -var namespaceTmpl = ` -apiVersion: v1 -kind: Namespace -metadata: - name: "{{ .Metadata.Name }}" - labels: - {{- with .Metadata.Labels }} - {{- range $key, $value := . }} - {{ $key }}: "{{ $value }}" - {{- end }} - {{- end }} -` - -var secretTmpl = ` -apiVersion: v1 -kind: Secret -type: "{{ .Type }}" -metadata: - name: "{{ .Metadata.Name }}" - namespace: "{{ .Metadata.Namespace }}" - labels: - {{- with .Metadata.Labels }} - {{- range $key, $value := . }} - {{ $key }}: "{{ $value }}" - {{- end }} - {{- end }} -data: - {{- with .Data }} - {{- range $key, $value := . }} - {{ $key }}: "{{ $value }}" - {{- end }} - {{- end }} -` - -var serviceAccountTmpl = ` -apiVersion: v1 -kind: ServiceAccount -metadata: - name: "{{ .Metadata.Name }}" - labels: - {{- with .Metadata.Labels }} - {{- range $key, $value := . }} - {{ $key }}: "{{ $value }}" - {{- end }} - {{- end }} -imagePullSecrets: -{{- with .ImagePullSecrets }} -{{- range . }} -- name: "{{ .Name }}" -{{- end }} -{{- end }} -` diff --git a/util/kubernetes/client/types.go b/util/kubernetes/client/types.go deleted file mode 100644 index e54c1aa099..0000000000 --- a/util/kubernetes/client/types.go +++ /dev/null @@ -1,209 +0,0 @@ -package client - -// ContainerPort. -type ContainerPort struct { - Name string `json:"name,omitempty"` - Protocol string `json:"protocol,omitempty"` - HostPort int `json:"hostPort,omitempty"` - ContainerPort int `json:"containerPort"` -} - -// EnvVar is environment variable. -type EnvVar struct { - Name string `json:"name"` - Value string `json:"value,omitempty"` -} - -type Condition struct { - Started string `json:"startedAt,omitempty"` - Reason string `json:"reason,omitempty"` - Message string `json:"message,omitempty"` -} - -// Container defined container runtime values. -type Container struct { - Name string `json:"name"` - Image string `json:"image"` - Env []EnvVar `json:"env,omitempty"` - Command []string `json:"command,omitempty"` - Args []string `json:"args,omitempty"` - Ports []ContainerPort `json:"ports,omitempty"` -} - -// DeploymentSpec defines micro deployment spec. -type DeploymentSpec struct { - Selector *LabelSelector `json:"selector"` - Template *Template `json:"template,omitempty"` - Replicas int `json:"replicas,omitempty"` -} - -// DeploymentCondition describes the state of deployment. -type DeploymentCondition struct { - LastUpdateTime string `json:"lastUpdateTime"` - Type string `json:"type"` - Reason string `json:"reason,omitempty"` - Message string `json:"message,omitempty"` -} - -// DeploymentStatus is returned when querying deployment. -type DeploymentStatus struct { - Conditions []DeploymentCondition `json:"conditions,omitempty"` - Replicas int `json:"replicas,omitempty"` - UpdatedReplicas int `json:"updatedReplicas,omitempty"` - ReadyReplicas int `json:"readyReplicas,omitempty"` - AvailableReplicas int `json:"availableReplicas,omitempty"` - UnavailableReplicas int `json:"unavailableReplicas,omitempty"` -} - -// Deployment is Kubernetes deployment. -type Deployment struct { - Metadata *Metadata `json:"metadata"` - Spec *DeploymentSpec `json:"spec,omitempty"` - Status *DeploymentStatus `json:"status,omitempty"` -} - -// DeploymentList. -type DeploymentList struct { - Items []Deployment `json:"items"` -} - -// LabelSelector is a label query over a set of resources -// NOTE: we do not support MatchExpressions at the moment. -type LabelSelector struct { - MatchLabels map[string]string `json:"matchLabels,omitempty"` -} - -type LoadBalancerIngress struct { - IP string `json:"ip,omitempty"` - Hostname string `json:"hostname,omitempty"` -} - -type LoadBalancerStatus struct { - Ingress []LoadBalancerIngress `json:"ingress,omitempty"` -} - -// Metadata defines api object metadata. -type Metadata struct { - Labels map[string]string `json:"labels,omitempty"` - Annotations map[string]string `json:"annotations,omitempty"` - Name string `json:"name,omitempty"` - Namespace string `json:"namespace,omitempty"` - Version string `json:"version,omitempty"` -} - -// PodSpec is a pod. -type PodSpec struct { - ServiceAccountName string `json:"serviceAccountName"` - Containers []Container `json:"containers"` -} - -// PodList. -type PodList struct { - Items []Pod `json:"items"` -} - -// Pod is the top level item for a pod. -type Pod struct { - Metadata *Metadata `json:"metadata"` - Spec *PodSpec `json:"spec,omitempty"` - Status *PodStatus `json:"status"` -} - -// PodStatus. -type PodStatus struct { - PodIP string `json:"podIP"` - Phase string `json:"phase"` - Reason string `json:"reason"` - Conditions []PodCondition `json:"conditions,omitempty"` - Containers []ContainerStatus `json:"containerStatuses"` -} - -// PodCondition describes the state of pod. -type PodCondition struct { - Type string `json:"type"` - Reason string `json:"reason,omitempty"` - Message string `json:"message,omitempty"` -} - -type ContainerStatus struct { - State ContainerState `json:"state"` -} - -type ContainerState struct { - Running *Condition `json:"running"` - Terminated *Condition `json:"terminated"` - Waiting *Condition `json:"waiting"` -} - -// Resource is API resource. -type Resource struct { - Value interface{} - Name string - Kind string -} - -// ServicePort configures service ports. -type ServicePort struct { - Name string `json:"name,omitempty"` - Protocol string `json:"protocol,omitempty"` - Port int `json:"port"` -} - -// ServiceSpec provides service configuration. -type ServiceSpec struct { - ClusterIP string `json:"clusterIP"` - Type string `json:"type,omitempty"` - Selector map[string]string `json:"selector,omitempty"` - Ports []ServicePort `json:"ports,omitempty"` -} - -// ServiceStatus. -type ServiceStatus struct { - LoadBalancer LoadBalancerStatus `json:"loadBalancer,omitempty"` -} - -// Service is kubernetes service. -type Service struct { - Metadata *Metadata `json:"metadata"` - Spec *ServiceSpec `json:"spec,omitempty"` - Status *ServiceStatus `json:"status,omitempty"` -} - -// ServiceList. -type ServiceList struct { - Items []Service `json:"items"` -} - -// Template is micro deployment template. -type Template struct { - Metadata *Metadata `json:"metadata,omitempty"` - PodSpec *PodSpec `json:"spec,omitempty"` -} - -// Namespace is a Kubernetes Namespace. -type Namespace struct { - Metadata *Metadata `json:"metadata,omitempty"` -} - -// NamespaceList. -type NamespaceList struct { - Items []Namespace `json:"items"` -} - -// ImagePullSecret. -type ImagePullSecret struct { - Name string `json:"name"` -} - -// Secret. -type Secret struct { - Data map[string]string `json:"data"` - Metadata *Metadata `json:"metadata"` - Type string `json:"type,omitempty"` -} - -// ServiceAccount. -type ServiceAccount struct { - Metadata *Metadata `json:"metadata,omitempty"` - ImagePullSecrets []ImagePullSecret `json:"imagePullSecrets,omitempty"` -} diff --git a/util/kubernetes/client/util.go b/util/kubernetes/client/util.go deleted file mode 100644 index ebce6d794b..0000000000 --- a/util/kubernetes/client/util.go +++ /dev/null @@ -1,104 +0,0 @@ -package client - -import ( - "crypto/x509" - "encoding/pem" - "errors" - "fmt" - "io" - "os" - "strings" - "text/template" -) - -// renderTemplateFile renders template for a given resource into writer w. -func renderTemplate(resource string, w io.Writer, data interface{}) error { - t := template.Must(template.New("kubernetes").Parse(templates[resource])) - - if err := t.Execute(w, data); err != nil { - return err - } - - return nil -} - -// COPIED FROM -// https://github.com/kubernetes/kubernetes/blob/7a725418af4661067b56506faabc2d44c6d7703a/pkg/util/crypto/crypto.go - -// CertPoolFromFile returns an x509.CertPool containing the certificates in the given PEM-encoded file. -// Returns an error if the file could not be read, a certificate could not be parsed, or if the file does not contain any certificates. -func CertPoolFromFile(filename string) (*x509.CertPool, error) { - certs, err := certificatesFromFile(filename) - if err != nil { - return nil, err - } - pool := x509.NewCertPool() - for _, cert := range certs { - pool.AddCert(cert) - } - return pool, nil -} - -// certificatesFromFile returns the x509.Certificates contained in the given PEM-encoded file. -// Returns an error if the file could not be read, a certificate could not be parsed, or if the file does not contain any certificates. -func certificatesFromFile(file string) ([]*x509.Certificate, error) { - if len(file) == 0 { - return nil, errors.New("error reading certificates from an empty filename") - } - pemBlock, err := os.ReadFile(file) - if err != nil { - return nil, err - } - certs, err := CertsFromPEM(pemBlock) - if err != nil { - return nil, fmt.Errorf("error reading %s: %s", file, err) - } - return certs, nil -} - -// CertsFromPEM returns the x509.Certificates contained in the given PEM-encoded byte array -// Returns an error if a certificate could not be parsed, or if the data does not contain any certificates. -func CertsFromPEM(pemCerts []byte) ([]*x509.Certificate, error) { - ok := false - certs := []*x509.Certificate{} - for len(pemCerts) > 0 { - var block *pem.Block - block, pemCerts = pem.Decode(pemCerts) - if block == nil { - break - } - // Only use PEM "CERTIFICATE" blocks without extra headers - if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { - continue - } - - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return certs, err - } - - certs = append(certs, cert) - ok = true - } - - if !ok { - return certs, errors.New("could not read any certificates") - } - return certs, nil -} - -// Format is used to format a string value into a k8s valid name. -func Format(v string) string { - // to lower case - v = strings.ToLower(v) - // / to dashes - v = strings.ReplaceAll(v, "/", "-") - // dots to dashes - v = strings.ReplaceAll(v, ".", "-") - // limit to 253 chars - if len(v) > 253 { - v = v[:253] - } - // return new name - return v -} diff --git a/util/kubernetes/client/util_test.go b/util/kubernetes/client/util_test.go deleted file mode 100644 index 8073a049a0..0000000000 --- a/util/kubernetes/client/util_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package client - -import ( - "bytes" - "testing" -) - -func TestTemplates(t *testing.T) { - name := "foo" - version := "123" - typ := "service" - namespace := "default" - - // Render default service - s := NewService(name, version, typ, namespace) - bs := new(bytes.Buffer) - if err := renderTemplate(templates["service"], bs, s); err != nil { - t.Errorf("Failed to render kubernetes service: %v", err) - } - - // Render default deployment - d := NewDeployment(name, version, typ, namespace) - bd := new(bytes.Buffer) - if err := renderTemplate(templates["deployment"], bd, d); err != nil { - t.Errorf("Failed to render kubernetes deployment: %v", err) - } -} - -func TestFormatName(t *testing.T) { - testCases := []struct { - name string - expect string - }{ - {"foobar", "foobar"}, - {"foo-bar", "foo-bar"}, - {"foo.bar", "foo-bar"}, - {"Foo.Bar", "foo-bar"}, - {"go.micro.foo.bar", "go-micro-foo-bar"}, - } - - for _, test := range testCases { - v := Format(test.name) - if v != test.expect { - t.Fatalf("Expected name %s for %s got: %s", test.expect, test.name, v) - } - } -} diff --git a/util/kubernetes/client/watch.go b/util/kubernetes/client/watch.go deleted file mode 100644 index 97b9e18dfd..0000000000 --- a/util/kubernetes/client/watch.go +++ /dev/null @@ -1,124 +0,0 @@ -package client - -import ( - "bufio" - "context" - "encoding/json" - "errors" - "net/http" - - "go-micro.dev/v5/util/kubernetes/api" -) - -const ( - // EventTypes used. - Added EventType = "ADDED" - Modified EventType = "MODIFIED" - Deleted EventType = "DELETED" - Error EventType = "ERROR" -) - -// Watcher is used to watch for events. -type Watcher interface { - // A channel of events - Chan() <-chan Event - // Stop the watcher - Stop() -} - -// EventType defines the possible types of events. -type EventType string - -// Event represents a single event to a watched resource. -type Event struct { - Type EventType `json:"type"` - Object json.RawMessage `json:"object"` -} - -// bodyWatcher scans the body of a request for chunks. -type bodyWatcher struct { - results chan Event - cancel func() - stop chan bool - res *http.Response - req *api.Request -} - -// Changes returns the results channel. -func (wr *bodyWatcher) Chan() <-chan Event { - return wr.results -} - -// Stop cancels the request. -func (wr *bodyWatcher) Stop() { - select { - case <-wr.stop: - return - default: - // cancel the request - wr.cancel() - // stop the watcher - close(wr.stop) - } -} - -func (wr *bodyWatcher) stream() { - reader := bufio.NewReader(wr.res.Body) - - go func() { - for { - // read a line - b, err := reader.ReadBytes('\n') - if err != nil { - return - } - - // send the event - var event Event - if err := json.Unmarshal(b, &event); err != nil { - continue - } - - select { - case <-wr.stop: - return - case wr.results <- event: - } - } - }() -} - -// newWatcher creates a k8s body watcher for -// a given http request. -func newWatcher(req *api.Request) (Watcher, error) { - // set request context so we can cancel the request - ctx, cancel := context.WithCancel(context.Background()) - req.Context(ctx) - - // do the raw request - res, err := req.Raw() - if err != nil { - cancel() - return nil, err - } - - if res.StatusCode < 200 || res.StatusCode >= 300 { - cancel() - // close the response body - res.Body.Close() - // return an error - return nil, errors.New(res.Request.URL.String() + ": " + res.Status) - } - - wr := &bodyWatcher{ - results: make(chan Event), - stop: make(chan bool), - cancel: cancel, - req: req, - res: res, - } - - go wr.stream() - - return wr, nil -}