From 288f3ab648c4f2ac17f96f4f915e36d9828c248a Mon Sep 17 00:00:00 2001 From: win Date: Thu, 22 Dec 2022 09:47:18 +0000 Subject: [PATCH] api client factory updated --- LICENSE | 42 +-- internal/gitapiclient/factory.go | 45 ++- internal/gitapiclient/factory_test.go | 28 ++ internal/gitapiclient/github.go | 45 ++- internal/gitapiclient/githubimpl.go | 77 +++-- internal/gitapiclient/interface.go | 22 +- internal/httpclient/httpclient.go | 258 +++++++------- internal/httpclient/interface.go | 24 +- .../testdata/test1_deployment.yaml | 98 +++--- .../testdata/test2_deployment.yaml | 40 +-- .../testdata/test3_deployment.yaml | 98 +++--- internal/updateManifest/update.go | 184 +++++----- internal/updateManifest/update_test.go | 318 +++++++++--------- 13 files changed, 668 insertions(+), 611 deletions(-) create mode 100644 internal/gitapiclient/factory_test.go diff --git a/LICENSE b/LICENSE index 255b46c..87017be 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2022 - Sheet Pilot - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2022 - Sheet Pilot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/internal/gitapiclient/factory.go b/internal/gitapiclient/factory.go index 020f9c5..2da9e0d 100644 --- a/internal/gitapiclient/factory.go +++ b/internal/gitapiclient/factory.go @@ -1,16 +1,29 @@ -package gitapiclient - -import "fmt" - -func GetGitApiClient(clientName string) (iGitClient, error) { - if clientName == "github" { - client := newGithubClient("actual repo path should be here /repo/path", "Dummy Token") - client.setApiHost("https://api.github.com/") - client.setToken("token") - - return client, nil - } - - // add new client name what ever you need - return nil, fmt.Errorf("Wrong Client passed") -} +package gitapiclient + +import ( + "fmt" + + "github.com/sheetpilot/sheetpilot/internal/httpclient" +) + +type gitApiClientFactory struct { + clientName string + token string +} + +func GitApiClientFactory(factoryArgs *gitApiClientFactory) (func(repoPath string) iGitClient, error) { + client := new(gitApiClientFactory) + return client.GetGitApiClient(factoryArgs) +} + +func (f *gitApiClientFactory) GetGitApiClient(args *gitApiClientFactory) (func(repoPath string) iGitClient, error) { + if args.clientName == "github" { + client := NewGitHubClient("https://api.github.com/", args.token) + client.githubapiClient(httpclient.NewHttpClient()) + + return client.githubapiClient(httpclient.NewHttpClient()), nil + } + + // add new client name what ever you need + return nil, fmt.Errorf("Wrong Client passed") +} diff --git a/internal/gitapiclient/factory_test.go b/internal/gitapiclient/factory_test.go new file mode 100644 index 0000000..c919a0d --- /dev/null +++ b/internal/gitapiclient/factory_test.go @@ -0,0 +1,28 @@ +package gitapiclient + +import ( + "testing" +) + +// TestHelloName calls greetings.Hello with a name, checking +// for a valid return value. +func TestHelloName(t *testing.T) { + clientArgs := new(gitApiClientFactory) + clientArgs.clientName = "github" + clientArgs.token = "xxxxx" + + client, _ := GitApiClientFactory(clientArgs) + client("/sample-deployment/contents/deployment/admin/deployment.yaml") + + if 1 == 1 { + t.Fatal("Failed Catastrphically") + // t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want) + } + + // name := "Gladys" + // want := regexp.MustCompile(`\b` + name + `\b`) + + // if !want.MatchString(msg) || err != nil { + // t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want) + // } +} diff --git a/internal/gitapiclient/github.go b/internal/gitapiclient/github.go index 628ac44..56c7fe6 100644 --- a/internal/gitapiclient/github.go +++ b/internal/gitapiclient/github.go @@ -1,17 +1,28 @@ -package gitapiclient - -type gitHubClient struct { - githubClientImpl -} - -func newGithubClient(repoPath string, token string) iGitClient { - - ghClient := new(gitHubClient) - - apiClient := new(githubClientImpl) - apiClient.setApiHost("https://api.github.com") - apiClient.setPath(repoPath) - apiClient.setToken(token) - ghClient.githubClientImpl = *apiClient - return ghClient -} +package gitapiclient + +import "github.com/sheetpilot/sheetpilot/internal/httpclient" + +type gitHubClient struct { + githubClientImpl +} + +func NewGitHubClient(host string, token string) gitHubClient { + apiclient := new(gitHubClient) + + apiclient.setApiHost(host) + apiclient.setToken(token) + return *apiclient +} + +func (gh *gitHubClient) githubapiClient(httpClient httpclient.IHttpClient) func(repoPath string) iGitClient { + + return func(repoPath string) iGitClient { + apiClient := new(githubClientImpl) + apiClient.setApiHost(gh.apiHost) + apiClient.setToken(gh.token) + apiClient.setHttpClient(httpClient) + apiClient.setPath(repoPath) + + return apiClient + } +} diff --git a/internal/gitapiclient/githubimpl.go b/internal/gitapiclient/githubimpl.go index 9b59e22..64f7c9d 100644 --- a/internal/gitapiclient/githubimpl.go +++ b/internal/gitapiclient/githubimpl.go @@ -1,35 +1,42 @@ -package gitapiclient - -type githubClientImpl struct { - apiHost string - token string - path string -} - -func (g *githubClientImpl) setApiHost(apiHost string) { - g.apiHost = apiHost -} - -func (g *githubClientImpl) GetApiHost() string { - return g.apiHost -} - -func (g *githubClientImpl) setToken(token string) { - g.token = token -} - -func (g *githubClientImpl) GetToken() string { - return g.token -} - -func (g *githubClientImpl) setPath(path string) { - g.path = path -} - -func (g *githubClientImpl) GetPath() string { - return g.path -} - -func (g *githubClientImpl) GetFileContentFromRepo() { - -} +package gitapiclient + +import "github.com/sheetpilot/sheetpilot/internal/httpclient" + +type githubClientImpl struct { + apiHost string + token string + path string + httpClient httpclient.IHttpClient +} + +func (g *githubClientImpl) setApiHost(apiHost string) { + g.apiHost = apiHost +} + +func (g *githubClientImpl) GetApiHost() string { + return g.apiHost +} + +func (g *githubClientImpl) setToken(token string) { + g.token = token +} + +func (g *githubClientImpl) GetToken() string { + return g.token +} + +func (g *githubClientImpl) setPath(path string) { + g.path = path +} + +func (g *githubClientImpl) GetPath() string { + return g.path +} + +func (g *githubClientImpl) setHttpClient(client httpclient.IHttpClient) { + g.httpClient = client +} + +func (g *githubClientImpl) GetHttpClient(client httpclient.IHttpClient) httpclient.IHttpClient { + return g.httpClient +} diff --git a/internal/gitapiclient/interface.go b/internal/gitapiclient/interface.go index 29e9a32..c58a76e 100644 --- a/internal/gitapiclient/interface.go +++ b/internal/gitapiclient/interface.go @@ -1,12 +1,10 @@ -package gitapiclient - -type iGitClient interface { - setApiHost(name string) - setToken(token string) - setPath(path string) - GetApiHost() string - GetToken() string - GetPath() string - - GetFileContentFromRepo() -} +package gitapiclient + +type iGitClient interface { + setApiHost(name string) + setToken(token string) + setPath(path string) + GetApiHost() string + GetToken() string + GetPath() string +} diff --git a/internal/httpclient/httpclient.go b/internal/httpclient/httpclient.go index 8276ea5..5c769c9 100644 --- a/internal/httpclient/httpclient.go +++ b/internal/httpclient/httpclient.go @@ -1,129 +1,129 @@ -package httpclient - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - "time" -) - -type httpClient struct { - client *http.Client - host string - bearerToken string -} - -func NewHttpClient() iHttpClient { - apiClient := new(httpClient) - - apiClient.client = new(http.Client) - apiClient.client.Timeout = 30 * time.Second - return apiClient -} - -func (h *httpClient) setHost(host string) iHttpClient { - h.host = host - return h -} - -func (h *httpClient) setBearerToken(token string) iHttpClient { - h.bearerToken = token - return h -} - -func (h *httpClient) setHeader(req *http.Request, queryParams map[string]interface{}) iHttpClient { - - keys := make([]string, 0) - for k, _ := range queryParams { - keys = append(keys, k) - } - - for _, key := range keys { - req.Header.Set(key, fmt.Sprint(queryParams[key])) - } - - return h -} - -func (h *httpClient) setTimeout(timeout int64) iHttpClient { - h.client.Timeout = time.Duration(timeout) * time.Second - return h -} - -func (h *httpClient) Get(path string, queryParams map[string]interface{}) ([]byte, error) { - - req, err := http.NewRequest(http.MethodGet, h.host+path, nil) - if err != nil { - log.Fatal(err) - } - - req.URL.RawQuery = getUrlQueryStringFromQueryParams(req, queryParams) - - resp, err := h.client.Do(req) - if err != nil { - fmt.Println("Errored when sending request") - return make([]byte, 0), err - } - - defer resp.Body.Close() - responseBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Fatal(err) - } - return responseBody, err - -} - -func (h *httpClient) Post(path string, queryParams map[string]interface{}, body map[string]interface{}) ([]byte, error) { - jsonBuffer, jsonError := prepareJsonPayload(body) - if jsonError != nil { - log.Fatal(jsonError) - } - - req, err := http.NewRequest(http.MethodPost, h.host+path, jsonBuffer) - if err != nil { - log.Fatal(err) - } - - req.URL.RawQuery = getUrlQueryStringFromQueryParams(req, queryParams) - - resp, err := h.client.Do(req) - if err != nil { - fmt.Println("Errored when sending request") - return make([]byte, 0), err - } - - defer resp.Body.Close() - responseBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Fatal(err) - } - return responseBody, err -} - -func prepareJsonPayload(values map[string]interface{}) (*bytes.Buffer, error) { - jsonData, err := json.Marshal(values) - if err != nil { - log.Fatal(err) - return nil, err - } - return bytes.NewBuffer(jsonData), nil - -} - -func getUrlQueryStringFromQueryParams(req *http.Request, queryParams map[string]interface{}) string { - q := req.URL.Query() - keys := make([]string, 0) - for k, _ := range queryParams { - keys = append(keys, k) - } - - for _, key := range keys { - q.Add(key, fmt.Sprint(queryParams[key])) - } - - return q.Encode() -} +package httpclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "time" +) + +type httpClient struct { + client *http.Client + host string + bearerToken string +} + +func NewHttpClient() IHttpClient { + apiClient := new(httpClient) + + apiClient.client = new(http.Client) + apiClient.client.Timeout = 30 * time.Second + return apiClient +} + +func (h *httpClient) setHost(host string) IHttpClient { + h.host = host + return h +} + +func (h *httpClient) setBearerToken(token string) IHttpClient { + h.bearerToken = token + return h +} + +func (h *httpClient) setHeader(req *http.Request, queryParams map[string]interface{}) IHttpClient { + + keys := make([]string, 0) + for k, _ := range queryParams { + keys = append(keys, k) + } + + for _, key := range keys { + req.Header.Set(key, fmt.Sprint(queryParams[key])) + } + + return h +} + +func (h *httpClient) setTimeout(timeout int64) IHttpClient { + h.client.Timeout = time.Duration(timeout) * time.Second + return h +} + +func (h *httpClient) Get(path string, queryParams map[string]interface{}) ([]byte, error) { + + req, err := http.NewRequest(http.MethodGet, h.host+path, nil) + if err != nil { + log.Fatal(err) + } + + req.URL.RawQuery = getUrlQueryStringFromQueryParams(req, queryParams) + + resp, err := h.client.Do(req) + if err != nil { + fmt.Println("Errored when sending request") + return make([]byte, 0), err + } + + defer resp.Body.Close() + responseBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + return responseBody, err + +} + +func (h *httpClient) Post(path string, queryParams map[string]interface{}, body map[string]interface{}) ([]byte, error) { + jsonBuffer, jsonError := prepareJsonPayload(body) + if jsonError != nil { + log.Fatal(jsonError) + } + + req, err := http.NewRequest(http.MethodPost, h.host+path, jsonBuffer) + if err != nil { + log.Fatal(err) + } + + req.URL.RawQuery = getUrlQueryStringFromQueryParams(req, queryParams) + + resp, err := h.client.Do(req) + if err != nil { + fmt.Println("Errored when sending request") + return make([]byte, 0), err + } + + defer resp.Body.Close() + responseBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + return responseBody, err +} + +func prepareJsonPayload(values map[string]interface{}) (*bytes.Buffer, error) { + jsonData, err := json.Marshal(values) + if err != nil { + log.Fatal(err) + return nil, err + } + return bytes.NewBuffer(jsonData), nil + +} + +func getUrlQueryStringFromQueryParams(req *http.Request, queryParams map[string]interface{}) string { + q := req.URL.Query() + keys := make([]string, 0) + for k, _ := range queryParams { + keys = append(keys, k) + } + + for _, key := range keys { + q.Add(key, fmt.Sprint(queryParams[key])) + } + + return q.Encode() +} diff --git a/internal/httpclient/interface.go b/internal/httpclient/interface.go index 05272b1..a0bc29a 100644 --- a/internal/httpclient/interface.go +++ b/internal/httpclient/interface.go @@ -1,12 +1,12 @@ -package httpclient - -import "net/http" - -type iHttpClient interface { - setHost(host string) iHttpClient - setHeader(req *http.Request, queryParams map[string]interface{}) iHttpClient - setBearerToken(token string) iHttpClient - setTimeout(seconds int64) iHttpClient - Get(path string, queryParams map[string]interface{}) ([]byte, error) - Post(path string, queryParams map[string]interface{}, body map[string]interface{}) ([]byte, error) -} +package httpclient + +import "net/http" + +type IHttpClient interface { + setHost(host string) IHttpClient + setHeader(req *http.Request, queryParams map[string]interface{}) IHttpClient + setBearerToken(token string) IHttpClient + setTimeout(seconds int64) IHttpClient + Get(path string, queryParams map[string]interface{}) ([]byte, error) + Post(path string, queryParams map[string]interface{}, body map[string]interface{}) ([]byte, error) +} diff --git a/internal/updateManifest/testdata/test1_deployment.yaml b/internal/updateManifest/testdata/test1_deployment.yaml index 234ba28..62e3a18 100644 --- a/internal/updateManifest/testdata/test1_deployment.yaml +++ b/internal/updateManifest/testdata/test1_deployment.yaml @@ -1,49 +1,49 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: test1 -spec: - replicas: 5 - selector: - matchLabels: - app: test1 - strategy: {} - template: - metadata: - creationTimestamp: null - labels: - app: test1 - spec: - containers: - - image: test1:1.14.2 - name: test1 - ports: - - containerPort: 80 - name: test1-tcp - protocol: TCP - readinessProbe: - exec: - command: - - ls - failureThreshold: 3 - initialDelaySeconds: 60 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 3 - resources: - limits: - cpu: "1" - memory: 1Gi - requests: - cpu: 500m - memory: 500Mi - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - dnsPolicy: ClusterFirst - restartPolicy: Always - schedulerName: default-scheduler - securityContext: {} - serviceAccountName: test1-service - terminationGracePeriodSeconds: 30 -status: {} +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + name: test1 +spec: + replicas: 5 + selector: + matchLabels: + app: test1 + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app: test1 + spec: + containers: + - image: test1:1.14.2 + name: test1 + ports: + - containerPort: 80 + name: test1-tcp + protocol: TCP + readinessProbe: + exec: + command: + - ls + failureThreshold: 3 + initialDelaySeconds: 60 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 3 + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 500m + memory: 500Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + serviceAccountName: test1-service + terminationGracePeriodSeconds: 30 +status: {} diff --git a/internal/updateManifest/testdata/test2_deployment.yaml b/internal/updateManifest/testdata/test2_deployment.yaml index 26fa44a..94435dc 100644 --- a/internal/updateManifest/testdata/test2_deployment.yaml +++ b/internal/updateManifest/testdata/test2_deployment.yaml @@ -1,21 +1,21 @@ -apiVersion: v1 -kind: Pod -metadata: - name: test2 - labels: - app: test2 -spec: - containers: - - name: test2 - image: nginx - ports: - - name: test2 - containerPort: 80 - protocol: TCP - resources: - limits: - cpu: 2000m - memory: 2000Mi - requests: - cpu: "1" +apiVersion: v1 +kind: Pod +metadata: + name: test2 + labels: + app: test2 +spec: + containers: + - name: test2 + image: nginx + ports: + - name: test2 + containerPort: 80 + protocol: TCP + resources: + limits: + cpu: 2000m + memory: 2000Mi + requests: + cpu: "1" memory: 1Gi \ No newline at end of file diff --git a/internal/updateManifest/testdata/test3_deployment.yaml b/internal/updateManifest/testdata/test3_deployment.yaml index e74857f..337985a 100644 --- a/internal/updateManifest/testdata/test3_deployment.yaml +++ b/internal/updateManifest/testdata/test3_deployment.yaml @@ -1,49 +1,49 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: test3 -spec: - replicas: 5 - selector: - matchLabels: - app: test3 - strategy: {} - template: - metadata: - creationTimestamp: null - labels: - app: test3 - spec: - containers: - - image: test3:1.14.2 - name: test3 - ports: - - containerPort: 80 - name: test3-tcp - protocol: TCP - readinessProbe: - exec: - command: - - ls - failureThreshold: 3 - initialDelaySeconds: 60 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 3 - resources: - limits: - cpu: 2000m - memory: 2000Mi - requests: - cpu: "1" - memory: 1Gi - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - dnsPolicy: ClusterFirst - restartPolicy: Always - schedulerName: default-scheduler - securityContext: {} - serviceAccountName: test3-service - terminationGracePeriodSeconds: 30 -status: {} +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + name: test3 +spec: + replicas: 5 + selector: + matchLabels: + app: test3 + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app: test3 + spec: + containers: + - image: test3:1.14.2 + name: test3 + ports: + - containerPort: 80 + name: test3-tcp + protocol: TCP + readinessProbe: + exec: + command: + - ls + failureThreshold: 3 + initialDelaySeconds: 60 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 3 + resources: + limits: + cpu: 2000m + memory: 2000Mi + requests: + cpu: "1" + memory: 1Gi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + serviceAccountName: test3-service + terminationGracePeriodSeconds: 30 +status: {} diff --git a/internal/updateManifest/update.go b/internal/updateManifest/update.go index 4ecfa0e..d293a1c 100644 --- a/internal/updateManifest/update.go +++ b/internal/updateManifest/update.go @@ -1,92 +1,92 @@ -package updateManifest - -import ( - "errors" - "io" - "log" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/cli-runtime/pkg/printers" - "k8s.io/client-go/kubernetes/scheme" -) - -var ( - ErrorNotSupport = errors.New("not support resource type") - ErrMismatch = errors.New("mismatch deployment name") -) - -// CheckDeployment check deployment name and return k8s Object if correct -func CheckDeployment(name string, yamlData []byte) (runtime.Object, error) { - //var deployment *appsv1.Deployment - decoder := scheme.Codecs.UniversalDeserializer() - obj, _, err := decoder.Decode(yamlData, nil, nil) - if err != nil { - log.Println(err) - } - - switch deployment := obj.(type) { - case *appsv1.Deployment: - if deployment.Name != name { - return nil, ErrMismatch - } - default: - return nil, ErrorNotSupport - } - - return obj, nil -} - -func int32Ptr(i int32) *int32 { - return &i -} - -// UpdateResourceValues update Deployment resource values -func UpdateResourceValues(obj runtime.Object, cpuRequest, memRequest, cpuLimit, memLimit string, replicaCount int32) runtime.Object { - deployment := obj.(*appsv1.Deployment) - cpuCondition := -1 - memCondition := -1 - - if cpuLimit != "" { - cpuLMT := resource.MustParse(cpuLimit) - cpuCondition = cpuLMT.Cmp(resource.MustParse(cpuRequest)) - } else { - cpuCondition = deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().Cmp(resource.MustParse(cpuRequest)) - } - - if memLimit != "" { - memLMT := resource.MustParse(memLimit) - memCondition = memLMT.Cmp(resource.MustParse(memRequest)) - } else { - memCondition = deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Cmp(resource.MustParse(memRequest)) - } - - if cpuRequest != "" && resource.MustParse(cpuRequest).Format == "DecimalSI" && cpuCondition != -1 { - deployment.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceCPU] = resource.MustParse(cpuRequest) - } - if memRequest != "" && resource.MustParse(memRequest).Format == "BinarySI" && memCondition != -1 { - deployment.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceMemory] = resource.MustParse(memRequest) - } - if cpuLimit != "" && resource.MustParse(cpuLimit).Format == "DecimalSI" && cpuCondition != -1 { - deployment.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceCPU] = resource.MustParse(cpuLimit) - } - - if memLimit != "" && resource.MustParse(memLimit).Format == "BinarySI" && memCondition != -1 { - deployment.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceMemory] = resource.MustParse(memLimit) - } - - deployment.Spec.Replicas = int32Ptr(replicaCount) - - return obj -} - -// PrintDeployment Print correct deployment yaml to file -func PrintDeployment(out io.Writer, obj runtime.Object) error { - printr := printers.NewTypeSetter(scheme.Scheme).ToPrinter(&printers.YAMLPrinter{}) - - deployment := obj.(*appsv1.Deployment) - - return printr.PrintObj(deployment, out) -} +package updateManifest + +import ( + "errors" + "io" + "log" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/cli-runtime/pkg/printers" + "k8s.io/client-go/kubernetes/scheme" +) + +var ( + ErrorNotSupport = errors.New("not support resource type") + ErrMismatch = errors.New("mismatch deployment name") +) + +// CheckDeployment check deployment name and return k8s Object if correct +func CheckDeployment(name string, yamlData []byte) (runtime.Object, error) { + //var deployment *appsv1.Deployment + decoder := scheme.Codecs.UniversalDeserializer() + obj, _, err := decoder.Decode(yamlData, nil, nil) + if err != nil { + log.Println(err) + } + + switch deployment := obj.(type) { + case *appsv1.Deployment: + if deployment.Name != name { + return nil, ErrMismatch + } + default: + return nil, ErrorNotSupport + } + + return obj, nil +} + +func int32Ptr(i int32) *int32 { + return &i +} + +// UpdateResourceValues update Deployment resource values +func UpdateResourceValues(obj runtime.Object, cpuRequest, memRequest, cpuLimit, memLimit string, replicaCount int32) runtime.Object { + deployment := obj.(*appsv1.Deployment) + cpuCondition := -1 + memCondition := -1 + + if cpuLimit != "" { + cpuLMT := resource.MustParse(cpuLimit) + cpuCondition = cpuLMT.Cmp(resource.MustParse(cpuRequest)) + } else { + cpuCondition = deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().Cmp(resource.MustParse(cpuRequest)) + } + + if memLimit != "" { + memLMT := resource.MustParse(memLimit) + memCondition = memLMT.Cmp(resource.MustParse(memRequest)) + } else { + memCondition = deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Cmp(resource.MustParse(memRequest)) + } + + if cpuRequest != "" && resource.MustParse(cpuRequest).Format == "DecimalSI" && cpuCondition != -1 { + deployment.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceCPU] = resource.MustParse(cpuRequest) + } + if memRequest != "" && resource.MustParse(memRequest).Format == "BinarySI" && memCondition != -1 { + deployment.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceMemory] = resource.MustParse(memRequest) + } + if cpuLimit != "" && resource.MustParse(cpuLimit).Format == "DecimalSI" && cpuCondition != -1 { + deployment.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceCPU] = resource.MustParse(cpuLimit) + } + + if memLimit != "" && resource.MustParse(memLimit).Format == "BinarySI" && memCondition != -1 { + deployment.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceMemory] = resource.MustParse(memLimit) + } + + deployment.Spec.Replicas = int32Ptr(replicaCount) + + return obj +} + +// PrintDeployment Print correct deployment yaml to file +func PrintDeployment(out io.Writer, obj runtime.Object) error { + printr := printers.NewTypeSetter(scheme.Scheme).ToPrinter(&printers.YAMLPrinter{}) + + deployment := obj.(*appsv1.Deployment) + + return printr.PrintObj(deployment, out) +} diff --git a/internal/updateManifest/update_test.go b/internal/updateManifest/update_test.go index e3f9006..4cf52ff 100644 --- a/internal/updateManifest/update_test.go +++ b/internal/updateManifest/update_test.go @@ -1,159 +1,159 @@ -package updateManifest - -import ( - "errors" - appsv1 "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/api/resource" - "os" - "testing" -) - -func TestCheckDeployment(t *testing.T) { - var testCases = []struct { - name string - app string - deploymentPath string - expErr error - }{ - {"SUCCESS", "test1", "./testdata/test1_deployment.yaml", nil}, - {"NOTSUPPORT", "test2", "./testdata/test2_deployment.yaml", ErrorNotSupport}, - {"MisMatch", "test4", "./testdata/test3_deployment.yaml", ErrMismatch}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - yamlData, err := os.ReadFile(tc.deploymentPath) - if err != nil { - t.Fatal(err) - } - _, err = CheckDeployment(tc.app, yamlData) - if tc.expErr != nil { - if err == nil { - t.Errorf("Expected error: %q. Got 'nil' instead.", tc.expErr) - return - } - if !errors.Is(err, tc.expErr) { - t.Errorf("Expected error: %q. Got %q.", tc.expErr, err) - } - return - } - if err != nil { - t.Errorf("Unexpected error: %q", err) - } - }) - } -} - -func TestUpdateResourceValues(t *testing.T) { - var testCases = []struct { - name string - app string - deploymentPath string - updateReqCPU string - updateReqMem string - updateLmtCPU string - updateLmtMem string - updateRplCount int32 - expReqCPU string - expReqMem string - expLmtCPU string - expLmtMem string - expRplCount int32 - }{ - { - "SuccessTest1", - "test1", - "./testdata/test1_deployment.yaml", - "100m", - "500Mi", - "300m", - "1Gi", - int32(1), - "100m", - "500Mi", - "300m", - "1Gi", - int32(1), - }, - { - "FailTest1", - "test1", - "./testdata/test1_deployment.yaml", - "300m", - "1Gi", - "100m", - "500Mi", - int32(1), - "500m", - "500Mi", - "1", - "1Gi", - int32(1), - }, - { - "SuccessEmptyLimitTest2", - "test3", - "./testdata/test3_deployment.yaml", - "1100m", - "1500Mi", - "", - "", - int32(2), - "1100m", - "1500Mi", - "2000m", - "2000Mi", - int32(2), - }, - { - "FailEmptyLimitTest2", - "test3", - "./testdata/test3_deployment.yaml", - "2100m", - "2100Mi", - "", - "", - int32(2), - "1", - "1Gi", - "2000m", - "2000Mi", - int32(2), - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - yamlData, err := os.ReadFile(tc.deploymentPath) - if err != nil { - t.Fatal(err) - } - obj, err := CheckDeployment(tc.app, yamlData) - if err != nil { - t.Fatal(err) - } - updatedObj := UpdateResourceValues(obj, tc.updateReqCPU, tc.updateReqMem, tc.updateLmtCPU, tc.updateLmtMem, tc.updateRplCount) - - deployment := updatedObj.(*appsv1.Deployment) - - if !deployment.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu().Equal(resource.MustParse(tc.expReqCPU)) { - t.Errorf("expected request CPU: %v, got %v instead.", deployment.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu(), tc.expReqCPU) - } - - if !deployment.Spec.Template.Spec.Containers[0].Resources.Requests.Memory().Equal(resource.MustParse(tc.expReqMem)) { - t.Errorf("expected request Memory: %v, go %v instead.", deployment.Spec.Template.Spec.Containers[0].Resources.Requests.Memory(), tc.expReqMem) - } - - if !deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().Equal(resource.MustParse(tc.expLmtCPU)) { - t.Errorf("expected limit CPU: %v, got %v instead.", deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu(), tc.expLmtCPU) - } - if !deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Equal(resource.MustParse(tc.expLmtMem)) { - t.Errorf("expected limit Memory: %v, got %v instead.", deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu(), tc.expLmtMem) - } - - if *deployment.Spec.Replicas != tc.expRplCount { - t.Errorf("expected replica count: %d, got %d instead.", *deployment.Spec.Replicas, tc.expRplCount) - } - }) - } -} +package updateManifest + +import ( + "errors" + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/api/resource" + "os" + "testing" +) + +func TestCheckDeployment(t *testing.T) { + var testCases = []struct { + name string + app string + deploymentPath string + expErr error + }{ + {"SUCCESS", "test1", "./testdata/test1_deployment.yaml", nil}, + {"NOTSUPPORT", "test2", "./testdata/test2_deployment.yaml", ErrorNotSupport}, + {"MisMatch", "test4", "./testdata/test3_deployment.yaml", ErrMismatch}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + yamlData, err := os.ReadFile(tc.deploymentPath) + if err != nil { + t.Fatal(err) + } + _, err = CheckDeployment(tc.app, yamlData) + if tc.expErr != nil { + if err == nil { + t.Errorf("Expected error: %q. Got 'nil' instead.", tc.expErr) + return + } + if !errors.Is(err, tc.expErr) { + t.Errorf("Expected error: %q. Got %q.", tc.expErr, err) + } + return + } + if err != nil { + t.Errorf("Unexpected error: %q", err) + } + }) + } +} + +func TestUpdateResourceValues(t *testing.T) { + var testCases = []struct { + name string + app string + deploymentPath string + updateReqCPU string + updateReqMem string + updateLmtCPU string + updateLmtMem string + updateRplCount int32 + expReqCPU string + expReqMem string + expLmtCPU string + expLmtMem string + expRplCount int32 + }{ + { + "SuccessTest1", + "test1", + "./testdata/test1_deployment.yaml", + "100m", + "500Mi", + "300m", + "1Gi", + int32(1), + "100m", + "500Mi", + "300m", + "1Gi", + int32(1), + }, + { + "FailTest1", + "test1", + "./testdata/test1_deployment.yaml", + "300m", + "1Gi", + "100m", + "500Mi", + int32(1), + "500m", + "500Mi", + "1", + "1Gi", + int32(1), + }, + { + "SuccessEmptyLimitTest2", + "test3", + "./testdata/test3_deployment.yaml", + "1100m", + "1500Mi", + "", + "", + int32(2), + "1100m", + "1500Mi", + "2000m", + "2000Mi", + int32(2), + }, + { + "FailEmptyLimitTest2", + "test3", + "./testdata/test3_deployment.yaml", + "2100m", + "2100Mi", + "", + "", + int32(2), + "1", + "1Gi", + "2000m", + "2000Mi", + int32(2), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + yamlData, err := os.ReadFile(tc.deploymentPath) + if err != nil { + t.Fatal(err) + } + obj, err := CheckDeployment(tc.app, yamlData) + if err != nil { + t.Fatal(err) + } + updatedObj := UpdateResourceValues(obj, tc.updateReqCPU, tc.updateReqMem, tc.updateLmtCPU, tc.updateLmtMem, tc.updateRplCount) + + deployment := updatedObj.(*appsv1.Deployment) + + if !deployment.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu().Equal(resource.MustParse(tc.expReqCPU)) { + t.Errorf("expected request CPU: %v, got %v instead.", deployment.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu(), tc.expReqCPU) + } + + if !deployment.Spec.Template.Spec.Containers[0].Resources.Requests.Memory().Equal(resource.MustParse(tc.expReqMem)) { + t.Errorf("expected request Memory: %v, go %v instead.", deployment.Spec.Template.Spec.Containers[0].Resources.Requests.Memory(), tc.expReqMem) + } + + if !deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu().Equal(resource.MustParse(tc.expLmtCPU)) { + t.Errorf("expected limit CPU: %v, got %v instead.", deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu(), tc.expLmtCPU) + } + if !deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Equal(resource.MustParse(tc.expLmtMem)) { + t.Errorf("expected limit Memory: %v, got %v instead.", deployment.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu(), tc.expLmtMem) + } + + if *deployment.Spec.Replicas != tc.expRplCount { + t.Errorf("expected replica count: %d, got %d instead.", *deployment.Spec.Replicas, tc.expRplCount) + } + }) + } +}