1
0
mirror of https://github.com/raseels-repos/golang-saas-starter-kit.git synced 2025-06-15 00:15:15 +02:00

checkpoint

This commit is contained in:
Lee Brown
2019-07-07 12:52:55 -08:00
parent 24dd0dff42
commit aef8054cbd
14 changed files with 2521 additions and 626 deletions

View File

@ -0,0 +1,222 @@
image: docker:stable
services:
- docker:dind
variables:
AWS_ECS_CLUSTER: example-project
AWS_S3_STATIC_BASE_URI: example-project-stage/public
CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
DOCKER_HOST: tcp://docker:2375
DOCKER_DRIVER: overlay2
before_script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
stages:
- build:base
- build:stage
- migrate:stage
- deploy:stage
- build:prod
- migrate:prod
- deploy:prod
cache:
key: ${CI_COMMIT_REF_SLUG}
# Everything should get this, whether through subtemplates or explicitly
# embedded in a job.
.job_tmpl: &job_tmpl
only:
- master
.build_tmpl: &build_tmpl
<<: *job_tmpl
script:
- 'git clone http://gitlab+deploy-token-50199:z2WzSQKY9Crzvw98yzTy@gitlab.com/gitw/akverse.git'
- 'CI=1 PUSH=${PUSH} PUSH_AWS_REGISTRY=${PUSH_AWS_REGISTRY} ./akverse/scripts/build.sh $SERVICE $TARGET_ENV -'
.deploy_tmpl: &deploy_tmpl
<<: *job_tmpl
script:
- 'git clone http://gitlab+deploy-token-50199:z2WzSQKY9Crzvw98yzTy@gitlab.com/gitw/akverse.git'
- 'LB=${ENABLE_LB} SD=${ENABLE_SD} VPC=${ENABLE_VPC} S3_BUCKET=${S3_BUCKET} S3_KEY=${S3_KEY} STATIC_S3_URI=${STATIC_S3_URI} ./akverse/scripts/deploy.sh $SERVICE $TARGET_ENV - ${ECS_CLUSTER}'
.build_base_tmpl: &build_base_tmpl
<<: *build_tmpl
stage: build:base
tags:
- stage
only:
- master
- stage
- /^stage-.*$/
- prod
- /^prod-.*$/
.build_stage_tmpl: &build_stage_tmpl
<<: *build_tmpl
stage: build:stage
tags:
- stage
.build_prod_tmpl: &build_prod_tmpl
<<: *build_tmpl
stage: build:prod
tags:
- prod
.deploy_stage_tmpl: &deploy_stage_tmpl
<<: *deploy_tmpl
stage: deploy:stage
tags:
- stage
environment:
name: 'stage/${SERVICE}-stage'
.deploy_prod_tmpl: &deploy_prod_tmpl
<<: *deploy_tmpl
stage: deploy:prod
tags:
- prod
environment:
name: 'production/${SERVICE}'
when: manual
.migrate_stage_tmpl: &migrate_stage_tmpl
<<: *build_tmpl
stage: migrate:stage
tags:
- stage
only:
- master
- stage
- /^stage-.*$/
- prod
- /^prod-.*$/
.migrate_prod_tmpl: &migrate_prod_tmpl
<<: *build_tmpl
stage: migrate:prod
tags:
- prod
when: manual
only:
- master
- prod
- /^prod-.*$/
datadog-agent:build:stage:
<<: *build_stage_tmpl
variables:
TARGET_ENV: 'stage'
SERVICE: 'datadog-agent'
PUSH_AWS_REGISTRY: 1
datadog-agent:build:prod:
<<: *build_prod_tmpl
variables:
TARGET_ENV: 'prod'
SERVICE: 'datadog-agent'
PUSH_AWS_REGISTRY: 1
dependencies:
- 'datadog-agent:build:stage'
db:migrate:stage:
<<: *migrate_stage_tmpl
variables:
TARGET_ENV: 'stage'
SERVICE: 'schema'
db:migrate:prod:
<<: *migrate_prod_tmpl
variables:
TARGET_ENV: 'prod'
SERVICE: 'schema'
dependencies:
- 'db:migrate:stage'
webapi:build:stage:
<<: *build_stage_tmpl
variables:
TARGET_ENV: 'stage'
SERVICE: 'webapi'
only:
- master
- stage
- stage-webapi
- prod
- prod-webapi
webapi:deploy:stage:
<<: *deploy_stage_tmpl
variables:
TARGET_ENV: 'stage'
SERVICE: 'webapi'
ECS_CLUSTER: '${ECS_CLUSTER}'
STATIC_S3_URI: '${AWS_S3_STATIC_BASE_URI}/stage/webapi'
ENABLE_LB: 0
dependencies:
- 'webapi:build:stage'
- 'db:migrate:stage'
- 'datadog-agent:build:stage'
only:
- master
- stage
- stage-webapi
- prod
- prod-webapi
webapi:build:prod:
<<: *build_prod_tmpl
variables:
TARGET_ENV: 'prod'
SERVICE: 'webapi'
dependencies:
- 'webapi:deploy:stage'
only:
- master
- prod
- prod-webapi
webapi:deploy:prod:
<<: *deploy_prod_tmpl
variables:
TARGET_ENV: 'prod'
SERVICE: 'webapi'
ECS_CLUSTER: '${ECS_CLUSTER}'
STATIC_S3_URI: '${AWS_S3_STATIC_BASE_URI}/prod/webapi'
ENABLE_LB: 0
dependencies:
- 'webapi:build:prod'
- 'db:migrate:prod'
- 'datadog-agent:build:prod'
only:
- master
- prod
- prod-webapi
#ddlogscollector:deploy:stage:
# <<: *deploy_stage_tmpl
# variables:
# TARGET_ENV: 'stage'
# ECS_CLUSTER: '${ECS_CLUSTER}'
# SERVICE: 'ddlogscollector'
# S3_BUCKET: 'keenispace-services-stage'
# S3_KEY: 'aws/lambda/ddlogscollector/src/ddlogscollector-stage.zip'
# ENABLE_VPC: 0
# only:
# - master
# - stage
#ddlogscollector:deploy:prod:
# <<: *deploy_prod_tmpl
# variables:
# TARGET_ENV: 'prod'
# ECS_CLUSTER: '${ECS_CLUSTER}'
# SERVICE: 'ddlogscollector'
# S3_BUCKET: 'keenispace-services-prod'
# S3_KEY: 'aws/lambda/ddlogscollector/src/ddlogscollector-prod.zip'
# ENABLE_VPC: 0
# only:
# - master
# - prod
# #dependencies:
# # - 'ddlogscollector:deploy:stage'

View File

@ -12,7 +12,8 @@ RUN GO111MODULE=off go get gopkg.in/go-playground/validator.v9 && \
GO111MODULE=off go get github.com/lib/pq/oid && \
GO111MODULE=off go get github.com/lib/pq/scram && \
GO111MODULE=off go get github.com/tinylib/msgp/msgp && \
GO111MODULE=off go get gopkg.in/DataDog/dd-trace-go.v1/ddtrace
GO111MODULE=off go get gopkg.in/DataDog/dd-trace-go.v1/ddtrace && \
GO111MODULE=off go get github.com/xwb1989/sqlparser
# Install swag with go modules enabled.
RUN GO111MODULE=on go get -u github.com/swaggo/swag/cmd/swag
@ -52,6 +53,12 @@ COPY --from=builder /gosrv /
#COPY --from=builder /static /static
COPY --from=builder /templates /templates
ARG service
ENV SERVICE_NAME $service
ARG env="dev"
ENV ENV $env
ARG gogc="20"
ENV GOGC $gogc

View File

@ -1,6 +1,6 @@
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag at
// 2019-06-25 22:24:43.036451 -0800 AKDT m=+251.619672639
// 2019-06-27 04:56:45.692511 -0800 AKDT m=+325.727343639
package docs
@ -62,7 +62,7 @@ var doc = `{
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -77,13 +77,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -136,13 +129,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
@ -191,38 +177,26 @@ var doc = `{
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/user.Token"
},
"headers": {
"Token": {
"type": "string",
"description": "qwerty"
}
}
},
"200": {},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.Error"
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"401": {
"description": "Unauthorized",
"schema": {
"type": "object",
"$ref": "#/definitions/web.Error"
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.Error"
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
@ -343,8 +317,8 @@ var doc = `{
}
],
"responses": {
"200": {
"description": "OK",
"201": {
"description": "Created",
"schema": {
"type": "object",
"$ref": "#/definitions/project.ProjectResponse"
@ -410,7 +384,7 @@ var doc = `{
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -425,13 +399,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -473,7 +440,7 @@ var doc = `{
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -488,13 +455,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -547,13 +507,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
@ -597,7 +550,7 @@ var doc = `{
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -612,13 +565,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -655,8 +601,8 @@ var doc = `{
}
],
"responses": {
"200": {
"description": "OK",
"201": {
"description": "Created",
"schema": {
"type": "object",
"$ref": "#/definitions/signup.SignupResponse"
@ -669,6 +615,83 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
}
},
"/user_accounts": {
"get": {
"security": [
{
"OAuth2Password": []
}
],
"description": "Find returns the existing user accounts in the system.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user_account"
],
"summary": "List user accounts",
"parameters": [
{
"type": "string",
"description": "Filter string, example: account_id = 'c4653bf9-5978-48b7-89c5-95704aebb7e2'",
"name": "where",
"in": "query"
},
{
"type": "string",
"description": "Order columns separated by comma, example: created_at desc",
"name": "order",
"in": "query"
},
{
"type": "integer",
"description": "Limit, example: 10",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"description": "Offset, example: 20",
"name": "offset",
"in": "query"
},
{
"type": "boolean",
"description": "Included Archived, example: false",
"name": "included-archived",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/user_account.UserAccountResponse"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
@ -684,6 +707,293 @@ var doc = `{
}
}
}
},
"post": {
"security": [
{
"OAuth2Password": []
}
],
"description": "Create inserts a new user account into the system.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user_account"
],
"summary": "Create new user account.",
"parameters": [
{
"description": "User Account details",
"name": "data",
"in": "body",
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/user_account.UserAccountCreateRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"type": "object",
"$ref": "#/definitions/user_account.UserAccountResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
},
"delete": {
"security": [
{
"OAuth2Password": []
}
],
"description": "Delete removes the specified user account from the system.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Delete user account by user ID and account ID",
"parameters": [
{
"type": "string",
"description": "UserAccount ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"204": {},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
},
"patch": {
"security": [
{
"OAuth2Password": []
}
],
"description": "Update updates the specified user account in the system.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Update user account by user ID and account ID",
"parameters": [
{
"description": "Update fields",
"name": "data",
"in": "body",
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/user_account.UserAccountUpdateRequest"
}
}
],
"responses": {
"204": {},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
}
},
"/user_accounts/archive": {
"patch": {
"security": [
{
"OAuth2Password": []
}
],
"description": "Archive soft-deletes the specified user account from the system.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Archive user account by user ID and account ID",
"parameters": [
{
"description": "Update fields",
"name": "data",
"in": "body",
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/user_account.UserAccountArchiveRequest"
}
}
],
"responses": {
"204": {},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
}
},
"/user_accounts/{id}": {
"get": {
"security": [
{
"OAuth2Password": []
}
],
"description": "Read returns the specified user account from the system.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user_account"
],
"summary": "Get user account by ID",
"parameters": [
{
"type": "string",
"description": "UserAccount ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/user_account.UserAccountResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
}
},
"/users": {
@ -753,13 +1063,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -799,8 +1102,8 @@ var doc = `{
}
],
"responses": {
"200": {
"description": "OK",
"201": {
"description": "Created",
"schema": {
"type": "object",
"$ref": "#/definitions/user.UserResponse"
@ -820,13 +1123,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -866,7 +1162,7 @@ var doc = `{
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -881,13 +1177,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -929,7 +1218,7 @@ var doc = `{
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -944,13 +1233,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -992,7 +1274,7 @@ var doc = `{
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -1007,13 +1289,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -1052,7 +1327,7 @@ var doc = `{
}
],
"responses": {
"201": {},
"200": {},
"400": {
"description": "Bad Request",
"schema": {
@ -1060,15 +1335,8 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"401": {
"description": "Unauthorized",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
@ -1126,13 +1394,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
@ -1176,7 +1437,7 @@ var doc = `{
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -1191,13 +1452,6 @@ var doc = `{
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -1435,17 +1689,14 @@ var doc = `{
}
}
},
"signup.SignupRequest": {
"type": "object",
"properties": {
"account": {
"signup.SignupAccount": {
"type": "object",
"required": [
"name",
"address1",
"city",
"region",
"country",
"name",
"region",
"zipcode"
],
"properties": {
@ -1483,11 +1734,37 @@ var doc = `{
}
}
},
"signup.SignupRequest": {
"type": "object",
"properties": {
"account": {
"type": "object",
"$ref": "#/definitions/signup.SignupAccount"
},
"user": {
"type": "object",
"$ref": "#/definitions/signup.SignupUser"
}
}
},
"signup.SignupResponse": {
"type": "object",
"properties": {
"account": {
"type": "object",
"$ref": "#/definitions/account.AccountResponse"
},
"user": {
"type": "object",
"$ref": "#/definitions/user.UserResponse"
}
}
},
"signup.SignupUser": {
"type": "object",
"required": [
"name",
"email",
"name",
"password"
],
"properties": {
@ -1508,33 +1785,6 @@ var doc = `{
"example": "SecretString"
}
}
}
}
},
"signup.SignupResponse": {
"type": "object",
"properties": {
"account": {
"type": "string"
},
"user": {
"type": "string"
}
}
},
"user.Token": {
"type": "object",
"properties": {
"access_token": {
"type": "string"
},
"expiry": {
"type": "string"
},
"token_type": {
"type": "string"
}
}
},
"user.UserArchiveRequest": {
"type": "object",
@ -1656,6 +1906,151 @@ var doc = `{
}
}
},
"user_account.UserAccountArchiveRequest": {
"type": "object",
"required": [
"account_id",
"user_id"
],
"properties": {
"account_id": {
"type": "string",
"example": "c4653bf9-5978-48b7-89c5-95704aebb7e2"
},
"user_id": {
"type": "string",
"example": "d69bdef7-173f-4d29-b52c-3edc60baf6a2"
}
}
},
"user_account.UserAccountCreateRequest": {
"type": "object",
"required": [
"account_id",
"roles",
"user_id"
],
"properties": {
"account_id": {
"type": "string",
"example": "c4653bf9-5978-48b7-89c5-95704aebb7e2"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": [
"admin",
"user"
]
},
"example": [
"admin"
]
},
"status": {
"type": "string",
"enum": [
"active",
"invited",
"disabled"
],
"example": "active"
},
"user_id": {
"type": "string",
"example": "d69bdef7-173f-4d29-b52c-3edc60baf6a2"
}
}
},
"user_account.UserAccountResponse": {
"type": "object",
"required": [
"roles"
],
"properties": {
"account_id": {
"type": "string",
"example": "c4653bf9-5978-48b7-89c5-95704aebb7e2"
},
"archived_at": {
"type": "object",
"$ref": "#/definitions/web.TimeResponse"
},
"created_at": {
"type": "object",
"$ref": "#/definitions/web.TimeResponse"
},
"id": {
"type": "string",
"example": "d69bdef7-173f-4d29-b52c-3edc60baf6a2"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": [
"admin",
"user"
]
},
"example": [
"admin"
]
},
"status": {
"type": "object",
"$ref": "#/definitions/web.EnumResponse"
},
"updated_at": {
"type": "object",
"$ref": "#/definitions/web.TimeResponse"
},
"user_id": {
"type": "string",
"example": "d69bdef7-173f-4d29-b52c-3edc60baf6a2"
}
}
},
"user_account.UserAccountUpdateRequest": {
"type": "object",
"required": [
"account_id",
"user_id"
],
"properties": {
"account_id": {
"type": "string",
"example": "c4653bf9-5978-48b7-89c5-95704aebb7e2"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": [
"admin",
"user"
]
},
"example": [
"user"
]
},
"status": {
"type": "string",
"enum": [
"active",
"invited",
"disabled"
],
"example": "disabled"
},
"user_id": {
"type": "string",
"example": "d69bdef7-173f-4d29-b52c-3edc60baf6a2"
}
}
},
"web.EnumOption": {
"type": "object",
"properties": {
@ -1692,23 +2087,6 @@ var doc = `{
}
}
},
"web.Error": {
"type": "object",
"properties": {
"err": {
"type": "error"
},
"fields": {
"type": "array",
"items": {
"$ref": "#/definitions/web.FieldError"
}
},
"status": {
"type": "integer"
}
}
},
"web.ErrorResponse": {
"type": "object",
"properties": {

View File

@ -49,7 +49,7 @@
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -64,13 +64,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -123,13 +116,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
@ -178,38 +164,26 @@
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/user.Token"
},
"headers": {
"Token": {
"type": "string",
"description": "qwerty"
}
}
},
"200": {},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.Error"
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"401": {
"description": "Unauthorized",
"schema": {
"type": "object",
"$ref": "#/definitions/web.Error"
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.Error"
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
@ -330,8 +304,8 @@
}
],
"responses": {
"200": {
"description": "OK",
"201": {
"description": "Created",
"schema": {
"type": "object",
"$ref": "#/definitions/project.ProjectResponse"
@ -397,7 +371,7 @@
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -412,13 +386,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -460,7 +427,7 @@
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -475,13 +442,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -534,13 +494,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
@ -584,7 +537,7 @@
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -599,13 +552,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -642,8 +588,8 @@
}
],
"responses": {
"200": {
"description": "OK",
"201": {
"description": "Created",
"schema": {
"type": "object",
"$ref": "#/definitions/signup.SignupResponse"
@ -656,6 +602,83 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
}
},
"/user_accounts": {
"get": {
"security": [
{
"OAuth2Password": []
}
],
"description": "Find returns the existing user accounts in the system.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user_account"
],
"summary": "List user accounts",
"parameters": [
{
"type": "string",
"description": "Filter string, example: account_id = 'c4653bf9-5978-48b7-89c5-95704aebb7e2'",
"name": "where",
"in": "query"
},
{
"type": "string",
"description": "Order columns separated by comma, example: created_at desc",
"name": "order",
"in": "query"
},
{
"type": "integer",
"description": "Limit, example: 10",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"description": "Offset, example: 20",
"name": "offset",
"in": "query"
},
{
"type": "boolean",
"description": "Included Archived, example: false",
"name": "included-archived",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/user_account.UserAccountResponse"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
@ -671,6 +694,293 @@
}
}
}
},
"post": {
"security": [
{
"OAuth2Password": []
}
],
"description": "Create inserts a new user account into the system.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user_account"
],
"summary": "Create new user account.",
"parameters": [
{
"description": "User Account details",
"name": "data",
"in": "body",
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/user_account.UserAccountCreateRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"type": "object",
"$ref": "#/definitions/user_account.UserAccountResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
},
"delete": {
"security": [
{
"OAuth2Password": []
}
],
"description": "Delete removes the specified user account from the system.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Delete user account by user ID and account ID",
"parameters": [
{
"type": "string",
"description": "UserAccount ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"204": {},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
},
"patch": {
"security": [
{
"OAuth2Password": []
}
],
"description": "Update updates the specified user account in the system.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Update user account by user ID and account ID",
"parameters": [
{
"description": "Update fields",
"name": "data",
"in": "body",
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/user_account.UserAccountUpdateRequest"
}
}
],
"responses": {
"204": {},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
}
},
"/user_accounts/archive": {
"patch": {
"security": [
{
"OAuth2Password": []
}
],
"description": "Archive soft-deletes the specified user account from the system.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Archive user account by user ID and account ID",
"parameters": [
{
"description": "Update fields",
"name": "data",
"in": "body",
"required": true,
"schema": {
"type": "object",
"$ref": "#/definitions/user_account.UserAccountArchiveRequest"
}
}
],
"responses": {
"204": {},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
}
},
"/user_accounts/{id}": {
"get": {
"security": [
{
"OAuth2Password": []
}
],
"description": "Read returns the specified user account from the system.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user_account"
],
"summary": "Get user account by ID",
"parameters": [
{
"type": "string",
"description": "UserAccount ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/user_account.UserAccountResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
}
}
}
},
"/users": {
@ -740,13 +1050,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -786,8 +1089,8 @@
}
],
"responses": {
"200": {
"description": "OK",
"201": {
"description": "Created",
"schema": {
"type": "object",
"$ref": "#/definitions/user.UserResponse"
@ -807,13 +1110,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -853,7 +1149,7 @@
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -868,13 +1164,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -916,7 +1205,7 @@
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -931,13 +1220,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -979,7 +1261,7 @@
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -994,13 +1276,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -1039,7 +1314,7 @@
}
],
"responses": {
"201": {},
"200": {},
"400": {
"description": "Bad Request",
"schema": {
@ -1047,15 +1322,8 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"401": {
"description": "Unauthorized",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
@ -1113,13 +1381,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
@ -1163,7 +1424,7 @@
}
],
"responses": {
"201": {},
"204": {},
"400": {
"description": "Bad Request",
"schema": {
@ -1178,13 +1439,6 @@
"$ref": "#/definitions/web.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"$ref": "#/definitions/web.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
@ -1422,17 +1676,14 @@
}
}
},
"signup.SignupRequest": {
"type": "object",
"properties": {
"account": {
"signup.SignupAccount": {
"type": "object",
"required": [
"name",
"address1",
"city",
"region",
"country",
"name",
"region",
"zipcode"
],
"properties": {
@ -1470,11 +1721,37 @@
}
}
},
"signup.SignupRequest": {
"type": "object",
"properties": {
"account": {
"type": "object",
"$ref": "#/definitions/signup.SignupAccount"
},
"user": {
"type": "object",
"$ref": "#/definitions/signup.SignupUser"
}
}
},
"signup.SignupResponse": {
"type": "object",
"properties": {
"account": {
"type": "object",
"$ref": "#/definitions/account.AccountResponse"
},
"user": {
"type": "object",
"$ref": "#/definitions/user.UserResponse"
}
}
},
"signup.SignupUser": {
"type": "object",
"required": [
"name",
"email",
"name",
"password"
],
"properties": {
@ -1495,33 +1772,6 @@
"example": "SecretString"
}
}
}
}
},
"signup.SignupResponse": {
"type": "object",
"properties": {
"account": {
"type": "string"
},
"user": {
"type": "string"
}
}
},
"user.Token": {
"type": "object",
"properties": {
"access_token": {
"type": "string"
},
"expiry": {
"type": "string"
},
"token_type": {
"type": "string"
}
}
},
"user.UserArchiveRequest": {
"type": "object",
@ -1643,6 +1893,151 @@
}
}
},
"user_account.UserAccountArchiveRequest": {
"type": "object",
"required": [
"account_id",
"user_id"
],
"properties": {
"account_id": {
"type": "string",
"example": "c4653bf9-5978-48b7-89c5-95704aebb7e2"
},
"user_id": {
"type": "string",
"example": "d69bdef7-173f-4d29-b52c-3edc60baf6a2"
}
}
},
"user_account.UserAccountCreateRequest": {
"type": "object",
"required": [
"account_id",
"roles",
"user_id"
],
"properties": {
"account_id": {
"type": "string",
"example": "c4653bf9-5978-48b7-89c5-95704aebb7e2"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": [
"admin",
"user"
]
},
"example": [
"admin"
]
},
"status": {
"type": "string",
"enum": [
"active",
"invited",
"disabled"
],
"example": "active"
},
"user_id": {
"type": "string",
"example": "d69bdef7-173f-4d29-b52c-3edc60baf6a2"
}
}
},
"user_account.UserAccountResponse": {
"type": "object",
"required": [
"roles"
],
"properties": {
"account_id": {
"type": "string",
"example": "c4653bf9-5978-48b7-89c5-95704aebb7e2"
},
"archived_at": {
"type": "object",
"$ref": "#/definitions/web.TimeResponse"
},
"created_at": {
"type": "object",
"$ref": "#/definitions/web.TimeResponse"
},
"id": {
"type": "string",
"example": "d69bdef7-173f-4d29-b52c-3edc60baf6a2"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": [
"admin",
"user"
]
},
"example": [
"admin"
]
},
"status": {
"type": "object",
"$ref": "#/definitions/web.EnumResponse"
},
"updated_at": {
"type": "object",
"$ref": "#/definitions/web.TimeResponse"
},
"user_id": {
"type": "string",
"example": "d69bdef7-173f-4d29-b52c-3edc60baf6a2"
}
}
},
"user_account.UserAccountUpdateRequest": {
"type": "object",
"required": [
"account_id",
"user_id"
],
"properties": {
"account_id": {
"type": "string",
"example": "c4653bf9-5978-48b7-89c5-95704aebb7e2"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": [
"admin",
"user"
]
},
"example": [
"user"
]
},
"status": {
"type": "string",
"enum": [
"active",
"invited",
"disabled"
],
"example": "disabled"
},
"user_id": {
"type": "string",
"example": "d69bdef7-173f-4d29-b52c-3edc60baf6a2"
}
}
},
"web.EnumOption": {
"type": "object",
"properties": {
@ -1679,23 +2074,6 @@
}
}
},
"web.Error": {
"type": "object",
"properties": {
"err": {
"type": "error"
},
"fields": {
"type": "array",
"items": {
"$ref": "#/definitions/web.FieldError"
}
},
"status": {
"type": "integer"
}
}
},
"web.ErrorResponse": {
"type": "object",
"properties": {

View File

@ -164,9 +164,7 @@ definitions:
required:
- id
type: object
signup.SignupRequest:
properties:
account:
signup.SignupAccount:
properties:
address1:
example: 221 Tatitlek Ave
@ -193,14 +191,32 @@ definitions:
example: "99686"
type: string
required:
- name
- address1
- city
- region
- country
- name
- region
- zipcode
type: object
signup.SignupRequest:
properties:
account:
$ref: '#/definitions/signup.SignupAccount'
type: object
user:
$ref: '#/definitions/signup.SignupUser'
type: object
type: object
signup.SignupResponse:
properties:
account:
$ref: '#/definitions/account.AccountResponse'
type: object
user:
$ref: '#/definitions/user.UserResponse'
type: object
type: object
signup.SignupUser:
properties:
email:
example: '{RANDOM_EMAIL}'
@ -215,27 +231,10 @@ definitions:
example: SecretString
type: string
required:
- name
- email
- name
- password
type: object
type: object
signup.SignupResponse:
properties:
account:
type: string
user:
type: string
type: object
user.Token:
properties:
access_token:
type: string
expiry:
type: string
token_type:
type: string
type: object
user.UserArchiveRequest:
properties:
id:
@ -322,6 +321,110 @@ definitions:
required:
- id
type: object
user_account.UserAccountArchiveRequest:
properties:
account_id:
example: c4653bf9-5978-48b7-89c5-95704aebb7e2
type: string
user_id:
example: d69bdef7-173f-4d29-b52c-3edc60baf6a2
type: string
required:
- account_id
- user_id
type: object
user_account.UserAccountCreateRequest:
properties:
account_id:
example: c4653bf9-5978-48b7-89c5-95704aebb7e2
type: string
roles:
example:
- admin
items:
enum:
- admin
- user
type: string
type: array
status:
enum:
- active
- invited
- disabled
example: active
type: string
user_id:
example: d69bdef7-173f-4d29-b52c-3edc60baf6a2
type: string
required:
- account_id
- roles
- user_id
type: object
user_account.UserAccountResponse:
properties:
account_id:
example: c4653bf9-5978-48b7-89c5-95704aebb7e2
type: string
archived_at:
$ref: '#/definitions/web.TimeResponse'
type: object
created_at:
$ref: '#/definitions/web.TimeResponse'
type: object
id:
example: d69bdef7-173f-4d29-b52c-3edc60baf6a2
type: string
roles:
example:
- admin
items:
enum:
- admin
- user
type: string
type: array
status:
$ref: '#/definitions/web.EnumResponse'
type: object
updated_at:
$ref: '#/definitions/web.TimeResponse'
type: object
user_id:
example: d69bdef7-173f-4d29-b52c-3edc60baf6a2
type: string
required:
- roles
type: object
user_account.UserAccountUpdateRequest:
properties:
account_id:
example: c4653bf9-5978-48b7-89c5-95704aebb7e2
type: string
roles:
example:
- user
items:
enum:
- admin
- user
type: string
type: array
status:
enum:
- active
- invited
- disabled
example: disabled
type: string
user_id:
example: d69bdef7-173f-4d29-b52c-3edc60baf6a2
type: string
required:
- account_id
- user_id
type: object
web.EnumOption:
properties:
selected:
@ -347,17 +450,6 @@ definitions:
example: active_etc
type: string
type: object
web.Error:
properties:
err:
type: error
fields:
items:
$ref: '#/definitions/web.FieldError'
type: array
status:
type: integer
type: object
web.ErrorResponse:
properties:
error:
@ -440,7 +532,7 @@ paths:
produces:
- application/json
responses:
"201": {}
"204": {}
"400":
description: Bad Request
schema:
@ -451,11 +543,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
@ -490,11 +577,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"403":
description: Forbidden
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
@ -527,29 +609,21 @@ paths:
produces:
- application/json
responses:
"200":
description: OK
headers:
Token:
description: qwerty
type: string
schema:
$ref: '#/definitions/user.Token'
type: object
"200": {}
"400":
description: Bad Request
schema:
$ref: '#/definitions/web.Error'
$ref: '#/definitions/web.ErrorResponse'
type: object
"403":
description: Forbidden
"401":
description: Unauthorized
schema:
$ref: '#/definitions/web.Error'
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/web.Error'
$ref: '#/definitions/web.ErrorResponse'
type: object
security:
- BasicAuth: []
@ -627,7 +701,7 @@ paths:
produces:
- application/json
responses:
"201": {}
"204": {}
"400":
description: Bad Request
schema:
@ -638,11 +712,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
@ -668,8 +737,8 @@ paths:
produces:
- application/json
responses:
"200":
description: OK
"201":
description: Created
schema:
$ref: '#/definitions/project.ProjectResponse'
type: object
@ -712,7 +781,7 @@ paths:
produces:
- application/json
responses:
"201": {}
"204": {}
"400":
description: Bad Request
schema:
@ -723,11 +792,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
@ -761,11 +825,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"403":
description: Forbidden
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
@ -797,7 +856,7 @@ paths:
produces:
- application/json
responses:
"201": {}
"204": {}
"400":
description: Bad Request
schema:
@ -808,11 +867,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
@ -839,11 +893,39 @@ paths:
produces:
- application/json
responses:
"200":
description: OK
"201":
description: Created
schema:
$ref: '#/definitions/signup.SignupResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
summary: Signup handles new account creation.
tags:
- signup
/user_accounts:
delete:
consumes:
- application/json
description: Delete removes the specified user account from the system.
parameters:
- description: UserAccount ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"204": {}
"400":
description: Bad Request
schema:
@ -859,9 +941,222 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
summary: Signup handles new account creation.
security:
- OAuth2Password: []
summary: Delete user account by user ID and account ID
tags:
- signup
- user
get:
consumes:
- application/json
description: Find returns the existing user accounts in the system.
parameters:
- description: 'Filter string, example: account_id = ''c4653bf9-5978-48b7-89c5-95704aebb7e2'''
in: query
name: where
type: string
- description: 'Order columns separated by comma, example: created_at desc'
in: query
name: order
type: string
- description: 'Limit, example: 10'
in: query
name: limit
type: integer
- description: 'Offset, example: 20'
in: query
name: offset
type: integer
- description: 'Included Archived, example: false'
in: query
name: included-archived
type: boolean
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/user_account.UserAccountResponse'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"403":
description: Forbidden
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
security:
- OAuth2Password: []
summary: List user accounts
tags:
- user_account
patch:
consumes:
- application/json
description: Update updates the specified user account in the system.
parameters:
- description: Update fields
in: body
name: data
required: true
schema:
$ref: '#/definitions/user_account.UserAccountUpdateRequest'
type: object
produces:
- application/json
responses:
"204": {}
"400":
description: Bad Request
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"403":
description: Forbidden
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
security:
- OAuth2Password: []
summary: Update user account by user ID and account ID
tags:
- user
post:
consumes:
- application/json
description: Create inserts a new user account into the system.
parameters:
- description: User Account details
in: body
name: data
required: true
schema:
$ref: '#/definitions/user_account.UserAccountCreateRequest'
type: object
produces:
- application/json
responses:
"201":
description: Created
schema:
$ref: '#/definitions/user_account.UserAccountResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"403":
description: Forbidden
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
security:
- OAuth2Password: []
summary: Create new user account.
tags:
- user_account
/user_accounts/{id}:
get:
consumes:
- application/json
description: Read returns the specified user account from the system.
parameters:
- description: UserAccount ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/user_account.UserAccountResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
security:
- OAuth2Password: []
summary: Get user account by ID
tags:
- user_account
/user_accounts/archive:
patch:
consumes:
- application/json
description: Archive soft-deletes the specified user account from the system.
parameters:
- description: Update fields
in: body
name: data
required: true
schema:
$ref: '#/definitions/user_account.UserAccountArchiveRequest'
type: object
produces:
- application/json
responses:
"204": {}
"400":
description: Bad Request
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"403":
description: Forbidden
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
security:
- OAuth2Password: []
summary: Archive user account by user ID and account ID
tags:
- user
/users:
get:
consumes:
@ -903,11 +1198,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"403":
description: Forbidden
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
@ -933,7 +1223,7 @@ paths:
produces:
- application/json
responses:
"201": {}
"204": {}
"400":
description: Bad Request
schema:
@ -944,11 +1234,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
@ -974,8 +1259,8 @@ paths:
produces:
- application/json
responses:
"200":
description: OK
"201":
description: Created
schema:
$ref: '#/definitions/user.UserResponse'
type: object
@ -989,11 +1274,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
@ -1018,7 +1298,7 @@ paths:
produces:
- application/json
responses:
"201": {}
"204": {}
"400":
description: Bad Request
schema:
@ -1029,11 +1309,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
@ -1067,11 +1342,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"403":
description: Forbidden
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
@ -1103,7 +1373,7 @@ paths:
produces:
- application/json
responses:
"201": {}
"204": {}
"400":
description: Bad Request
schema:
@ -1114,11 +1384,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
@ -1145,7 +1410,7 @@ paths:
produces:
- application/json
responses:
"201": {}
"204": {}
"400":
description: Bad Request
schema:
@ -1156,11 +1421,6 @@ paths:
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"500":
description: Internal Server Error
schema:
@ -1185,19 +1445,14 @@ paths:
produces:
- application/json
responses:
"201": {}
"200": {}
"400":
description: Bad Request
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"403":
description: Forbidden
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object
"404":
description: Not Found
"401":
description: Unauthorized
schema:
$ref: '#/definitions/web.ErrorResponse'
type: object

View File

@ -11,6 +11,7 @@ import (
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
"github.com/jmoiron/sqlx"
"gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis"
_ "geeks-accelerator/oss/saas-starter-kit/example-project/internal/signup"
)
// API returns a handler for a set of routes.

View File

@ -37,6 +37,12 @@ COPY --from=builder /gosrv /
COPY --from=builder /static /static
COPY --from=builder /templates /templates
ARG service
ENV SERVICE_NAME $service
ARG env="dev"
ENV ENV $env
ARG gogc="20"
ENV GOGC $gogc

View File

@ -2,7 +2,7 @@ module geeks-accelerator/oss/saas-starter-kit/example-project
require (
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
github.com/aws/aws-sdk-go v1.19.33
github.com/aws/aws-sdk-go v1.20.15
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/dimfeld/httptreemux v5.0.1+incompatible
github.com/dustin/go-humanize v1.0.0

View File

@ -7,6 +7,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5Vpd
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/aws/aws-sdk-go v1.19.33 h1:qz9ZQtxCUuwBKdc5QiY6hKuISYGeRQyLVA2RryDEDaQ=
github.com/aws/aws-sdk-go v1.19.33/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.20.15 h1:y9ts8MJhB7ReUidS6Rq+0KxdFeL01J+pmOlGq6YqpiQ=
github.com/aws/aws-sdk-go v1.20.15/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
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=

View File

@ -0,0 +1,152 @@
package devops
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/pkg/errors"
)
func findProjectGoModFile() (string, error) {
var err error
projectRoot, err := os.Getwd()
if err != nil {
return "", errors.WithMessage(err, "failed to get current working directory")
}
// Try to find the project root for looking for the go.mod file in a parent directory.
var goModFile string
testDir := projectRoot
for i := 0; i < 3; i++ {
if goModFile != "" {
testDir = filepath.Join(testDir, "../")
}
goModFile = filepath.Join(testDir, "go.mod")
ok, _ := exists(goModFile)
if ok {
projectRoot = testDir
break
}
}
// Verify the go.mod file was found.
ok, err := exists(goModFile)
if err != nil {
return "", errors.WithMessagef(err, "failed to load go.mod for project using project root %s")
} else if !ok {
return "", errors.Errorf("failed to locate project go.mod in project root %s", projectRoot)
}
return goModFile, nil
}
// findServiceDockerFile finds the service directory.
func findServiceDockerFile(projectRoot, targetService string) (string, error) {
checkDirs := []string{
filepath.Join(projectRoot, "cmd", targetService),
filepath.Join(projectRoot, "tools", targetService),
}
var dockerFile string
for _, cd := range checkDirs {
// Check to see if directory contains Dockerfile.
tf := filepath.Join(cd, "Dockerfile")
fmt.Println(tf)
ok, _ := exists(tf)
if ok {
dockerFile = tf
break
}
}
if dockerFile == "" {
return "", errors.Errorf("failed to locate Dockerfile for service %s", targetService)
}
return dockerFile, nil
}
// getTargetEnv checks for an env var that is prefixed with the current target env.
func getTargetEnv(targetEnv, envName string) string {
k := fmt.Sprintf("%s_%s", strings.ToUpper(targetEnv), envName)
if v := os.Getenv(k); v != "" {
// Set the non prefixed env var with the prefixed value.
os.Setenv(envName, v )
return v
}
return os.Getenv(envName)
}
// loadGoModName parses out the module name from go.mod.
func loadGoModName(goModFile string ) (string, error) {
ok, err := exists(goModFile)
if err != nil {
return "", errors.WithMessage(err, "Failed to load go.mod for project")
} else if !ok {
return "", errors.Errorf("Failed to locate project go.mod at %s", goModFile)
}
b, err := ioutil.ReadFile(goModFile)
if err != nil {
return"", errors.WithMessagef(err, "Failed to read go.mod at %s", goModFile)
}
var name string
lines := strings.Split(string(b), "\n")
for _, l := range lines {
if strings.HasPrefix(l, "module ") {
name = strings.TrimSpace(strings.Split(l, " ")[1])
break
}
}
return name, nil
}
// exists returns a bool as to whether a file path exists.
func exists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return true, err
}
type EnvVars []string
// execCmds executes a set of commands.
func execCmds(workDir string, envVars *EnvVars, cmds ...[]string) ([]string, error) {
if envVars == nil {
ev := EnvVars(os.Environ())
envVars = &ev
}
var results []string
for _, cmdVals := range cmds {
cmd := exec.Command(cmdVals[0], cmdVals[1:]...)
cmd.Dir = workDir
cmd.Env = *envVars
out, err := cmd.CombinedOutput()
if err != nil {
return results, errors.WithMessagef(err, "failed to execute %s - %s\n%s", strings.Join(cmdVals, " "), string(out))
}
results = append(results, string(out))
// Update the current env vars after command has been executed.
ev := EnvVars(cmd.Env)
envVars = &ev
}
return results, nil
}

View File

@ -0,0 +1,213 @@
package devops
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/tests"
"github.com/pkg/errors"
)
// requiredCmdsBuild proves a list of required executables for completing build.
var requiredCmdsBuild = [][]string{
[]string{"docker", "version", "-f", "{{.Client.Version}}"},
}
// Run is the main entrypoint for building a service for a given target env.
func ServiceBuild(log *log.Logger, projectRoot, targetService, targetEnv, releaseImage string, noPush, noCache bool) error {
log.SetPrefix(log.Prefix() + " build : ")
//
log.Println("Verify required commands are installed.")
for _, cmdVals := range requiredCmdsBuild {
cmd := exec.Command(cmdVals[0], cmdVals[1:]...)
cmd.Env = os.Environ()
out, err := cmd.CombinedOutput()
if err != nil {
return errors.WithMessagef(err, "failed to execute %s - %s\n%s", strings.Join(cmdVals, " "), string(out))
}
log.Printf("\t%s\t%s - %s", tests.Success, cmdVals[0], string(out))
}
// When project root directory is empty or set to current working path, then search for the project root by locating
// the go.mod file.
var goModFile string
if projectRoot == "" || projectRoot == "." {
log.Println("Attempting to location project root directory from current working directory.")
var err error
goModFile, err = findProjectGoModFile()
if err != nil {
return err
}
projectRoot = filepath.Dir(goModFile)
} else {
log.Println("Using supplied project root directory.")
goModFile = filepath.Join(projectRoot, "go.mod")
}
log.Printf("\t\tproject root: %s", projectRoot)
log.Printf("\t\tgo.mod: %s", goModFile)
log.Printf("\t%s\tFound project root directory.", tests.Success)
log.Println("Extracting go module name from go.mod.")
modName, err := loadGoModName(goModFile)
if err != nil {
return err
}
log.Printf("\t\tmodule name: %s", modName)
log.Printf("\t%s\tgo module name.", tests.Success)
log.Println("Determining the project name.")
projectName := getTargetEnv(targetEnv, "PROJECT_NAME")
if projectName != "" {
log.Printf("\t\tproject name: %s", projectName)
log.Printf("\t%s\tFound env variable.", tests.Success)
} else {
projectName = filepath.Base(modName)
log.Printf("\t\tproject name: %s", projectName)
log.Printf("\t%s\tSet from go module.", tests.Success)
}
log.Println("Attempting to locate service directory from project root directory.")
dockerFile, err := findServiceDockerFile(projectRoot, targetService)
if err != nil {
return err
}
serviceDir := filepath.Dir(dockerFile)
log.Printf("\t\tservice directory: %s", serviceDir)
log.Printf("\t\tdockerfile: %s", dockerFile)
log.Printf("\t%s\tFound service directory.", tests.Success)
log.Println("Verify release image.")
if releaseImage == "" {
if v := os.Getenv("CI_REGISTRY_IMAGE"); v != "" {
releaseImage = fmt.Sprintf("%s:%s-%s", v, targetService, targetEnv)
} else {
releaseImage = fmt.Sprintf("%s/%s:latest", targetService, targetEnv)
noPush = true
}
}
log.Printf("\t\trelease image: %s", releaseImage)
log.Printf("\t%s\tRelease image valid.", tests.Success)
// Load the AWS
log.Println("Verify AWS credentials.")
awsCreds, err := GetAwsCredentials(targetEnv)
if err != nil {
return err
}
log.Printf("\t\tAccessKeyID: %s", awsCreds.AccessKeyID)
log.Printf("\t\tRegion: %s", awsCreds.Region)
// Set default AWS Registry Name if needed.
if awsCreds.RepositoryName == "" {
awsCreds.RepositoryName = projectName
log.Printf("\t\tSetting Repository Name to Project Name.")
}
log.Printf("\t\tRepository Name: %s", awsCreds.RepositoryName)
log.Printf("\t%s\tAWS credentials valid.", tests.Success)
// Pull the current env variables to be passed in for command execution.
envVars := EnvVars(os.Environ())
// Do the docker build.
{
cmdVals := []string{
"docker",
"build",
"--file=" + dockerFile,
"--build-arg", "service=" + targetService,
"--build-arg", "env=" + targetEnv,
"-t", releaseImage,
}
if noCache {
cmdVals = append(cmdVals, "--no-cache")
}
cmdVals = append(cmdVals, ".")
log.Printf("starting docker build: \n\t%s\n", strings.Join(cmdVals, " "))
out, err := execCmds(projectRoot, &envVars, cmdVals)
if err != nil {
return err
}
log.Printf("build complete\n\t%s\n", string(out[0]))
}
// Push the newly built docker container to the registry.
if !noPush {
log.Println("Push release image.")
_, err = execCmds(projectRoot, &envVars, []string{"docker", "push", releaseImage})
if err != nil {
return err
}
}
if awsCreds.RepositoryName != "" {
awsRepo, err := EcrGetOrCreateRepository(awsCreds, projectName, targetEnv)
if err != nil {
return err
}
maxImages := defaultAwsRegistryMaxImages
if v := getTargetEnv(targetEnv, "AWS_REPOSITORY_MAX_IMAGES"); v != "" {
maxImages, err = strconv.Atoi(v)
if err != nil {
return errors.WithMessagef(err, "Failed to parse max ECR images")
}
}
log.Println("Purging old ECR images.")
err = EcrPurgeImages(log, awsCreds, maxImages)
if err != nil {
return err
}
log.Println("Retrieve ECR authorization token used for docker login.")
dockerLogin, err := GetEcrLogin(awsCreds)
if err != nil {
return err
}
awsRegistryImage := *awsRepo.RepositoryUri
// Login to AWS docker registry and pull release image locally.
log.Println("Push release image to ECR.")
log.Printf("\t\t%s", awsRegistryImage)
_, err = execCmds(projectRoot, &envVars, dockerLogin, []string{"docker", "tag", releaseImage, awsRegistryImage}, []string{"docker", "push", awsRegistryImage})
if err != nil {
return err
}
tag1 := targetEnv+"-"+targetService
log.Printf("\t\ttagging as %s", tag1)
_, err = execCmds(projectRoot, &envVars, []string{"docker", "tag", releaseImage, awsRegistryImage+":"+tag1}, []string{"docker", "push", awsRegistryImage+":"+tag1})
if err != nil {
return err
}
if v := os.Getenv("CI_COMMIT_REF_NAME"); v != "" {
tag2 := tag1 + "-" + v
log.Printf("\t\ttagging as %s", tag2)
_, err = execCmds(projectRoot, &envVars, []string{"docker", "tag", releaseImage, awsRegistryImage+ ":" + tag2}, []string{"docker", "push", awsRegistryImage + ":" + tag2})
if err != nil {
return err
}
}
}
return nil
}

View File

@ -0,0 +1,195 @@
package devops
import (
"fmt"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/tests"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/pkg/errors"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
)
/*
secretsmanager:GetSecretValue
ecr:GetAuthorizationToken
ecr:ListImages
ecr:DescribeRepositories
CreateRepository
*/
// requiredCmdsBuild proves a list of required executables for completing build.
var requiredCmdsDeploy = [][]string{
[]string{"docker", "version", "-f", "{{.Client.Version}}"},
}
// Run is the main entrypoint for deploying a service for a given target env.
func ServiceDeploy(log *log.Logger, projectRoot, targetService, targetEnv, releaseImage, ecsCluster string, enableVpc bool, noBuild, noDeploy, noCache bool) error {
log.SetPrefix(log.Prefix() + " deploy : ")
//
log.Println("Verify required commands are installed.")
for _, cmdVals := range requiredCmdsDeploy {
cmd := exec.Command(cmdVals[0], cmdVals[1:]...)
cmd.Env = os.Environ()
out, err := cmd.CombinedOutput()
if err != nil {
return errors.WithMessagef(err, "failed to execute %s - %s\n%s", strings.Join(cmdVals, " "), string(out))
}
log.Printf("\t%s\t%s - %s", tests.Success, cmdVals[0], string(out))
}
// When project root directory is empty or set to current working path, then search for the project root by locating
// the go.mod file.
var goModFile string
if projectRoot == "" || projectRoot == "." {
log.Println("Attempting to location project root directory from current working directory.")
var err error
goModFile, err = findProjectGoModFile()
if err != nil {
return err
}
projectRoot = filepath.Dir(goModFile)
} else {
log.Println("Using supplied project root directory.")
goModFile = filepath.Join(projectRoot, "go.mod")
}
log.Printf("\t\tproject root: %s", projectRoot)
log.Printf("\t\tgo.mod: %s", goModFile)
log.Printf("\t%s\tFound project root directory.", tests.Success)
log.Println("Extracting go module name from go.mod.")
modName, err := loadGoModName(goModFile)
if err != nil {
return err
}
log.Printf("\t\tmodule name: %s", modName)
log.Printf("\t%s\tgo module name.", tests.Success)
log.Println("Determining the project name.")
projectName := getTargetEnv(targetEnv, "PROJECT_NAME")
if projectName != "" {
log.Printf("\t\tproject name: %s", projectName)
log.Printf("\t%s\tFound env variable.", tests.Success)
} else {
projectName = filepath.Base(modName)
log.Printf("\t\tproject name: %s", projectName)
log.Printf("\t%s\tSet from go module.", tests.Success)
}
log.Println("Attempting to locate service directory from project root directory.")
dockerFile, err := findServiceDockerFile(projectRoot, targetService)
if err != nil {
return err
}
serviceDir := filepath.Dir(dockerFile)
log.Printf("\t\tservice directory: %s", serviceDir)
log.Printf("\t\tdockerfile: %s", dockerFile)
log.Printf("\t%s\tFound service directory.", tests.Success)
log.Println("Verify release image.")
var noPull bool
if releaseImage == "" {
if v := os.Getenv("CI_REGISTRY_IMAGE"); v != "" {
releaseImage = fmt.Sprintf("%s:%s-%s", v, targetService, targetEnv)
} else {
releaseImage = fmt.Sprintf("%s/%s:latest", targetService, targetEnv)
noPull = true
}
}
log.Printf("\t\trelease image: %s", releaseImage)
log.Printf("\t%s\tRelease image valid.", tests.Success)
log.Println("Verify AWS credentials.")
awsCreds, err := GetAwsCredentials(targetEnv)
if err != nil {
return err
}
if ecsCluster == "" {
ecsCluster = filepath.Base(goModFile) + "-" + targetEnv
log.Printf("AWS ECS cluster not set, assigning default value %s.", ecsCluster)
}
// Create default service name used for deployment.
serviceName := targetService + "-" + targetEnv
_ = serviceName
awsRepo, err := EcrGetOrCreateRepository(awsCreds, projectName, targetEnv)
if err != nil {
return err
}
envVars := EnvVars(os.Environ())
if !noPull {
log.Println("Retrieve ECR authorization token used for docker login.")
dockerLogin, err := GetEcrLogin(awsCreds)
if err != nil {
return err
}
// Login to AWS docker registry and pull release image locally.
log.Println("Pull Release image from ECR.")
log.Printf("\t\t%s", releaseImage)
_, err = execCmds(projectRoot, &envVars, dockerLogin, []string{"docker", "pull", releaseImage})
if err != nil {
return err
}
}
return nil
}
// GetDatadogApiKey returns the Datadog API Key which can be either stored in an env var or in AWS Secrets Manager.
func GetDatadogApiKey(targetEnv string, creds *AwsCredentials) (string, error) {
// 1. Check env vars for [DEV|STAGE|PROD]_DD_API_KEY and DD_API_KEY
apiKey := getTargetEnv(targetEnv, "DD_API_KEY")
if apiKey != "" {
return apiKey, nil
}
// 2. Check AWS Secrets Manager for datadog entry prefixed with target env.
prefixedSecretId := strings.ToUpper(targetEnv) + "/DATADOG"
var err error
apiKey, err = GetAwsSecretValue(creds, prefixedSecretId)
if err != nil {
if aerr, ok := errors.Cause(err).(awserr.Error); !ok || aerr.Code() != secretsmanager.ErrCodeResourceNotFoundException {
return "", err
}
} else if apiKey != "" {
return apiKey, nil
}
// 3. Check AWS Secrets Manager for datadog entry.
secretId := "DATADOG"
apiKey, err = GetAwsSecretValue(creds, secretId)
if err != nil {
if aerr, ok := errors.Cause(err).(awserr.Error); !ok || aerr.Code() != secretsmanager.ErrCodeResourceNotFoundException {
return "", err
}
}
return apiKey, nil
}

View File

@ -11,6 +11,7 @@ import (
"path/filepath"
"strings"
"geeks-accelerator/oss/saas-starter-kit/example-project/tools/truss/cmd/devops"
"geeks-accelerator/oss/saas-starter-kit/example-project/tools/truss/cmd/dbtable2crud"
"github.com/kelseyhightower/envconfig"
"github.com/lib/pq"
@ -205,6 +206,63 @@ func main() {
return dbtable2crud.Run(masterDb, log, cfg.DB.Database, dbTable, modelFile, modelName, templateDir, projectPath, c.Bool("saveChanges"))
},
},
{
Name: "build",
Aliases: []string{"serviceBuild"},
Usage: "-service=web-api -env=dev [-image=gitlab.com/example-project:latest] [-root=.]",
Flags: []cli.Flag{
cli.StringFlag{Name: "service", Usage: "name of cmd"},
cli.StringFlag{Name: "env", Usage: "dev, stage, or prod"},
cli.StringFlag{Name: "image", Usage: "release image used to tag docker build"},
cli.StringFlag{Name: "root", Usage: "project root directory"},
cli.BoolFlag{Name: "no_push", Usage: "skip docker push after build"},
cli.BoolFlag{Name: "no_cache", Usage: "skip docker cache"},
},
Action: func(c *cli.Context) error {
service := strings.TrimSpace(c.String("service"))
env := strings.TrimSpace(c.String("env"))
image := strings.TrimSpace(c.String("image"))
projectRoot := strings.TrimSpace(c.String("root"))
noPush := c.Bool("no_push")
noCache := c.Bool("no_cache")
if image == "-" {
image = ""
}
return devops.ServiceBuild(log, projectRoot, service, env, image, noPush, noCache)
},
},
{
Name: "deploy",
Aliases: []string{"serviceDeploy"},
Usage: "-service=web-api -env=dev [-image=gitlab.com/example-project:latest] [-root=.]",
Flags: []cli.Flag{
cli.StringFlag{Name: "service", Usage: "name of cmd"},
cli.StringFlag{Name: "env", Usage: "dev, stage, or prod"},
cli.StringFlag{Name: "image", Usage: "release image used to tag docker build"},
cli.StringFlag{Name: "root", Usage: "project root directory"},
cli.StringFlag{Name: "cluster, ecs_cluster", Usage: "name of the AWS EC2 cluster."},
cli.BoolFlag{Name: "vpc, enable_vpc", Usage: "skip docker push after build"},
cli.BoolFlag{Name: "no_build", Usage: "skip docker push after build"},
cli.BoolFlag{Name: "no_deploy", Usage: "skip docker push after build"},
cli.BoolFlag{Name: "no_cache", Usage: "skip docker cache"},
},
Action: func(c *cli.Context) error {
service := strings.TrimSpace(c.String("service"))
env := strings.TrimSpace(c.String("env"))
image := strings.TrimSpace(c.String("image"))
projectRoot := strings.TrimSpace(c.String("root"))
ecsCluster := strings.TrimSpace(c.String("cluster"))
enableVpc := c.Bool("vpc")
if image == "-" {
image = ""
}
return devops.ServiceDeploy(log, projectRoot, service, env, image, ecsCluster, enableVpc)
},
},
}
err = app.Run(os.Args)

View File

@ -1,4 +1,32 @@
# Variables to configure Postgres for database migration.
export TRUSS_DB_HOST=127.0.0.1:5433
export TRUSS_DB_USER=postgres
export TRUSS_DB_PASS=postgres
export TRUSS_DB_DISABLE_TLS=true
# Variables to configure service build and deploy.
# export PROJECT_NAME=example-project
# Variables to configure AWS for service build and deploy.
# Use the same set for AWS credentials for all target envinments.
#AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXX
#AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXX
#AWS_REGION=us-west-2
#AWS_REPOSITORY_NAME=example-project
#AWS_REPOSITORY_MAX_IMAGES=1000
# AWS credentials can be prefixed with the target uppercased target envinments.
# This allows credentials unique accounts to be used for each target envinments.
# Default target envinments are: DEV, STAGE, PROD
#DEV_AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXX
#DEV_AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXX
#DEV_AWS_REGION=us-west-2
#DEV_AWS_REPOSITORY_NAME=example-project
#DEV_AWS_REPOSITORY_MAX_IMAGES=1000
# GitLab CI/CD environment variables. These are set by the GitLab when the build
# pipeline is running. These can be optional set for testing/debugging locally.
#CI_REGISTRY_IMAGE="registry.example.com/gitlab-org/gitlab-ce"
#CI_COMMIT_REF_NAME=master