diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..aab3c9e --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +docker-compose/postgres diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..01c6fb3 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,31 @@ +name: ci + +on: + push: + branches: + - "main" + - "master" + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to ghcr + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{github.actor}} + password: ${{secrets.GH_TOKEN}} + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ghcr.io/harshsinghvi/golang-postgres-kubernetes:latest diff --git a/.gitignore b/.gitignore index 512d187..74c8e88 100644 --- a/.gitignore +++ b/.gitignore @@ -59,4 +59,6 @@ Temporary Items # End of https://www.toptal.com/developers/gitignore/api/go,macos -.env \ No newline at end of file +.env +docker-compose/ +__debug_bin* diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5ac2bb4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "./main.go" + } + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4bab49e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +############################ +# STEP 1 build executable binary +############################ +FROM golang:alpine AS builder +# Install git. +# Git is required for fetching the dependencies. +RUN apk update && apk add --no-cache 'git=~2' + +# Install dependencies +ENV GO111MODULE=on +WORKDIR $GOPATH/src/packages/goginapp/ +COPY . . + +# Fetch dependencies. +# Using go get. +RUN go get -d -v + +# Build the binary. +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /go/main . + +############################ +# STEP 2 build a small image +############################ +FROM alpine +LABEL org.opencontainers.image.source="https://github.com/harshsinghvi/golang-postgres-kubernetes" + +WORKDIR / + +# Copy our static executable. +COPY --from=builder /go/main /go/main +# COPY public /go/public + +ENV PORT 8080 +ENV GIN_MODE debug +EXPOSE 8080 + +WORKDIR /go + +# Run the Go Gin binary. +ENTRYPOINT ["/go/main"] diff --git a/README.md b/README.md index 62033df..0312ba4 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,313 @@ ## resources -- Linkedin Learning (kubernetes, golang courses) -- udemy hnsr course db -- https://go.dev/doc/tutorial/web-service-gin -- https://dev.to/ramu_mangalarapu/building-rest-apis-in-golang-go-gin-with-persistence-database-postgres-4616 \ No newline at end of file +- +- +- +- +- +- ghcr.io kubernetes + +- fargarte exose services +- alb imp +- eks ingress imp +- HPA + +- install Matrics server +- matrics server +- go and postgres eks + +- golang postgres api + +## TODOS + +- Golang API +- Deploy go API to Kubernetes + +- test autoscaling using Apache benchmark + +- setup CI/CD pipeline +- Connect external postgress to it +- deploy postgress to Kubernetes +- autoscale postgress deployment + +## K8S procedure + +1. eksctl faragete cluster `eksctl create cluster --name cluster --region ap-south-1 --fargate` +1. cluster ALB ingress +1. setup matrics server (for HPA) from YML +1. sertup efs (elastic file storage) get file_system_id and replace volumeHandle: fs-1234567899 in database.yml +1. ghcr secrets for image replace required fields in secrets.yml + +1. deploy services (yml files) yml files includes HPA + +## commands + +```bash +kubectl rollout restart deployment/name # to update image +kubectl get ingress # ingress exposed url +kubectl port-forward statefulset.apps/postgres 5432:5432 +kubectl exec --stdin --tty pod/postgres-0 -- /bin/bash +kubectl logs -f pod/go-todo-api-5587558c9b-zhb75 -c check-db-ready + +psql -h localhost -p 5432 -d postgres -U postgres + +aws eks update-kubeconfig --region ap-south-1 --name cluster +``` + +## GHCR image build and push + +`https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry` + +```bash +docker buildx build --platform=linux/amd64 -t golang-postgres-kubernetes . +docker tag golang-postgres-kubernetes ghcr.io/harshsinghvi/golang-postgres-kubernetes:latest +docker push ghcr.io/harshsinghvi/golang-postgres-kubernetes:latest +``` + +## ELB and ingress SETUP + +```bash +ACCOUNT_ID= # aws sts get-caller-identity +AWS_EKS_CLUSTER_NAME=cluster +AWS_EKS_CLUSTER_REGION=ap-south-1 + +AWS_EKS_CLUSTER_VPC_ID=$(aws eks describe-cluster \ + --name $AWS_EKS_CLUSTER_NAME \ + --query "cluster.resourcesVpcConfig.vpcId" \ + --output text) + +# AWS_EKS_CLUSTER_VPC_ID= # console>cloudformations + +curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.5.4/docs/install/iam_policy.json + +aws iam create-policy \ + --policy-name AWSLoadBalancerControllerIAMPolicy \ + --policy-document file://iam_policy.json + +eksctl utils associate-iam-oidc-provider --region=ap-south-1 --cluster=cluster --approve + +eksctl create iamserviceaccount \ + --cluster=cluster \ + --namespace=kube-system \ + --name=aws-load-balancer-controller \ + --role-name AmazonEKSLoadBalancerControllerRole \ + --attach-policy-arn=arn:aws:iam::194505915562:policy/AWSLoadBalancerControllerIAMPolicy \ + --approve + +helm repo add eks https://aws.github.io/eks-charts + +aws sts get-caller-identity + +helm install aws-load-balancer-controller eks/aws-load-balancer-controller \ + -n kube-system \ + --set clusterName=cluster \ + --set serviceAccount.create=false \ + --set serviceAccount.name=aws-load-balancer-controller \ + --set region=ap-south-1 \ + --set vpcId=vpc-07ae5f71518dd2545 + +kubectl get deployment -n kube-system aws-load-balancer-controller + + # during upgrade + kubectl apply -k "github.com/aws/eks-charts/stable/aws-load-balancer-controller/crds?ref=master" + + helm upgrade aws-load-balancer-controller eks/aws-load-balancer-controller \ + -n kube-system \ + --set clusterName=cluster \ + --set serviceAccount.create=false \ + --set serviceAccount.name=aws-load-balancer-controller \ + --set region=ap-south-1 \ + --set vpcId=vpc-07ae5f71518dd2545 +``` + +## EFS Setup + +```bash +AWS_EKS_CLUSTER_NAME=cluster +AWS_EKS_CLUSTER_REGION=ap-south-1 + +vpc_id=$(aws eks describe-cluster \ + --name $AWS_EKS_CLUSTER_NAME \ + --query "cluster.resourcesVpcConfig.vpcId" \ + --output text) + +cidr_range=$(aws ec2 describe-vpcs \ + --vpc-ids $vpc_id \ + --query "Vpcs[].CidrBlock" \ + --output text \ + --region $AWS_EKS_CLUSTER_REGION) + +security_group_id=$(aws ec2 create-security-group \ + --group-name MyEfsSecurityGroup \ + --description "My EFS security group" \ + --vpc-id $vpc_id \ + --output text) + +aws ec2 authorize-security-group-ingress \ + --group-id $security_group_id \ + --protocol tcp \ + --port 2049 \ + --cidr $cidr_range + +file_system_id=$(aws efs create-file-system \ + --region ap-south-1 \ + --performance-mode generalPurpose \ + --query 'FileSystemId' \ + --output text) + +aws ec2 describe-subnets \ + --filters "Name=vpc-id,Values=$vpc_id" \ + --query 'Subnets[*].{SubnetId: SubnetId,AvailabilityZone: AvailabilityZone,CidrBlock: CidrBlock}' \ + --output table + +# run for each subnet +aws efs create-mount-target \ + --file-system-id $file_system_id \ + --subnet-id subnet-09555c7ce2147f642 \ + --security-groups $security_group_id +aws efs create-mount-target \ + --file-system-id $file_system_id \ + --subnet-id subnet-019b6e706b2823a7b \ + --security-groups $security_group_id +aws efs create-mount-target \ + --file-system-id $file_system_id \ + --subnet-id subnet-0324d7a94eb3afd09 \ + --security-groups $security_group_id +aws efs create-mount-target \ + --file-system-id $file_system_id \ + --subnet-id subnet-04d07f3812cf78123 \ + --security-groups $security_group_id +aws efs create-mount-target \ + --file-system-id $file_system_id \ + --subnet-id subnet-0ee5c658df8ef377c \ + --security-groups $security_group_id +aws efs create-mount-target \ + --file-system-id $file_system_id \ + --subnet-id subnet-0360ff2918bf5fceb \ + --security-groups $security_group_id +``` + +## AUTOSCALE LOGS HPA + +`kubectl get hpa --watch` + +```text +go-todo-api-hpa Deployment/go-todo-api 15%/30% 1 10 2 19m +go-todo-api-hpa Deployment/go-todo-api 14%/30% 1 10 1 19m +go-todo-api-hpa Deployment/go-todo-api 15%/30% 1 10 1 19m +go-todo-api-hpa Deployment/go-todo-api 14%/30% 1 10 1 20m +go-todo-api-hpa Deployment/go-todo-api 15%/30% 1 10 1 20m +go-todo-api-hpa Deployment/go-todo-api 14%/30% 1 10 1 20m +go-todo-api-hpa Deployment/go-todo-api 22%/30% 1 10 1 21m +go-todo-api-hpa Deployment/go-todo-api 26%/30% 1 10 1 22m +go-todo-api-hpa Deployment/go-todo-api 26%/30% 1 10 1 22m +go-todo-api-hpa Deployment/go-todo-api 27%/30% 1 10 1 22m +go-todo-api-hpa Deployment/go-todo-api 25%/30% 1 10 1 22m +go-todo-api-hpa Deployment/go-todo-api 26%/30% 1 10 1 23m +go-todo-api-hpa Deployment/go-todo-api 26%/30% 1 10 1 23m +go-todo-api-hpa Deployment/go-todo-api 27%/30% 1 10 1 23m +go-todo-api-hpa Deployment/go-todo-api 26%/30% 1 10 1 23m +go-todo-api-hpa Deployment/go-todo-api 36%/30% 1 10 1 24m +go-todo-api-hpa Deployment/go-todo-api 37%/30% 1 10 2 24m +go-todo-api-hpa Deployment/go-todo-api 38%/30% 1 10 2 24m +go-todo-api-hpa Deployment/go-todo-api 38%/30% 1 10 2 24m +go-todo-api-hpa Deployment/go-todo-api 37%/30% 1 10 2 25m +go-todo-api-hpa Deployment/go-todo-api 29%/30% 1 10 2 25m +go-todo-api-hpa Deployment/go-todo-api 22%/30% 1 10 2 25m +go-todo-api-hpa Deployment/go-todo-api 21%/30% 1 10 2 26m +go-todo-api-hpa Deployment/go-todo-api 21%/30% 1 10 2 26m +go-todo-api-hpa Deployment/go-todo-api 21%/30% 1 10 2 27m +go-todo-api-hpa Deployment/go-todo-api 21%/30% 1 10 2 28m +go-todo-api-hpa Deployment/go-todo-api 21%/30% 1 10 2 28m +go-todo-api-hpa Deployment/go-todo-api 21%/30% 1 10 2 28m +go-todo-api-hpa Deployment/go-todo-api 21%/30% 1 10 2 29m +go-todo-api-hpa Deployment/go-todo-api 67%/30% 1 10 2 29m +go-todo-api-hpa Deployment/go-todo-api 73%/30% 1 10 4 29m +go-todo-api-hpa Deployment/go-todo-api 75%/30% 1 10 5 29m +go-todo-api-hpa Deployment/go-todo-api 74%/30% 1 10 5 30m +go-todo-api-hpa Deployment/go-todo-api 73%/30% 1 10 5 30m +go-todo-api-hpa Deployment/go-todo-api 72%/30% 1 10 5 30m +go-todo-api-hpa Deployment/go-todo-api 23%/30% 1 10 5 30m +go-todo-api-hpa Deployment/go-todo-api 8%/30% 1 10 5 31m +go-todo-api-hpa Deployment/go-todo-api 6%/30% 1 10 5 31m +go-todo-api-hpa Deployment/go-todo-api 3%/30% 1 10 5 31m +go-todo-api-hpa Deployment/go-todo-api 3%/30% 1 10 5 31m +go-todo-api-hpa Deployment/go-todo-api 3%/30% 1 10 5 32m +go-todo-api-hpa Deployment/go-todo-api 28%/30% 1 10 5 32m +go-todo-api-hpa Deployment/go-todo-api 40%/30% 1 10 5 32m +go-todo-api-hpa Deployment/go-todo-api 40%/30% 1 10 7 32m +go-todo-api-hpa Deployment/go-todo-api 39%/30% 1 10 7 33m +go-todo-api-hpa Deployment/go-todo-api 43%/30% 1 10 7 33m +go-todo-api-hpa Deployment/go-todo-api 36%/30% 1 10 7 33m +go-todo-api-hpa Deployment/go-todo-api 22%/30% 1 10 7 33m +go-todo-api-hpa Deployment/go-todo-api 15%/30% 1 10 7 34m +go-todo-api-hpa Deployment/go-todo-api 13%/30% 1 10 7 34m +go-todo-api-hpa Deployment/go-todo-api 7%/30% 1 10 7 34m +go-todo-api-hpa Deployment/go-todo-api 6%/30% 1 10 7 34m +go-todo-api-hpa Deployment/go-todo-api 7%/30% 1 10 7 35m +go-todo-api-hpa Deployment/go-todo-api 19%/30% 1 10 7 35m +go-todo-api-hpa Deployment/go-todo-api 32%/30% 1 10 7 35m +go-todo-api-hpa Deployment/go-todo-api 33%/30% 1 10 7 36m +go-todo-api-hpa Deployment/go-todo-api 42%/30% 1 10 7 36m +go-todo-api-hpa Deployment/go-todo-api 38%/30% 1 10 7 36m +go-todo-api-hpa Deployment/go-todo-api 24%/30% 1 10 7 36m +go-todo-api-hpa Deployment/go-todo-api 17%/30% 1 10 7 37m +go-todo-api-hpa Deployment/go-todo-api 16%/30% 1 10 7 37m +go-todo-api-hpa Deployment/go-todo-api 9%/30% 1 10 7 37m +go-todo-api-hpa Deployment/go-todo-api 9%/30% 1 10 7 37m +go-todo-api-hpa Deployment/go-todo-api 10%/30% 1 10 7 38m +go-todo-api-hpa Deployment/go-todo-api 19%/30% 1 10 7 38m +go-todo-api-hpa Deployment/go-todo-api 24%/30% 1 10 7 38m +go-todo-api-hpa Deployment/go-todo-api 31%/30% 1 10 7 39m +go-todo-api-hpa Deployment/go-todo-api 44%/30% 1 10 7 39m +go-todo-api-hpa Deployment/go-todo-api 45%/30% 1 10 7 39m +go-todo-api-hpa Deployment/go-todo-api 34%/30% 1 10 7 39m +go-todo-api-hpa Deployment/go-todo-api 21%/30% 1 10 7 40m +go-todo-api-hpa Deployment/go-todo-api 22%/30% 1 10 7 40m +go-todo-api-hpa Deployment/go-todo-api 13%/30% 1 10 7 40m +go-todo-api-hpa Deployment/go-todo-api 30%/30% 1 10 7 41m +go-todo-api-hpa Deployment/go-todo-api 40%/30% 1 10 7 41m +go-todo-api-hpa Deployment/go-todo-api 38%/30% 1 10 7 41m +go-todo-api-hpa Deployment/go-todo-api 36%/30% 1 10 7 42m +go-todo-api-hpa Deployment/go-todo-api 32%/30% 1 10 7 42m +go-todo-api-hpa Deployment/go-todo-api 31%/30% 1 10 7 42m +go-todo-api-hpa Deployment/go-todo-api 29%/30% 1 10 7 43m +go-todo-api-hpa Deployment/go-todo-api 25%/30% 1 10 7 43m +go-todo-api-hpa Deployment/go-todo-api 20%/30% 1 10 7 44m +go-todo-api-hpa Deployment/go-todo-api 19%/30% 1 10 7 44m +go-todo-api-hpa Deployment/go-todo-api 19%/30% 1 10 7 44m +go-todo-api-hpa Deployment/go-todo-api 21%/30% 1 10 7 44m +go-todo-api-hpa Deployment/go-todo-api 19%/30% 1 10 7 45m +go-todo-api-hpa Deployment/go-todo-api 16%/30% 1 10 7 45m +go-todo-api-hpa Deployment/go-todo-api 16%/30% 1 10 7 45m +go-todo-api-hpa Deployment/go-todo-api 17%/30% 1 10 7 45m +go-todo-api-hpa Deployment/go-todo-api 14%/30% 1 10 7 46m +go-todo-api-hpa Deployment/go-todo-api 15%/30% 1 10 7 46m +go-todo-api-hpa Deployment/go-todo-api 18%/30% 1 10 7 46m +go-todo-api-hpa Deployment/go-todo-api 14%/30% 1 10 7 46m +go-todo-api-hpa Deployment/go-todo-api 14%/30% 1 10 7 47m +go-todo-api-hpa Deployment/go-todo-api 18%/30% 1 10 7 47m +go-todo-api-hpa Deployment/go-todo-api 14%/30% 1 10 7 47m +go-todo-api-hpa Deployment/go-todo-api 13%/30% 1 10 7 47m +go-todo-api-hpa Deployment/go-todo-api 15%/30% 1 10 7 48m +go-todo-api-hpa Deployment/go-todo-api 15%/30% 1 10 7 48m +go-todo-api-hpa Deployment/go-todo-api 13%/30% 1 10 7 48m +go-todo-api-hpa Deployment/go-todo-api 13%/30% 1 10 5 48m +go-todo-api-hpa Deployment/go-todo-api 15%/30% 1 10 4 49m +go-todo-api-hpa Deployment/go-todo-api 16%/30% 1 10 4 49m +go-todo-api-hpa Deployment/go-todo-api 15%/30% 1 10 4 49m +go-todo-api-hpa Deployment/go-todo-api 18%/30% 1 10 4 49m +go-todo-api-hpa Deployment/go-todo-api 18%/30% 1 10 4 50m +go-todo-api-hpa Deployment/go-todo-api 18%/30% 1 10 3 50m +go-todo-api-hpa Deployment/go-todo-api 20%/30% 1 10 3 50m +go-todo-api-hpa Deployment/go-todo-api 24%/30% 1 10 3 50m +go-todo-api-hpa Deployment/go-todo-api 24%/30% 1 10 3 51m +go-todo-api-hpa Deployment/go-todo-api 22%/30% 1 10 3 51m +go-todo-api-hpa Deployment/go-todo-api 23%/30% 1 10 3 52m +go-todo-api-hpa Deployment/go-todo-api 22%/30% 1 10 3 52m +go-todo-api-hpa Deployment/go-todo-api 23%/30% 1 10 3 52m +go-todo-api-hpa Deployment/go-todo-api 20%/30% 1 10 3 52m +go-todo-api-hpa Deployment/go-todo-api 8%/30% 1 10 3 53m +go-todo-api-hpa Deployment/go-todo-api 0%/30% 1 10 3 53m +``` diff --git a/controllers/controllers.go b/controllers/controllers.go new file mode 100644 index 0000000..25317c5 --- /dev/null +++ b/controllers/controllers.go @@ -0,0 +1,134 @@ +package controllers + +import ( + "fmt" + "github.com/gin-gonic/gin" + guuid "github.com/google/uuid" + "harshsinghvi/golang-postgres-kubernetes/database" + "harshsinghvi/golang-postgres-kubernetes/models" + "harshsinghvi/golang-postgres-kubernetes/utils" + "log" + "net/http" + "time" +) + +func GetAllTodos(c *gin.Context) { + var pag models.Pagination + var err error + + var todos []models.Todo + var searchString = c.Query("search") + var pageString = c.Query("page") + pag.ParseString(pageString) + + querry := database.Connection.Model(&todos).Order("created_at DESC") + + if searchString != "" { + querry = querry.Where(fmt.Sprintf("text like '%%%s%%'", searchString)) + } + + if pag.TotalRecords, err = querry.Count(); err != nil { + utils.InternalServerError(c, "Error while getting all todos, Reason:", err) + return + } + + if pag.CurrentPage != -1 { + querry = querry.Limit(10).Offset(10 * (pag.CurrentPage)) + } + + if err := querry.Select(); err != nil { + utils.InternalServerError(c, "Error while getting all todos, Reason:", err) + return + } + + c.JSON(http.StatusOK, gin.H{ + "status": http.StatusOK, + "message": "All Todos", + "data": todos, + "pagination": pag.Validate(), + }) +} + +func GetSingleTodo(c *gin.Context) { + todoId := c.Param("id") + todo := &models.Todo{ID: todoId} + if err := database.Connection.Select(todo); err != nil { + utils.InternalServerError(c, "Error while getting a single todo, Reason:", err) + return + } + c.JSON(http.StatusOK, gin.H{ + "status": http.StatusOK, + "message": "Single Todo", + "data": todo, + }) +} + +func CreateTodo(c *gin.Context) { + var todo models.Todo + c.BindJSON(&todo) + + text := todo.Text + id := guuid.New().String() + + insertError := database.Connection.Insert(&models.Todo{ + ID: id, + Text: text, + Completed: false, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + }) + + if insertError != nil { + utils.InternalServerError(c, "Error while inserting new todo into db, Reason:", insertError) + return + } + + c.JSON(http.StatusCreated, gin.H{ + "status": http.StatusCreated, + "message": "Todo created Successfully", + }) +} + +func EditTodo(c *gin.Context) { + todoId := c.Param("id") + var todo models.Todo + c.BindJSON(&todo) + + querry := database.Connection.Model(&models.Todo{}).Set("completed = ?", todo.Completed).Set("updated_at = ?", time.Now()) + if todo.Text != "" { + querry = querry.Set("text = ?", todo.Text) + } + + res, err := querry.Where("id = ?", todoId).Update() + + if err != nil { + utils.InternalServerError(c, "Error while editing todo, Reason:", err) + } + + if res.RowsAffected() == 0 { + log.Printf("Error while update todo, Reason: \n") + c.JSON(http.StatusNotFound, gin.H{ + "status": http.StatusNotFound, + "message": "Todo not found", + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "status": 200, + "message": "Todo Edited Successfully", + }) +} + +func DeleteTodo(c *gin.Context) { + todoId := c.Param("id") + todo := &models.Todo{ID: todoId} + if err := database.Connection.Delete(todo); err != nil { + utils.InternalServerError(c, "Error while deleting a single todo, Reason:", err) + return + } + c.JSON(http.StatusOK, gin.H{ + "status": http.StatusOK, + "message": "Todo deleted successfully", + }) +} diff --git a/controllers_old/controllers_old.go b/controllers_old/controllers_old.go new file mode 100644 index 0000000..fd06cf8 --- /dev/null +++ b/controllers_old/controllers_old.go @@ -0,0 +1,95 @@ +package controllers_old + +import ( + "github.com/gin-gonic/gin" + "harshsinghvi/golang-postgres-kubernetes/models" + "net/http" +) + +var TODOS = []models.Todo{ + {ID: "1", Text: "Task 1", Completed: false}, + {ID: "2", Text: "Task 2", Completed: false}, + {ID: "3", Text: "Task 3", Completed: false}, +} + +func GetTodos(c *gin.Context) { + id := c.Query("id") + // completed := c.Query("completed") == "true" // TODO: implement this filter + + if id == "" { + c.IndentedJSON(http.StatusOK, TODOS) + return + } + + for _, a := range TODOS { + if a.ID == id { + c.IndentedJSON(http.StatusOK, a) + return + } + } + c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "not found"}) +} + +func PostTodos(c *gin.Context) { + var newTodo models.Todo + + // Call BindJSON to bind the received JSON to + if err := c.BindJSON(&newTodo); err != nil { + return + } + + if newTodo.Text == "" { + c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "no text"}) + return + } + + for _, a := range TODOS { + if a.ID == newTodo.ID { + c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "Duplicate ID"}) + return + } + } + // Add the new album to the slice. + TODOS = append(TODOS, newTodo) + c.IndentedJSON(http.StatusCreated, newTodo) +} + +func UpdateTodos(c *gin.Context) { + id := c.Param("id") + var updateTodo models.Todo + + // Call BindJSON to bind the received JSON to + if err := c.BindJSON(&updateTodo); err != nil { + return + } + + if updateTodo.ID == "" { + c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "no ID"}) + return + } + + for index, a := range TODOS { + if a.ID == id { + if updateTodo.Text != "" { + TODOS[index].Text = updateTodo.Text + } + TODOS[index].Completed = updateTodo.Completed + c.IndentedJSON(http.StatusOK, TODOS[index]) + return + } + } + c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "invalid ID"}) +} + +func DeleteTodos(c *gin.Context) { + id := c.Param("id") + + for index, a := range TODOS { + if a.ID == id { + TODOS = append(TODOS[:index], TODOS[index+1:]...) + c.IndentedJSON(http.StatusOK, TODOS) + return + } + } + c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "no ID"}) +} diff --git a/database/database.go b/database/database.go new file mode 100644 index 0000000..e8c9d44 --- /dev/null +++ b/database/database.go @@ -0,0 +1,82 @@ +package database + +import ( + "github.com/go-pg/pg/v9" + orm "github.com/go-pg/pg/v9/orm" + "harshsinghvi/golang-postgres-kubernetes/models" + "harshsinghvi/golang-postgres-kubernetes/utils" + "log" +) + +var Connection *pg.DB + +func IsDtabaseReady() bool { + ctx := Connection.Context() + var version string + _, err := Connection.QueryOneContext(ctx, pg.Scan(&version), "SELECT version()") + if err != nil { + log.Printf("Failed to connect to database") + return false + } + return true +} + +func GetDatabase() **pg.DB { + return &Connection +} + +func Connect() *pg.DB { + DB_HOST := utils.GetEnv("DB_HOST", "localhost") + DB_PORT := utils.GetEnv("DB_PORT", "5432") + DB_USER := utils.GetEnv("DB_USER", "postgres") + DB_PASSWORD := utils.GetEnv("DB_PASSWORD", "postgres") + DB_NAME := utils.GetEnv("DB_NAME", "postgres") + + opts := &pg.Options{ + User: DB_USER, + Password: DB_PASSWORD, + Addr: DB_HOST + ":" + DB_PORT, + Database: DB_NAME, + } + + Connection = pg.Connect(opts) + + if Connection == nil { + log.Printf("Failed to connect to database") + return nil + } + + ctx := Connection.Context() + var version string + _, err := Connection.QueryOneContext(ctx, pg.Scan(&version), "SELECT version()") + if err != nil { + log.Printf("Failed to connect to database") + return nil + } + + log.Printf("Connected to db") + + return Connection +} + +// Create User Table +func CreateTodoTable() error { + opts := &orm.CreateTableOptions{ + IfNotExists: true, + } + + createError := Connection.CreateTable(&models.Todo{}, opts) + if createError != nil { + log.Printf("Error while creating todo table, Reason: %v\n", createError) + return createError + } + + _, err := Connection.Exec(`CREATE UNIQUE INDEX IF NOT EXISTS index_todo ON todos(id, completed, created_at, updated_at);`) + + if err != nil { + log.Println(err.Error()) + } + + log.Printf("Todo table and indexes created") + return nil +} diff --git a/db.sql b/db.sql index af3dd9f..a30d86a 100644 --- a/db.sql +++ b/db.sql @@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS todos ( complete BOOLEAN NOT NULL ); -SELECT id,text, complete FROM todos +SELECT id,text, complete FROM todos; UPDATE todos SET text='nae56', WHERE id=1; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2c1fbf7 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,41 @@ +version: '3' +services: + app: + container_name: go-todo-api + build: . + ports: + - 8080:8080 + restart: on-failure + depends_on: + - postgres + networks: + - fullstack + environment: + - DB_HOST=postgres + - DB_DRIVER=${DB_DRIVER} + - DB_USER=${DB_USER} + - DB_PASSWORD=${DB_PASSWORD} + - DB_NAME=${DB_NAME} + - DB_PORT=${DB_PORT} + - POSTGRES_URL=${POSTGRES_URL} + # - PORT=${PORT} + + postgres: + image: postgres:latest + container_name: postgres + hostname: postgres + environment: + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PASSWORD} + - POSTGRES_DB=${DB_NAME} + ports: + - 5432:5432 + volumes: + - ./docker-compose/postgres:/var/lib/postgresql/data + networks: + - fullstack + +# Networks to be created to facilitate communication between containers +networks: + fullstack: + driver: bridge diff --git a/docker-compose/postgres/.gitkeep b/docker-compose/postgres/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/go.mod b/go.mod index e48e739..f90f53e 100644 --- a/go.mod +++ b/go.mod @@ -2,31 +2,53 @@ module harshsinghvi/golang-postgres-kubernetes go 1.21.1 -require github.com/gin-gonic/gin v1.9.1 +require ( + github.com/gin-gonic/gin v1.9.1 + github.com/go-pg/pg/v10 v10.11.2 + github.com/go-pg/pg/v9 v9.2.1 + github.com/google/uuid v1.4.0 + github.com/joho/godotenv v1.5.1 + github.com/lib/pq v1.10.9 +) require ( - github.com/bytedance/sonic v1.9.1 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/bytedance/sonic v1.10.2 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect + github.com/codemodus/kace v0.5.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-pg/zerochecker v0.2.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/go-playground/validator/v10 v10.16.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/leodido/go-urn v1.2.4 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/segmentio/asm v1.1.3 // indirect + github.com/segmentio/encoding v0.3.6 // indirect + github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect + github.com/vmihailenco/bufpool v0.1.11 // indirect + github.com/vmihailenco/msgpack/v4 v4.3.13 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser v0.1.2 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + golang.org/x/arch v0.6.0 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + mellium.im/sasl v0.3.1 // indirect ) diff --git a/go.sum b/go.sum index bfe325c..54fed72 100644 --- a/go.sum +++ b/go.sum @@ -1,86 +1,267 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= +github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/codemodus/kace v0.5.1 h1:4OCsBlE2c/rSJo375ggfnucv9eRzge/U5LrrOZd47HA= +github.com/codemodus/kace v0.5.1/go.mod h1:coddaHoX1ku1YFSe4Ip0mL9kQjJvKkzb9CfIdG1YR04= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-pg/pg/v10 v10.11.2/go.mod h1:ExJWndhDNNftBdw1Ow83xqpSf4WMSJK8urmXD5VXS1I= +github.com/go-pg/pg/v9 v9.2.1 h1:4rWNJkj+aPuDFqgieTzNhHBuYaXREh3yaB9NlBerFys= +github.com/go-pg/pg/v9 v9.2.1/go.mod h1:fG8qbL+ei4e/fCZLHK+Z+/7b9B+pliZtbpaucG4/YNQ= +github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU= +github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE= +github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +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/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 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= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= +github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= +github.com/segmentio/encoding v0.1.15/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw= +github.com/segmentio/encoding v0.3.6 h1:E6lVLyDPseWEulBmCmAKPanDd3jiyGDo5gMcugCRwZQ= +github.com/segmentio/encoding v0.3.6/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94= +github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ= +github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/msgpack/v4 v4.3.13 h1:A2wsiTbvp63ilDaWmsk2wjx6xZdxQOvpiNlKBGKKXKI= +github.com/vmihailenco/msgpack/v4 v4.3.13/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= +github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= +golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ= +mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo= +mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/k8s-deployments/database.yml b/k8s-deployments/database.yml new file mode 100644 index 0000000..bdec7a4 --- /dev/null +++ b/k8s-deployments/database.yml @@ -0,0 +1,109 @@ +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: efs-pv +spec: + capacity: + storage: 5Gi + volumeMode: Filesystem + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: efs-sc + csi: + driver: efs.csi.aws.com + volumeHandle: fs-0b9f5c3cf12064d83 + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: efs-claim +spec: + accessModes: + - ReadWriteMany + storageClassName: efs-sc + resources: + requests: + storage: 5Gi + +--- +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: efs-sc +provisioner: efs.csi.aws.com + +--- +apiVersion: apps/v1 +kind: StatefulSet # Type of the kubernetes resource +metadata: + name: postgres # Name of the statefulset + labels: # Labels applied to this statefulset + app: postgres +spec: + serviceName: postgres + selector: + matchLabels: # This statefulset applies to the Pods matching the specified labels + app: postgres + tier: postgres + replicas: 1 + template: # Template for the Pods in this statefulset + metadata: + labels: # Labels to be applied to the Pods in this statefulset + app: postgres + tier: postgres + spec: # The spec for the containers that will be run inside the Pods in this statefulset + containers: + - image: postgres:latest # The container image + name: postgres + imagePullPolicy: "IfNotPresent" + envFrom: # Get the environmental variables from a secret file whose name is "postgres-secret" + - secretRef: + name: postgres-secret + ports: + - containerPort: 5432 # The port that the container exposes + name: postgres + resources: + limits: + cpu: "500m" + requests: + cpu: "200m" + volumeMounts: + - mountPath: /var/lib/postgresql/data + name: postgres-persistent-storage # This name should match the name specified in `volumes.name` + volumes: # A PersistentVolume is mounted as a volume to the Pod + - name: postgres-persistent-storage + persistentVolumeClaim: + claimName: efs-claim + +--- +apiVersion: v1 # API version +kind: Service # Type of kubernetes resource +metadata: + name: postgres # Name of the resource + labels: # Labels that will be applied to the resource + app: postgres +spec: + ports: + - name: db + port: 5432 + targetPort: 5432 # Exposing default port of 5432. + selector: # Selects any Pod with labels `app=fullstack-postgres,tier=postgres` + app: postgres + tier: postgres + +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: postgres-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: StatefulSet + name: postgres + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 80 \ No newline at end of file diff --git a/k8s-deployments/deployment.yml b/k8s-deployments/deployment.yml new file mode 100644 index 0000000..64817b4 --- /dev/null +++ b/k8s-deployments/deployment.yml @@ -0,0 +1,110 @@ +--- +apiVersion: apps/v1 +kind: Deployment # Type of Kubernetes resource +metadata: + name: go-todo-api # Name of the Kubernetes resource +spec: + replicas: 1 # Number of pods to run at any given time + selector: + matchLabels: + app: go-todo-api # This deployment applies to any Pods matching the specified label + template: # This deployment will create a set of pods using the configurations in this template + metadata: + labels: # The labels that will be applied to all of the pods in this deployment + app: go-todo-api + spec: # Spec for the container which will run in the Pod + imagePullSecrets: + - name: dockerconfigjson-github-com + initContainers: + - name: check-db-ready + image: postgres:latest + command: ['sh', '-c', + 'until pg_isready -h $DB_HOST -p $DB_PORT; + do echo waiting for database; sleep 2; done;'] + envFrom: + - secretRef: + name: app-secret + containers: + - name: go-todo-api + image: ghcr.io/harshsinghvi/golang-postgres-kubernetes:latest + imagePullPolicy: IfNotPresent # Always + envFrom: + - secretRef: + name: app-secret + ports: + - containerPort: 8080 # Should match the port number that the Go application listens on + resources: + limits: + cpu: "500m" + requests: + cpu: "200m" + livenessProbe: # To check the health of the Pod + httpGet: + path: /health + port: 8080 + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 15 + timeoutSeconds: 5 + readinessProbe: # To check if the Pod is ready to serve traffic or not + httpGet: + path: /readiness + port: 8080 + scheme: HTTP + initialDelaySeconds: 5 + timeoutSeconds: 1 + +--- +apiVersion: v1 +kind: Service +metadata: + name: golang-todo-api-service +spec: + ports: + - port: 8080 + targetPort: 8080 + # protocol: TCP + type: LoadBalancer + selector: + app: go-todo-api + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: golang-todo-api-ingress + annotations: + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/target-type: ip + + # alb.ingress.kubernetes.io/group.name: kube-alb-group #Use this to share ALB among multiple ingresses. #CostEffective + # alb.ingress.kubernetes.io/load-balancer-name: kube-alb # give ALB a meaningfull name otherwise a random name is assigned by AWS. + # alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:eu-west-1:XXXX:certificate/YYYY" # Get it by $ aws acm list-certificates + # alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' + # alb.ingress.kubernetes.io/ssl-redirect: "443" +spec: + ingressClassName: alb + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: golang-todo-api-service + port: + number: 8080 + +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: go-todo-api-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: go-todo-api + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 80 diff --git a/k8s-deployments/secrets.yml b/k8s-deployments/secrets.yml new file mode 100644 index 0000000..066bda0 --- /dev/null +++ b/k8s-deployments/secrets.yml @@ -0,0 +1,43 @@ +# DEPLOYMENT SECRET +--- +kind: Secret +type: kubernetes.io/dockerconfigjson +apiVersion: v1 +metadata: + name: dockerconfigjson-github-com + labels: + app: app-name +data: + .dockerconfigjson: eyAiYXV0aHMiOiB7ICJnaGNyLmlvIjogeyAiYXV0aCI6ImFHRnljMmh6YVc1bmFIWnBPbWRvY0Y5UU1FOHdZbXRXU0VwMVYzbHJObWRPVnpaQ1lWYzRRMFY2Y25sTFVXZ3hkRkp1UlVrPSIgfSB9IH0= + +# App Secrets +--- +apiVersion: v1 +kind: Secret +metadata: + name: app-secret +type: Opaque +stringData: + # Postgres Config + DB_HOST: postgres + DB_DRIVER: postgres + DB_USER: postgres + DB_PASSWORD: postgres + DB_NAME: postgres + DB_PORT: "5432" + #Go app PORT configuration + PORT: "8080" + POSTGRES_URL: postgres://postgres:postgres@postgres:5432/postgres?sslmode=disable # Currently hardcoding DB parameters, could use nested variables as future improvement + # POSTGRES_URL: postgres://:@:5432/simple-service?sslmode=disable # Currently hardcoding DB parameters, could use nested variables as future improvement + +# Database secrets +--- +apiVersion: v1 +kind: Secret +metadata: + name: postgres-secret +type: Opaque +stringData: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres diff --git a/k8s-eks-system/.dockerconfigjson b/k8s-eks-system/.dockerconfigjson new file mode 100644 index 0000000..1ab5294 --- /dev/null +++ b/k8s-eks-system/.dockerconfigjson @@ -0,0 +1,9 @@ +{ + "auths": + { + "ghcr.io": + { + "auth":"aGFyc2hzaW5naHZpOmdocF9QME8wYmtWSEp1V3lrNmdOVzZCYVc4Q0V6cnlLUWgxdFJuRUk=" + } + } +} \ No newline at end of file diff --git a/k8s-eks-system/iam_policy.json b/k8s-eks-system/iam_policy.json new file mode 100644 index 0000000..7944f2a --- /dev/null +++ b/k8s-eks-system/iam_policy.json @@ -0,0 +1,241 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "iam:CreateServiceLinkedRole" + ], + "Resource": "*", + "Condition": { + "StringEquals": { + "iam:AWSServiceName": "elasticloadbalancing.amazonaws.com" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "ec2:DescribeAccountAttributes", + "ec2:DescribeAddresses", + "ec2:DescribeAvailabilityZones", + "ec2:DescribeInternetGateways", + "ec2:DescribeVpcs", + "ec2:DescribeVpcPeeringConnections", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + "ec2:DescribeInstances", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeTags", + "ec2:GetCoipPoolUsage", + "ec2:DescribeCoipPools", + "elasticloadbalancing:DescribeLoadBalancers", + "elasticloadbalancing:DescribeLoadBalancerAttributes", + "elasticloadbalancing:DescribeListeners", + "elasticloadbalancing:DescribeListenerCertificates", + "elasticloadbalancing:DescribeSSLPolicies", + "elasticloadbalancing:DescribeRules", + "elasticloadbalancing:DescribeTargetGroups", + "elasticloadbalancing:DescribeTargetGroupAttributes", + "elasticloadbalancing:DescribeTargetHealth", + "elasticloadbalancing:DescribeTags" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "cognito-idp:DescribeUserPoolClient", + "acm:ListCertificates", + "acm:DescribeCertificate", + "iam:ListServerCertificates", + "iam:GetServerCertificate", + "waf-regional:GetWebACL", + "waf-regional:GetWebACLForResource", + "waf-regional:AssociateWebACL", + "waf-regional:DisassociateWebACL", + "wafv2:GetWebACL", + "wafv2:GetWebACLForResource", + "wafv2:AssociateWebACL", + "wafv2:DisassociateWebACL", + "shield:GetSubscriptionState", + "shield:DescribeProtection", + "shield:CreateProtection", + "shield:DeleteProtection" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:RevokeSecurityGroupIngress" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateSecurityGroup" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateTags" + ], + "Resource": "arn:aws:ec2:*:*:security-group/*", + "Condition": { + "StringEquals": { + "ec2:CreateAction": "CreateSecurityGroup" + }, + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateTags", + "ec2:DeleteTags" + ], + "Resource": "arn:aws:ec2:*:*:security-group/*", + "Condition": { + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "true", + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:RevokeSecurityGroupIngress", + "ec2:DeleteSecurityGroup" + ], + "Resource": "*", + "Condition": { + "Null": { + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:CreateLoadBalancer", + "elasticloadbalancing:CreateTargetGroup" + ], + "Resource": "*", + "Condition": { + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:CreateListener", + "elasticloadbalancing:DeleteListener", + "elasticloadbalancing:CreateRule", + "elasticloadbalancing:DeleteRule" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags" + ], + "Resource": [ + "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*" + ], + "Condition": { + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "true", + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags" + ], + "Resource": [ + "arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*", + "arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*", + "arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*", + "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*" + ] + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:ModifyLoadBalancerAttributes", + "elasticloadbalancing:SetIpAddressType", + "elasticloadbalancing:SetSecurityGroups", + "elasticloadbalancing:SetSubnets", + "elasticloadbalancing:DeleteLoadBalancer", + "elasticloadbalancing:ModifyTargetGroup", + "elasticloadbalancing:ModifyTargetGroupAttributes", + "elasticloadbalancing:DeleteTargetGroup" + ], + "Resource": "*", + "Condition": { + "Null": { + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:AddTags" + ], + "Resource": [ + "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*" + ], + "Condition": { + "StringEquals": { + "elasticloadbalancing:CreateAction": [ + "CreateTargetGroup", + "CreateLoadBalancer" + ] + }, + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:RegisterTargets", + "elasticloadbalancing:DeregisterTargets" + ], + "Resource": "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*" + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:SetWebAcl", + "elasticloadbalancing:ModifyListener", + "elasticloadbalancing:AddListenerCertificates", + "elasticloadbalancing:RemoveListenerCertificates", + "elasticloadbalancing:ModifyRule" + ], + "Resource": "*" + } + ] +} diff --git a/k8s-eks-system/loadgenerator.yml b/k8s-eks-system/loadgenerator.yml new file mode 100644 index 0000000..a5a20f1 --- /dev/null +++ b/k8s-eks-system/loadgenerator.yml @@ -0,0 +1,25 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: loadgenerator + labels: + app: loadgenerator +spec: + replicas: 50 + selector: + matchLabels: + app: loadgenerator + template: + metadata: + name: loadgenerator + labels: + app: loadgenerator + spec: + containers: + - name: loadgenerator + imagePullPolicy: IfNotPresent # Always + image: busybox + command: + - /bin/sh + - -c + - "while true; do wget -q -O- http://golang-todo-api-service:8080/api/v2/todo; done" \ No newline at end of file diff --git a/k8s-eks-system/matrics-server.yaml b/k8s-eks-system/matrics-server.yaml new file mode 100644 index 0000000..d84b7e2 --- /dev/null +++ b/k8s-eks-system/matrics-server.yaml @@ -0,0 +1,197 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + k8s-app: metrics-server + name: metrics-server + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + k8s-app: metrics-server + rbac.authorization.k8s.io/aggregate-to-admin: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-view: "true" + name: system:aggregated-metrics-reader +rules: +- apiGroups: + - metrics.k8s.io + resources: + - pods + - nodes + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + k8s-app: metrics-server + name: system:metrics-server +rules: +- apiGroups: + - "" + resources: + - nodes/metrics + verbs: + - get +- apiGroups: + - "" + resources: + - pods + - nodes + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + k8s-app: metrics-server + name: metrics-server-auth-reader + namespace: kube-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: extension-apiserver-authentication-reader +subjects: +- kind: ServiceAccount + name: metrics-server + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + k8s-app: metrics-server + name: metrics-server:system:auth-delegator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: metrics-server + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + k8s-app: metrics-server + name: system:metrics-server +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:metrics-server +subjects: +- kind: ServiceAccount + name: metrics-server + namespace: kube-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + k8s-app: metrics-server + name: metrics-server + namespace: kube-system +spec: + ports: + - name: https + port: 443 + protocol: TCP + targetPort: https + selector: + k8s-app: metrics-server +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + k8s-app: metrics-server + name: metrics-server + namespace: kube-system +spec: + selector: + matchLabels: + k8s-app: metrics-server + strategy: + rollingUpdate: + maxUnavailable: 0 + template: + metadata: + labels: + k8s-app: metrics-server + spec: + containers: + - args: + - --cert-dir=/tmp + - --secure-port=4443 + - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname + - --kubelet-use-node-status-port + - --metric-resolution=15s + - --kubelet-insecure-tls + image: registry.k8s.io/metrics-server/metrics-server:v0.6.4 + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /livez + port: https + scheme: HTTPS + periodSeconds: 10 + name: metrics-server + ports: + - containerPort: 4443 + name: https + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /readyz + port: https + scheme: HTTPS + initialDelaySeconds: 20 + periodSeconds: 10 + resources: + requests: + cpu: 100m + memory: 200Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + volumeMounts: + - mountPath: /tmp + name: tmp-dir + nodeSelector: + kubernetes.io/os: linux + priorityClassName: system-cluster-critical + serviceAccountName: metrics-server + volumes: + - emptyDir: {} + name: tmp-dir +--- +apiVersion: apiregistration.k8s.io/v1 +kind: APIService +metadata: + labels: + k8s-app: metrics-server + name: v1beta1.metrics.k8s.io +spec: + group: metrics.k8s.io + groupPriorityMinimum: 100 + insecureSkipTLSVerify: true + service: + name: metrics-server + namespace: kube-system + version: v1beta1 + versionPriority: 100 diff --git a/main.go b/main.go index 49c1099..4299df8 100644 --- a/main.go +++ b/main.go @@ -1,115 +1,57 @@ package main import ( - // "fmt" - "net/http" - "github.com/gin-gonic/gin" + "log" + "net/http" + "github.com/gin-gonic/gin" + "github.com/joho/godotenv" + "harshsinghvi/golang-postgres-kubernetes/database" + "harshsinghvi/golang-postgres-kubernetes/controllers" + "harshsinghvi/golang-postgres-kubernetes/controllers_old" ) -type Todo struct { - ID string `json:"id"` - Text string `json:"text"` - Completed bool `json:"completed"` - // Date float64 `json:"date"` // TODO: implement latter +func healthHandler(c *gin.Context) { + c.IndentedJSON(http.StatusOK, gin.H{"message": "OK"}) } -var TODOS = []Todo{ - {ID: "1", Text: "Task 1", Completed: false}, - {ID: "2", Text: "Task 2", Completed: false}, - {ID: "3", Text: "Task 3", Completed: false}, -} - -func getTodos(c *gin.Context) { - id := c.Query("id") - // completed := c.Query("completed") == "true" // TODO: implement this filter - - if id == "" { - c.IndentedJSON(http.StatusOK, TODOS) +func readinessHandler(c *gin.Context) { + if !database.IsDtabaseReady() { + c.IndentedJSON(http.StatusServiceUnavailable, gin.H{"message": "server not ready"}) return } - - for _, a := range TODOS { - if a.ID == id { - c.IndentedJSON(http.StatusOK, a) - return - } - } - - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "not found"}) - + c.IndentedJSON(http.StatusOK, gin.H{"message": "OK"}) } -func postTodos(c *gin.Context) { - var newTodo Todo - - // Call BindJSON to bind the received JSON to - if err := c.BindJSON(&newTodo); err != nil { - return - } - - if newTodo.Text == "" { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "no text"}) - return - } - - for _, a := range TODOS { - if(a.ID == newTodo.ID){ - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "Duplicate ID"}) - return - } +func main() { + err := godotenv.Load() + if err != nil { + log.Printf("Error loading .env file") } - // Add the new album to the slice. - TODOS = append(TODOS, newTodo) - c.IndentedJSON(http.StatusCreated, newTodo) -} - -func updateTodos(c *gin.Context){ - id := c.Param("id") - var updateTodo Todo - - // Call BindJSON to bind the received JSON to - if err := c.BindJSON(&updateTodo); err != nil { - return - } - if updateTodo.ID == "" { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "no ID"}) - return - } - - for index, a := range TODOS { - if a.ID == id { - if updateTodo.Text != "" { - TODOS[index].Text = updateTodo.Text - } - TODOS[index].Completed = updateTodo.Completed - c.IndentedJSON(http.StatusOK, TODOS[index]) - return - } - } - - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "invalid ID"}) -} - -func deleteTodos(c *gin.Context){ - id := c.Param("id") + database.Connect() + database.CreateTodoTable() + + router := gin.Default() + api := router.Group("/api") + { + v1 := api.Group("/v1") + { + v1.GET("/todo", controllers_old.GetTodos) + v1.POST("/todo", controllers_old.PostTodos) + v1.PUT("/todo/:id", controllers_old.UpdateTodos) + v1.DELETE("/todo/:id", controllers_old.DeleteTodos) + } - for index, a := range TODOS { - if a.ID == id{ - TODOS = append(TODOS[:index], TODOS[index+1:]...) - c.IndentedJSON(http.StatusOK, TODOS) - return + v2 := api.Group("/v2") + { + v2.GET("/todo", controllers.GetAllTodos) + v2.GET("/todo/:id", controllers.GetSingleTodo) + v2.POST("/todo", controllers.CreateTodo) + v2.PUT("/todo/:id", controllers.EditTodo) + v2.DELETE("/todo/:id", controllers.DeleteTodo) } } - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "no ID"}) -} - -func main() { - router := gin.Default() - router.GET("/todos", getTodos) - router.POST("/todos", postTodos) - router.PUT("/todos/:id", updateTodos) - router.DELETE("/todos/:id", deleteTodos) - - router.Run("localhost:8080") + router.GET("/health", healthHandler) + router.GET("/readiness", readinessHandler) + router.Run(":8080") } diff --git a/models/pagination.go b/models/pagination.go new file mode 100644 index 0000000..b2c151a --- /dev/null +++ b/models/pagination.go @@ -0,0 +1,58 @@ +package models + +import "strconv" + +type Pagination struct { + TotalRecords int `json:"total_records"` + CurrentPage int `json:"current_page"` + TotalPages int `json:"total_pages"` + NextPage int `json:"next_page"` + PrevPage int `json:"prev_page"` +} + +func (pag *Pagination) Validate() *Pagination { + pag.Set(pag.CurrentPage, pag.TotalRecords) + return pag +} + +func (pag *Pagination) Set(current int, totalRec int) *Pagination { + pag.TotalRecords = totalRec + + if pag.TotalPages = pag.TotalRecords / 10; pag.TotalRecords%10 == 0 { + pag.TotalPages = pag.TotalPages - 1 + } + + if current <= 0 { + pag.PrevPage = 0 + } else { + pag.PrevPage = current - 1 + } + + if current >= pag.TotalPages { + pag.NextPage = pag.TotalPages + } else { + pag.NextPage = current + 1 + } + + pag.CurrentPage = current + return pag +} + +func (pag *Pagination) ParseString(pageString string) { + pag.CurrentPage = 0 + + if pageString == "" { + return + } + + pag.CurrentPage, _ = strconv.Atoi(pageString) + + if pag.CurrentPage == -1 { + return + } + + if pag.CurrentPage < 0 { + pag.CurrentPage = 0 + return + } +} diff --git a/models/todo.go b/models/todo.go new file mode 100644 index 0000000..f70b307 --- /dev/null +++ b/models/todo.go @@ -0,0 +1,11 @@ +package models + +import "time" + +type Todo struct { + ID string `json:"id"` + Text string `json:"text"` + Completed bool `json:"completed"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/utils/error.go b/utils/error.go new file mode 100644 index 0000000..37b9e02 --- /dev/null +++ b/utils/error.go @@ -0,0 +1,15 @@ +package utils + +import ( + "github.com/gin-gonic/gin" + "log" + "net/http" +) + +func InternalServerError(c *gin.Context, msg string, err error) { + log.Printf("%s %v\n", msg, err) + c.JSON(http.StatusInternalServerError, gin.H{ + "status": http.StatusInternalServerError, + "message": "Something went wrong", + }) +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..7505e09 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,13 @@ +package utils + +import ( + "os" +) + +func GetEnv(varNameString string, defaultValue string) string { + var varValue string + if varValue = os.Getenv(varNameString); varNameString == "" { + varValue = defaultValue + } + return varValue +}