When I got new repo from Developer

Dounpct
8 min readFeb 9, 2023

--

Objective

  • Overview what application need
  • Test application on local
  • Create Dockerfile with Scan SonarQube
  • Jenkins JCasC
  • Jenkins build and push to repository
  • Deploy to GCP with ArgoCD

Overview what application need

  • Application in Golang and have simple todo rest api. application need database to start

Test application on local

  • Clone repo
  • Create temp database on local
  • From source download dependency
go get ./...
  • Test run application
go run main.go

or

go build
./$appname
  • Test api
  • Finish test on local

Create Dockerfile for build (can done by developer)

FROM golang:1.16-alpine as build-stage

# Define environment variables
ENV APPNAME=devops-go-test \
WRKDIR=/app \
USER=appuser \
UID=10001

RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
"${USER}"

WORKDIR $WRKDIR

# copy and fetch external module
COPY go.mod go.sum ./
RUN go mod download && \
go mod verify

# ---
# # copy and build application
# # removing debug informations and compile only for linux target and disabling cross compilation
COPY . .

RUN GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o $APPNAME

FROM alpine as server-stage

# Define environment variables
ENV APPNAME=devops-go-test
ENV APPPATH=/app/$APPNAME
ENV WRKDIR=/build

WORKDIR $WRKDIR

# Copy builded code from the builder
COPY --from=build-stage $APPPATH $WRKDIR

USER 1001

EXPOSE 8080

CMD ["./devops-go-test"]
  • Test build docker in local
  • Test docker run
  • Add sonar-project.properties
# ------------------------------ Mandatory Parameters -----------------------------------#
# must be unique in a given SonarQube instance
sonar.projectKey=devops-go-test

# this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1.
sonar.projectName=devops-go-test

# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
# This property is optional if sonar.modules is set.
sonar.sources=.

# Server URL Local
sonar.host.url=http://sonarqube.local:9000/sonar/

# ------------------------------ Authentication ----------------------------------------
sonar.login=CHANGE-TO-CREDENCIAL-TO-AUTHEN-SONARQUBE

# ------------------------------ Exclusions / Inclusions -------------------------------
# Ignore test and vendor from source
sonar.exclusions=**/**_test.go,go.**,**/template/**,**/internal/app/**,**/internal/config/**,**/internal/secret/**,**/internal/applog.go,**/internal/util.go,main.**

# Source file extension *.go
sonar.sources.inclusions=**/*.go

# ------------------------ Unit Test and Coverage --------------------------------------
# Test Path
sonar.tests=./

# Test file (suffix = *_test.go)
sonar.test.inclusions=**/*_test.go

# Ignore vendor path
sonar.test.exclusions=**/vendor/**

# Unit Tests Results Import (go test -json ./controller > report.json)
sonar.go.tests.reportPaths=report.json

# Go Coverage Results Import (go test -coverprofile=coverage.out ./controller)
sonar.go.coverage.reportPaths=coverage.out
  • Add step run Sonarqube in Dockerfile
RUN GOOS=linux GOARCH=amd64 go test -v -coverprofile=coverage.out ./... && \
GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o $APPNAME

############################################################################################
# SonarQube
############################################################################################
FROM sonarsource/sonar-scanner-cli:4.6 as scanner-stage

ARG SCANFLAG=false
ENV SCANFLAG ${SCANFLAG}

WORKDIR /app
COPY . .
COPY --from=build-stage /app/sonar-project.properties /app/coverage.out* ./

RUN ls -alrt
RUN echo $SCANFLAG
RUN if [ $SCANFLAG == true ] ; then sonar-scanner ; else echo Argument scanner is $SCANFLAG ; fi
RUN ls /tmp -alrt

############################################################################################
# Build image
############################################################################################
  • So all of Dockerfile
FROM golang:1.16-alpine as build-stage

# Install git and mercurial
RUN apk update && \
apk --no-cache add git mercurial gcc g++ && \
rm -rf /var/cache/apk/*

# Define environment variables
ENV APPNAME=devops-go-test \
WRKDIR=/app \
USER=appuser \
UID=10001

RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
"${USER}"

WORKDIR $WRKDIR

# copy and fetch external module
COPY go.mod go.sum ./
RUN go mod download && \
go mod verify

# ---
# # copy and build application
# # removing debug informations and compile only for linux target and disabling cross compilation
COPY . .
RUN GOOS=linux GOARCH=amd64 go test -v -coverprofile=coverage.out ./... && \
GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o $APPNAME

############################################################################################
# SonarQube
############################################################################################
FROM sonarsource/sonar-scanner-cli:4.6 as scanner-stage

ARG SCANFLAG=false
ENV SCANFLAG ${SCANFLAG}

WORKDIR /app
COPY . .
COPY --from=build-stage /app/sonar-project.properties /app/coverage.out* ./

RUN ls -alrt
RUN echo $SCANFLAG
RUN if [ $SCANFLAG == true ] ; then sonar-scanner ; else echo Argument scanner is $SCANFLAG ; fi
RUN ls /tmp -alrt

############################################################################################
# Build image
############################################################################################

FROM alpine as server-stage

# Define environment variables
ENV APPNAME=devops-go-test
ENV APPPATH=/app/$APPNAME
ENV WRKDIR=/build

WORKDIR $WRKDIR

# Copy builded code from the builder
COPY --from=build-stage $APPPATH $WRKDIR
COPY --from=scanner-stage /tmp /tmp

USER 1001

EXPOSE 8080

CMD ["./devops-go-test"]
  • Test build docker again
docker build --no-cache -t devops-go-test:0.0.1 --build-arg "SCANFLAG=true" -f Dockerfile
  • Note : we choose download from Local Harbor for prevent Download rate limit from Docker Hub
  • Check Sonarqube Server

Jenkins JCasC

  • Create job config (can done by developer)
profile|multibranch-build-rf|build-TRUNK-BASE-NO-CD|DevopsBitBucketID|${BITBUCKET_REMOTE_SCRIPT_URL}|DevopsBitBucketID|${BRANCHES_TRUNCK_BASE_MONITOR}|false|true|false
multibranch-build-rf|Sandbox/Devops:sandbox|devops-go-test|build-TRUNK-BASE-NO-CD|${BITBUCKET_CI_URL}/devops-go-test.git
  • wait devops’s jenkins to update auto generate JCasC ,check syntax and have replace to developer’s team jenkins
  • example for jcasc update
- script: >
multibranchPipelineJob('Sandbox/Devops/multibranch-build-rf/devops-go-test')
{
branchSources
{
git
{
id('devops-go-test')
remote('https://bitbucket.org/devops/devops-go-test.git')
credentialsId('DevopsBitBucketID')
includes('master')
}
}

factory
{
remoteJenkinsFileWorkflowBranchProjectFactory
{
remoteJenkinsFile("templates/master/Jenkinsfile")
localMarker("")
matchBranches(false)
fallbackBranch("master")
}
}

configure { project ->
def factoryNode = project / 'factory'
factoryNode << owner(class:'org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject', reference:'../..')

factoryNode << remoteJenkinsFileSCM(class:'hudson.plugins.git.GitSCM') {
userRemoteConfigs
{
'hudson.plugins.git.UserRemoteConfig'
{
url('https://bitbucket.org/devops/devops-jenkins-ci-pipeline.git')
credentialsId('DevopsBitBucketID')
}
}

branches
{
'hudson.plugins.git.BranchSpec'
{
name('master')
}
}
}
}

configure { project ->
def extensionsNode = project / 'sources' / 'data' / 'jenkins.branch.BranchSource' / 'source' / 'traits'

extensionsNode << 'jenkins.plugins.git.traits.BranchDiscoveryTrait'{
}

extensionsNode << 'jenkins.plugins.git.traits.CloneOptionTrait' {
extension(class:'hudson.plugins.git.extensions.impl.CloneOption')
{
shallow(false)
noTags(false)
reference('')
timeout(39)
honorRefspec(false)
}
}

def branchSourceNode = project / 'sources' / 'data' / 'jenkins.branch.BranchSource'
branchSourceNode << 'buildStrategies' {

'jenkins.branch.buildstrategies.basic.NamedBranchBuildStrategyImpl'
{
'filters'
{
'jenkins.branch.buildstrategies.basic.NamedBranchBuildStrategyImpl_-WildcardsNameFilter'
{
includes('master')
}
}
}
}
}


configure { project ->
def extensionsNode = project / 'sources' / 'data' / 'jenkins.branch.BranchSource' / 'source' / 'traits'

extensionsNode << 'jenkins.plugins.git.traits.TagDiscoveryTrait'{
}

def branchSourceNode = project / 'sources' / 'data' / 'jenkins.branch.BranchSource' / 'buildStrategies'
branchSourceNode << 'jenkins.branch.buildstrategies.basic.TagBuildStrategyImpl'{
atLeastMillis(-1)
atMostMillis(86400000)
}
}



configure { project ->
def extensionsNode = project

extensionsNode << disabled(false)
}


triggers
{
cron('*/5 * * * *')
}
}
  • add application.yaml to pipeline-libraries (can done by developer)
application:
veracodeTeam: "DevOps"
Type: "GoLang"

build:
applySemverFlag: true
arguments:
#--build-arg DMPENV=dev-p2, the param can be separated by pipe
master: "DMPENV=dev-p2|SCANFLAG=true"
tag: "DMPENV=alpha-p2"
others: "DMPENV=alpha-p2"

deploy:
DeploymentAppName: devops-go-test

Jenkins build and push to repository

  • Result send to SonarQube Server
  • Set tag for release
  • Wait for jenkins auto build tags
  • Image keep both harbor and GCR

Deploy to GCP with ArgoCD (can done by developer)

  • add application set :
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: devops-test-applicationset
spec:
generators:
- list:
elements:
- tag: develop
namespace: dev
- tag: alpha
namespace: alpha
- tag: preprod
namespace: preprod
template:
metadata:
labels:
argocd.argoproj.io/instance: devops-test-applicationset
name: 'devops-test-{{tag}}'
spec:
project: devops-test
source:
path: 'devops-test'
repoURL: 'https://bitbucket.org/abc/helm-argocd-devops-np.git'
targetRevision: master
plugin:
name: avp-vault-params
env:
- name: INIT_ARGS
value: "helm dep update"
- name: ARG_PARAMETERS
value: "helm template devops-test -n {{namespace}}-devops-test . -f values/values-devops-test.yaml --set tags.{{tag}}=true "
destination:
server: https://kubernetes.default.svc
namespace: '{{namespace}}-devops-test'
syncPolicy:
syncOptions:
- CreateNamespace=true
  • add chart for values-devops-test.yaml
app-gcp:
develop:
workload:
create: true
fullnameOverride: devops-test
nameOverride: devops-test
replicaCount: 1
image:
tag: master-20230209123044-688b8
repository: gcr.io/test/devops-go-test
imagePullSecrets:
- name: gcr-authen
resources:
limits:
cpu: 1000m
memory: 1000Mi
requests:
cpu: 200m
memory: 200Mi
hpa:
create: true
min: 1
max: 1
cpuUtil: 70
ports:
- port: 8080
service:
create: true
ports:
- port: 8080
targetPort: 8080
name: http-web
env:
PORT: "8080"
DMPENV: dev-p2
probe:
readiness:
tcpSocket:
port: 8080
initDelay: 5
timeout: 5
period: 3
success: 1
failure: 3 # end readinessprobe
liveness:
tcpSocket:
port: 8080
initDelay: 0
timeout: 1
period: 3
success: 1
failure: 3 # end livenessprobe
istio:
create: true
istioGateway:
create: true
selector:
istio: 'ingressgateway-bn-int'
hosts:
- 'dev-devops-test/*.xxx.xxx'
server:
protocol: HTTP
istioVirtualService:
create: true
hosts:
internal: 'devops-test-dev-devops-test.xxx.xxx'
port: 8080
subset: master
istioDestinationRule:
create: true
subsets:
- name: master
labels:
version: master
  • dependency chart
apiVersion: v2
name: helm-devops-test
description: A Helm chart for DevOps Test

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 1.0.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
appVersion: 1.0.0
dependencies:
- name: app-gcp
version: 1.3.4
repository: https://harbor.local/chartrepo/library
  • commit to git
  • deploy with ArgoCD
  • check resource from command line
  • map DNS and open vpn or map api gateway to service

Note: next time Developer can create Dockerfile, JCasC config,Application in ArgoCD by themselves

— — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Credit : TrueDigitalGroup

— — — — — — — — — — — — — — — — — — — — — — — — — — — — —

--

--

Dounpct
Dounpct

Written by Dounpct

I work for TrueDigitalGroup in DevOps x Automation Team

No responses yet