├── .gitignore ├── README.md ├── Templates ├── Build │ ├── build.yml │ ├── docker-build-scan-push.yml │ ├── golang │ │ └── build-go.yml │ ├── helm-lint.yml │ ├── java │ │ └── build-maven.yml │ ├── nodejs │ │ └── build-npm.yml │ └── publish-helm-chart.yml ├── Common │ └── helm-init.yml ├── Deploy │ ├── deploy-fabrikate.yml │ ├── deploy-gitops.yml │ ├── deploy-helm-native.yml │ ├── deploy-to-environment.yml │ └── deploy-weblogic.yml ├── Scan │ ├── code-scan.yml │ ├── cred-scan.yml │ └── vulnerabilities-scan.yml └── Test │ ├── gatling-functional-tests.yml │ ├── jmeter-performance-tests.yml │ ├── postman-functional-tests.yml │ └── smoke-tests.yml ├── docs ├── demo │ └── nodejs │ │ ├── .azure │ │ ├── ci-azure-pipelines.yml │ │ └── pr-azure-pipelines.yml │ │ ├── .eslintrc │ │ ├── Dockerfile │ │ ├── charts │ │ ├── mydrive-user-java │ │ │ ├── Chart.yaml │ │ │ ├── templates │ │ │ │ ├── NOTES.txt │ │ │ │ ├── _helpers.tpl │ │ │ │ ├── deployment.blue.yaml │ │ │ │ ├── deployment.green.yaml │ │ │ │ ├── ingress.prod.yaml │ │ │ │ ├── ingress.stage.yaml │ │ │ │ ├── service.prod.yaml │ │ │ │ └── service.stage.yaml │ │ │ └── values.yaml │ │ └── mydrive-user │ │ │ ├── Chart.yaml │ │ │ ├── README.md │ │ │ ├── secret.yaml │ │ │ ├── templates │ │ │ ├── NOTES.txt │ │ │ ├── _helpers.tpl │ │ │ ├── deployment.yaml │ │ │ ├── ingress.yaml │ │ │ └── service.yaml │ │ │ └── values.yaml │ │ ├── config │ │ ├── sqlConfig.js │ │ └── swagger.json │ │ ├── data │ │ ├── healthcheck │ │ │ └── user.js │ │ ├── mockgen.js │ │ ├── queries.js │ │ ├── user.js │ │ └── user │ │ │ └── {userID}.js │ │ ├── handlers │ │ ├── healthcheck │ │ │ └── user.js │ │ ├── user.js │ │ └── user │ │ │ └── {userID}.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── readme.md │ │ ├── server.js │ │ ├── swagger │ │ └── swagger.yaml │ │ └── tests │ │ ├── healthcheck │ │ └── user.js │ │ ├── user.js_ │ │ └── user │ │ └── {userID}.js_ └── devops_components.md └── pipeline-template.yml /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | This repository demonstrates how to enable cross-team sharing of Azure DevOps pipelines using templating and parameter injection. 3 | Please read [this]() blog for more information about the concept. 4 | 5 | # Getting Started 6 | 7 | ## Pre-requisites 8 | 1. An Azure DevOps project 9 | 2. Azure Subscription 10 | 3. Azure Container Registry 11 | 4. Azure Kubernetes Services 12 | 13 | ## Working with the code sample 14 | 1. Clone the repo. 15 | 2. Import the pipelines in [the documentation folder](./docs/demo/nodejs/.azure/ci-azure-pipelines.yml) to Azure Pipelines. 16 | 3. Create Environment targeting the AKS cluster. 17 | 4. Create Service Connection for Azure Subscription and Azure Container Registry. 18 | 3. Provide your environment's values to the pipelines parameter. 19 | -------------------------------------------------------------------------------- /Templates/Build/build.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | buildType: '' 3 | runTests: '' 4 | buildGoSettingsFile: '' 5 | buildMavenSettingsFile: '' 6 | verbose: '' 7 | codeRootDir: '' 8 | 9 | steps: 10 | - ${{ if eq(parameters.buildType, 'java') }}: 11 | 12 | - template: java/build-maven.yml 13 | parameters: 14 | buildMavenSettingsFile: ${{parameters.buildMavenSettingsFile}} 15 | runTests: ${{parameters.runTests}} 16 | 17 | 18 | - ${{ if eq(parameters.buildType, 'nodejs') }}: 19 | 20 | - template: nodejs/build-npm.yml 21 | parameters: 22 | runTests: ${{parameters.runTests}} 23 | verbose: ${{parameters.verbose}} 24 | codeRootDir: ${{parameters.codeRootDir}} 25 | 26 | - ${{ if eq(parameters.buildType, 'golang') }}: 27 | 28 | - template: golang/build-go.yml 29 | parameters: 30 | buildGoSettingsFile: ${{parameters.buildGoSettingsFile}} 31 | runTests: ${{parameters.runTests}} -------------------------------------------------------------------------------- /Templates/Build/docker-build-scan-push.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | azureContainerRegistry: '' 3 | imageName: '' 4 | codeRootDir: '' 5 | scan: '' 6 | tag: '' 7 | steps: 8 | 9 | - task: Docker@2 10 | displayName: Login to ACR 11 | inputs: 12 | command: login 13 | containerRegistry: ${{parameters.azureContainerRegistry}} 14 | 15 | - task: Docker@2 16 | displayName: Build and Push 17 | inputs: 18 | command: buildAndPush 19 | containerRegistry: ${{parameters.azureContainerRegistry}} 20 | Dockerfile: ${{parameters.codeRootDir}}/Dockerfile 21 | repository: ${{parameters.imageName}} 22 | tags: ${{parameters.tag}} 23 | 24 | - bash: | 25 | echo docker scan using 3rd party tool 26 | displayName: Docker Scan 3rd Party 27 | condition: eq(${{parameters.scan}}, 'true') -------------------------------------------------------------------------------- /Templates/Build/golang/build-go.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | buildGoSettingsFile: '' 3 | runTests: '' 4 | steps: 5 | - bash: | 6 | echo build golang task with parameter: ${{parameters.buildGoSettingsFile}} 7 | echo testing golang with arg: ${{parameters.runTests}} 8 | displayName: Build and test Golang -------------------------------------------------------------------------------- /Templates/Build/helm-lint.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | helmChartsFolder: '' 3 | gate: '' 4 | steps: 5 | 6 | - task: HelmDeploy@0 7 | displayName: Helm lint 8 | inputs: 9 | connectionType: None 10 | command: lint 11 | arguments: ${{parameters.helmChartsFolder}} 12 | 13 | - task: HelmDeploy@0 14 | displayName: Helm Install Plugin Kubeval 15 | inputs: 16 | connectionType: None 17 | command: plugin 18 | arguments: 'install https://github.com/instrumenta/helm-kubeval' 19 | 20 | - task: HelmDeploy@0 21 | displayName: Helm Run Plugin Kubeval 22 | continueOnError: ${{parameters.gate}} 23 | inputs: 24 | connectionType: None 25 | command: kubeval 26 | arguments: ${{parameters.helmChartsFolder}} --ignore-missing-schemas --strict 27 | 28 | - task: HelmDeploy@0 29 | displayName: Helm Remove Plugin Kubeval 30 | inputs: 31 | connectionType: None 32 | command: plugin 33 | arguments: remove kubeval -------------------------------------------------------------------------------- /Templates/Build/java/build-maven.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | buildMavenSettingsFile: '' 3 | runTests: '' 4 | steps: 5 | - bash: | 6 | echo build maven task with parameter: ${{parameters.buildMavenSettingsFile}} 7 | echo testing maven with arg: ${{parameters.runTests}} 8 | displayName: Build and test Maven -------------------------------------------------------------------------------- /Templates/Build/nodejs/build-npm.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | codeRootDir: '' 3 | verbose: '' 4 | runTests: '' 5 | 6 | steps: 7 | - task: NodeTool@0 8 | displayName: 'Install Node $(nodeVersionSpec)' 9 | inputs: 10 | versionSpec: $(nodeVersionSpec) 11 | 12 | - task: Npm@1 13 | displayName: 'npm install dependencies' 14 | inputs: 15 | command: custom 16 | workingDir: ${{parameters.codeRootDir}} 17 | verbose: ${{parameters.verbose}} 18 | customCommand: "install --loglevel=warn --unsafe-perm" 19 | 20 | - task: Npm@1 21 | displayName: npm lint 22 | inputs: 23 | command: custom 24 | workingDir: ${{parameters.codeRootDir}} 25 | verbose: ${{parameters.verbose}} 26 | customCommand: "run lint" 27 | 28 | - task: Npm@1 29 | displayName: 'npm test' 30 | inputs: 31 | command: custom 32 | workingDir: ${{parameters.codeRootDir}} 33 | verbose: ${{parameters.verbose}} 34 | customCommand: "run test" 35 | condition: and(succeeded(), ne('${{ parameters.runTests }}', 'none')) 36 | 37 | - task: Npm@1 38 | displayName: 'npm cover' 39 | inputs: 40 | command: custom 41 | workingDir: ${{parameters.codeRootDir}} 42 | verbose: ${{parameters.verbose}} 43 | customCommand: "run cover" 44 | condition: and(succeeded(), ne('${{ parameters.runTests }}', 'none')) 45 | 46 | - task: PublishTestResults@2 47 | displayName: publish test results 48 | inputs: 49 | mergeTestResults: true 50 | testResultsFiles: '**/reports/**.xml' 51 | testRunTitle: 'Unit Tests' 52 | condition: and(succeeded(), ne('${{ parameters.runTests }}', 'none')) 53 | 54 | - task: publishCodeCoverageResults@1 55 | displayName: 'publish code coverage' 56 | inputs: 57 | codeCoverageTool: Cobertura 58 | summaryFileLocation: '**/reports/**coverage.xml' 59 | reportDirectory: '**/reports' 60 | condition: and(succeeded(), ne('${{ parameters.runTests }}', 'none')) -------------------------------------------------------------------------------- /Templates/Build/publish-helm-chart.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | chartVersion: '' 3 | ArtifactStagingDirectory: '' 4 | helmChartsFolder: '' 5 | azureContainerRegistryName: '' 6 | azureSubscription: '' 7 | projectName: '' 8 | 9 | steps: 10 | 11 | - task: HelmDeploy@0 12 | displayName: Helm Package 13 | inputs: 14 | connectionType: None 15 | save: false 16 | destination: ${{parameters.ArtifactStagingDirectory}} 17 | chartPath: ${{parameters.helmChartsFolder}} 18 | command: package 19 | arguments: --version ${{parameters.chartVersion}} 20 | 21 | - task: AzureCLI@2 22 | displayName: Push chart to ACR ${{parameters.azureContainerRegistryName}} 23 | inputs: 24 | azureSubscription: ${{parameters.azureSubscription}} 25 | scriptType: bash 26 | scriptLocation: inlineScript 27 | inlineScript: | 28 | az acr helm push -n ${{parameters.azureContainerRegistryName}} ${{parameters.ArtifactStagingDirectory}}/${{parameters.projectName}}-${{parameters.chartVersion}}.tgz -------------------------------------------------------------------------------- /Templates/Common/helm-init.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | azureSubscription: '' 3 | azureContainerRegistryName: '' 4 | connectToAcr: '' 5 | steps: 6 | 7 | - task: HelmInstaller@0 8 | displayName: Install Helm $(helmVersion) 9 | inputs: 10 | helmVersion: $(helmVersion) 11 | checkLatestHelmVersion: false 12 | kubectlVersion: $(kubectlVersion) 13 | checkLatestKubectl: false 14 | 15 | - ${{ if eq(parameters.connectToAcr, 'true') }}: 16 | 17 | - task: AzureCLI@2 18 | displayName: Connect ACR ${{parameters.azureContainerRegistryName}} as Helm repo 19 | inputs: 20 | azureSubscription: ${{parameters.azureSubscription}} 21 | scriptType: bash 22 | scriptLocation: inlineScript 23 | inlineScript: | 24 | helm init --client-only 25 | az configure --defaults acr=${{parameters.azureContainerRegistryName}} 26 | az acr helm repo add 27 | helm repo update 28 | -------------------------------------------------------------------------------- /Templates/Deploy/deploy-fabrikate.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | projectName: '' 3 | steps: 4 | - bash: | 5 | echo deploy ${{parameters.projectName}} using fabrikate 6 | displayName: Deploy with Fabrikate Task -------------------------------------------------------------------------------- /Templates/Deploy/deploy-gitops.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | projectName: '' 3 | steps: 4 | - bash: | 5 | echo deploy ${{parameters.projectName}} using gitops 6 | displayName: Deploy with gitops Task -------------------------------------------------------------------------------- /Templates/Deploy/deploy-helm-native.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | projectName: '' 3 | releaseName: '' 4 | chartVersion: '' 5 | namespace: '' 6 | azureContainerRegistryName: '' 7 | azureSubscription: '' 8 | upgradeParameters: '' 9 | 10 | steps: 11 | - template: ../Common/helm-init.yml 12 | parameters: 13 | azureSubscription: ${{parameters.azureSubscription}} 14 | azureContainerRegistryName: ${{parameters.azureContainerRegistryName}} 15 | connectToAcr: true 16 | 17 | - task: AzureCLI@2 18 | displayName: Deploy the application version ${{parameters.chartVersion}} to namespace 19 | inputs: 20 | azureSubscription: ${{parameters.azureSubscription}} 21 | scriptType: bash 22 | scriptLocation: inlineScript 23 | inlineScript: | 24 | helm upgrade --namespace ${{parameters.namespace}} ${{parameters.upgradeParameters}} --install --force --version ${{parameters.chartVersion}} --wait ${{parameters.releaseName}} ${{parameters.azureContainerRegistryName}}/${{parameters.projectName}} 25 | -------------------------------------------------------------------------------- /Templates/Deploy/deploy-to-environment.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | deployType: '' 3 | projectName: '' 4 | releaseName: '' 5 | chartVersion: '' 6 | namespace: '' 7 | azureContainerRegistryName: '' 8 | azureSubscription: '' 9 | upgradeParameters: '' 10 | deploySteps: [] 11 | 12 | steps: 13 | - task: Kubernetes@1 14 | displayName: login 15 | inputs: 16 | command: login 17 | 18 | - ${{parameters.deploySteps}} 19 | 20 | - ${{ if eq(parameters.deployType, 'helm-native') }}: 21 | 22 | - template: deploy-helm-native.yml 23 | parameters: 24 | projectName: ${{parameters.projectName}} 25 | releaseName: ${{parameters.releaseName}} 26 | chartVersion: ${{parameters.chartVersion}} 27 | namespace: ${{parameters.namespace}} 28 | azureContainerRegistryName: ${{parameters.azureContainerRegistryName}} 29 | azureSubscription: ${{parameters.azureSubscription}} 30 | upgradeParameters: ${{parameters.upgradeParameters}} 31 | 32 | - ${{ if eq(parameters.deployType, 'helm-weblogic') }}: 33 | 34 | - template: deploy-weblogic.yml 35 | parameters: 36 | projectName: ${{parameters.projectName}} 37 | 38 | - ${{ if eq(parameters.deployType, 'gitops') }}: 39 | 40 | - template: deploy-gitops.yml 41 | parameters: 42 | projectName: ${{parameters.projectName}} 43 | 44 | - ${{ if eq(parameters.deployType, 'fabrikate') }}: 45 | 46 | - template: deploy-fabrikate.yml 47 | parameters: 48 | projectName: ${{parameters.projectName}} 49 | -------------------------------------------------------------------------------- /Templates/Deploy/deploy-weblogic.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | projectName: '' 3 | steps: 4 | - bash: | 5 | echo deploy ${{parameters.projectName}} using weblogic operator 6 | displayName: Deploy with Weblogic Task -------------------------------------------------------------------------------- /Templates/Scan/code-scan.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | buildGoSettingsFile: '' 3 | steps: 4 | - bash: | 5 | echo code scan task 6 | displayName: Code Scan Task -------------------------------------------------------------------------------- /Templates/Scan/cred-scan.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | credScan: '' 3 | steps: 4 | - bash: | 5 | echo cred scan task 6 | displayName: Cred scan task -------------------------------------------------------------------------------- /Templates/Scan/vulnerabilities-scan.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | buildGoSettingsFile: '' 3 | steps: 4 | - bash: | 5 | echo vulnerabilities scan task 6 | displayName: Vulnerabilities Scan Task -------------------------------------------------------------------------------- /Templates/Test/gatling-functional-tests.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | scenario: '' 3 | gate: '' 4 | steps: 5 | - bash: | 6 | echo gatling test task 7 | displayName: Gatling Test Task 8 | -------------------------------------------------------------------------------- /Templates/Test/jmeter-performance-tests.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | scenario: '' 3 | gate: '' 4 | steps: 5 | - bash: | 6 | echo jmeter performance test task 7 | displayName: Jmeter Performance Tests task 8 | - bash: | 9 | echo jmeter stress test task 10 | displayName: Jmeter Stress Tests task -------------------------------------------------------------------------------- /Templates/Test/postman-functional-tests.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | scenario: '' 3 | gate: '' 4 | steps: 5 | - bash: | 6 | echo postman test task 7 | displayName: Postman Tests Task -------------------------------------------------------------------------------- /Templates/Test/smoke-tests.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | buildGoSettingsFile: '' 3 | steps: 4 | - bash: | 5 | echo smoke test task 6 | displayName: Smoke Tests Task -------------------------------------------------------------------------------- /docs/demo/nodejs/.azure/ci-azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | 2 | trigger: 3 | branches: 4 | include: 5 | - master 6 | 7 | variables: 8 | helmVersion: '2.14.1' 9 | kubectlVersion: v1.12.1 10 | nodeVersionSpec: 10.x 11 | 12 | stages: 13 | - template: ../../../../pipeline-template.yml 14 | parameters: 15 | linuxAgentPool: 'ubuntu-latest' 16 | windowsAgentPool: 'windows-latest' 17 | buildType: 'nodejs' 18 | projectName: 'mydrive-user' 19 | initSteps: [] 20 | docker: true 21 | helm: true 22 | imageName: 'mydrive-user' 23 | helmCharts: charts 24 | codeRootDir: docs/demo/nodejs 25 | deployType: 'helm-native' 26 | PR: { 27 | enabled: false, 28 | gate: true, 29 | credscan: true, 30 | codescan: true, 31 | vulnerabilitiesscan: true 32 | } 33 | CI: { 34 | enabled: true, 35 | azureContainerRegistry: 'artifacts-repo', 36 | azureSubscription: 'azure-service-connection', 37 | azureContainerRegistryName: 'orgsharedregistry', 38 | scan: true, 39 | gate: true, 40 | } 41 | AUTOMATION: { 42 | enabled: true, 43 | azureSubscription: 'azure-service-connection', 44 | azureContainerRegistry: 'artifacts-repo', 45 | azureContainerRegistryName: 'orgsharedregistry', 46 | environmentNamespace: 'Automation.automation', 47 | namespace: 'automation', 48 | upgradeParameters: '--set repository=orgsharedregistry.azurecr.io/mydrive-user --set tag=$(Build.BuildId) --set name=mydrive-user', 49 | gate: true 50 | } 51 | FUNCTIONAL: { 52 | enabled: true, 53 | azureSubscription: 'azure-service-connection', 54 | azureContainerRegistry: 'artifacts-repo', 55 | azureContainerRegistryName: 'orgsharedregistry', 56 | environmentNamespace: 'Functional.functional', 57 | namespace: 'functional', 58 | upgradeParameters: '--set repository=orgsharedregistry.azurecr.io/mydrive-user --set tag=$(Build.BuildId) --set name=mydrive-user', 59 | testType: 'galing', 60 | gatlingScenario: '', 61 | gate: true, 62 | } 63 | NONFUNCTIONAL: { 64 | enabled: true, 65 | azureSubscription: 'azure-service-connection', 66 | azureContainerRegistry: 'artifacts-repo', 67 | azureContainerRegistryName: 'orgsharedregistry', 68 | environmentNamespace: 'NonFunctional.nonfunctional', 69 | namespace: 'nonfunctional', 70 | upgradeParameters: '--set repository=orgsharedregistry.azurecr.io/mydrive-user --set tag=$(Build.BuildId) --set name=mydrive-user', 71 | gate: true 72 | } -------------------------------------------------------------------------------- /docs/demo/nodejs/.azure/pr-azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | 2 | pr: 3 | branches: 4 | include: 5 | - master 6 | 7 | variables: 8 | helmVersion: '2.14.1' 9 | kubectlVersion: v1.12.1 10 | nodeVersionSpec: 10.x 11 | 12 | stages: 13 | - template: ../../../../pipeline-template.yml 14 | parameters: 15 | 16 | linuxAgentPool: 'ubuntu-latest' 17 | windowsAgentPool: 'windows-latest' 18 | buildType: 'nodejs' 19 | projectName: 'mydrive-user' 20 | initSteps: [] 21 | docker: true 22 | helm: true 23 | imageName: 'mydrive-user' 24 | helmCharts: charts 25 | codeRootDir: docs/demo/nodejs 26 | deployType: 'helm-native' 27 | PR: { 28 | enabled: true, 29 | gate: true, 30 | credscan: true, 31 | codescan: true, 32 | vulnerabilitiesscan: true 33 | } 34 | CI: { 35 | enabled: false 36 | } 37 | AUTOMATION: { 38 | enabled: false 39 | } 40 | FUNCTIONAL: { 41 | enabled: false 42 | } 43 | NONFUNCTIONAL: { 44 | enabled: false 45 | } -------------------------------------------------------------------------------- /docs/demo/nodejs/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 2017 4 | }, 5 | 6 | "env": { 7 | "es6": true 8 | } 9 | } -------------------------------------------------------------------------------- /docs/demo/nodejs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8-alpine 2 | 3 | COPY . /app 4 | 5 | WORKDIR /app 6 | 7 | EXPOSE 8080 8 | 9 | RUN npm install 10 | 11 | CMD npm start -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user-java/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A chart for deploying the My Driving User API 3 | name: mydrive-user-java 4 | version: 1.0.0 -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user-java/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | The following two commands enables accessing the api via the 1st pod in the deployment at http://localhost:{{ .Values.image.containerPort }} 2 | 3 | export API_USER_POD_NAME=$(kubectl get pods -l app={{ .Values.image.label }} -o jsonpath="{.items[0].metadata.name}") 4 | kubectl port-forward $API_USER_POD_NAME {{ .Values.image.containerPort }}:{{ .Values.image.containerPort }} -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user-java/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "chart.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | */}} 13 | {{- define "chart.fullname" -}} 14 | {{- $name := default .Chart.Name .Values.nameOverride -}} 15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 16 | {{- end -}} 17 | -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user-java/templates/deployment.blue.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.blue.enabled }} 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: "{{ .Values.image.name }}-deploy-blue" 6 | labels: 7 | deploy: {{ .Values.image.label }} 8 | spec: 9 | replicas: {{ .Values.replicaCount }} 10 | selector: 11 | matchLabels: 12 | app: {{ .Values.image.label }} 13 | template: 14 | metadata: 15 | labels: 16 | app: {{ .Values.image.label }} 17 | slot: blue 18 | spec: 19 | containers: 20 | - image: "{{ .Values.repository.image }}:{{ .Values.blue.tag }}" 21 | imagePullPolicy: {{ .Values.repository.pullPolicy }} 22 | name: {{ .Values.image.name }} 23 | livenessProbe: 24 | httpGet: 25 | path: /api/healthcheck/user-java 26 | port: {{ .Values.image.containerPort }} 27 | httpHeaders: 28 | - name: Accept 29 | value: application/json 30 | initialDelaySeconds: 10 31 | periodSeconds: 30 32 | readinessProbe: 33 | httpGet: 34 | path: /api/healthcheck/user-java 35 | port: {{ .Values.image.containerPort }} 36 | httpHeaders: 37 | - name: Accept 38 | value: application/json 39 | initialDelaySeconds: 5 40 | periodSeconds: 5 41 | ports: 42 | - containerPort: {{ .Values.image.containerPort }} 43 | name: http 44 | protocol: TCP 45 | - containerPort: 443 46 | name: https 47 | protocol: TCP 48 | env: 49 | - name: SQL_USER 50 | valueFrom: 51 | secretKeyRef: 52 | name: sql 53 | key: sql_user 54 | - name: SQL_PASSWORD 55 | valueFrom: 56 | secretKeyRef: 57 | name: sql 58 | key: sql_password 59 | - name: SQL_SERVER 60 | valueFrom: 61 | secretKeyRef: 62 | name: sql 63 | key: sql_server 64 | - name: SQL_DBNAME 65 | valueFrom: 66 | secretKeyRef: 67 | name: sql 68 | key: sql_dbname 69 | {{ end }} 70 | -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user-java/templates/deployment.green.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.green.enabled }} 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: "{{ .Values.image.name }}-deploy-green" 6 | labels: 7 | deploy: {{ .Values.image.label }} 8 | spec: 9 | replicas: {{ .Values.replicaCount }} 10 | selector: 11 | matchLabels: 12 | app: {{ .Values.image.label }} 13 | template: 14 | metadata: 15 | labels: 16 | app: {{ .Values.image.label }} 17 | slot: green 18 | spec: 19 | containers: 20 | - image: "{{ .Values.repository.image }}:{{ .Values.green.tag }}" 21 | imagePullPolicy: {{ .Values.repository.pullPolicy }} 22 | name: {{ .Values.image.name }} 23 | livenessProbe: 24 | httpGet: 25 | path: /api/healthcheck/user-java 26 | port: {{ .Values.image.containerPort }} 27 | httpHeaders: 28 | - name: Accept 29 | value: application/json 30 | initialDelaySeconds: 10 31 | periodSeconds: 30 32 | readinessProbe: 33 | httpGet: 34 | path: /api/healthcheck/user-java 35 | port: {{ .Values.image.containerPort }} 36 | httpHeaders: 37 | - name: Accept 38 | value: application/json 39 | initialDelaySeconds: 5 40 | periodSeconds: 5 41 | ports: 42 | - containerPort: {{ .Values.image.containerPort }} 43 | name: http 44 | protocol: TCP 45 | - containerPort: 443 46 | name: https 47 | protocol: TCP 48 | env: 49 | - name: SQL_USER 50 | valueFrom: 51 | secretKeyRef: 52 | name: sql 53 | key: sql_user 54 | - name: SQL_PASSWORD 55 | valueFrom: 56 | secretKeyRef: 57 | name: sql 58 | key: sql_password 59 | - name: SQL_SERVER 60 | valueFrom: 61 | secretKeyRef: 62 | name: sql 63 | key: sql_server 64 | - name: SQL_DBNAME 65 | valueFrom: 66 | secretKeyRef: 67 | name: sql 68 | key: sql_dbname 69 | {{ end }} -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user-java/templates/ingress.prod.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $serviceName := include "chart.fullname" . -}} 3 | {{- $servicePort := .Values.service.externalPort -}} 4 | {{- $path := .Values.ingress.path -}} 5 | apiVersion: extensions/v1beta1 6 | kind: Ingress 7 | metadata: 8 | name: {{ template "chart.fullname" . }}-prod 9 | labels: 10 | app: {{ template "chart.name" . }} 11 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 12 | release: {{ .Release.Name }} 13 | heritage: {{ .Release.Service }} 14 | annotations: 15 | {{- range $key, $value := .Values.ingress.annotations }} 16 | {{ $key }}: {{ $value | quote }} 17 | {{- end }} 18 | spec: 19 | rules: 20 | {{- range $key, $value := .Values.ingress.rules }} 21 | - host: {{ $value.host }} 22 | http: 23 | paths: 24 | {{- range $path := .paths }} 25 | - path: {{ $path.path }} 26 | backend: 27 | serviceName: {{ $path.serviceName }}-prod 28 | servicePort: {{ $path.servicePort }} 29 | {{- end -}} 30 | {{- end -}} 31 | {{- if .Values.ingress.tls }} 32 | tls: 33 | {{ toYaml .Values.ingress.tls | indent 4 }} 34 | {{- end -}} 35 | {{- end -}} -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user-java/templates/ingress.stage.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $serviceName := include "chart.fullname" . -}} 3 | {{- $servicePort := .Values.service.externalPort -}} 4 | {{- $path := .Values.ingress.path -}} 5 | apiVersion: extensions/v1beta1 6 | kind: Ingress 7 | metadata: 8 | name: {{ template "chart.fullname" . }}-stage 9 | labels: 10 | app: {{ template "chart.name" . }} 11 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 12 | release: {{ .Release.Name }} 13 | heritage: {{ .Release.Service }} 14 | annotations: 15 | {{- range $key, $value := .Values.ingress.annotations }} 16 | {{ $key }}: {{ $value | quote }} 17 | {{- end }} 18 | spec: 19 | rules: 20 | {{- range $key, $value := .Values.ingress.rules }} 21 | - host: stage{{ $value.host }} 22 | http: 23 | paths: 24 | {{- range $path := .paths }} 25 | - path: {{ $path.path }} 26 | backend: 27 | serviceName: {{ $path.serviceName }}-stage 28 | servicePort: {{ $path.servicePort }} 29 | {{- end -}} 30 | {{- end -}} 31 | {{- if .Values.ingress.tls }} 32 | tls: 33 | {{ toYaml .Values.ingress.tls | indent 4 }} 34 | {{- end -}} 35 | {{- end -}} -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user-java/templates/service.prod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: 'api-user-java-svc-prod' 5 | spec: 6 | type: ClusterIP 7 | selector: 8 | app: {{ .Values.image.label }} 9 | slot: {{ .Values.productionSlot }} 10 | ports: 11 | - protocol: TCP 12 | name: {{ .Values.image.name }}http 13 | port: {{ .Values.service.externalPort }} 14 | targetPort: {{ .Values.image.containerPort }} 15 | - protocol: TCP 16 | name: {{ .Values.image.name }}https 17 | port: 443 18 | targetPort: 443 19 | -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user-java/templates/service.stage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: 'api-user-java-svc-stage' 5 | spec: 6 | type: ClusterIP 7 | selector: 8 | app: {{ .Values.image.label }} 9 | {{- if eq .Values.productionSlot "blue" }} 10 | slot: green 11 | {{- else }} 12 | slot: blue 13 | {{- end }} 14 | ports: 15 | - protocol: TCP 16 | name: {{ .Values.image.name }}http 17 | port: {{ .Values.service.externalPort }} 18 | targetPort: {{ .Values.image.containerPort }} 19 | - protocol: TCP 20 | name: {{ .Values.image.name }}https 21 | port: 443 22 | targetPort: 443 23 | -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user-java/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for helmv2. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | productionSlot: blue 5 | blue: 6 | enabled: true 7 | tag: latest 8 | 9 | green: 10 | enabled: false 11 | tag: latest 12 | 13 | replicaCount: 2 14 | repository: 15 | # Fully qualified path to image in ACR (Ex. youracr.azureacr.io/devopsoh/poi-build). 16 | # See deployment scripts in https://github.com/Azure-Samples/openhack-devops-proctor for example usage. 17 | image: 18 | tag: latest 19 | pullPolicy: Always 20 | image: 21 | name: user-java 22 | label: user-java 23 | containerPort: 8080 24 | service: 25 | externalPort: 80 26 | ingress: 27 | enabled: true 28 | annotations: 29 | kubernetes.io/ingress.class: traefik 30 | rules: 31 | endpoint: 32 | #fqdn of your exposed application (found in ohteamvalues file) 33 | host: 34 | paths: 35 | - path: /api/user-java 36 | serviceName: api-user-java-svc 37 | servicePort: 80 38 | - path: /api/healthcheck/user-java 39 | serviceName: api-user-java-svc 40 | servicePort: 80 41 | - path: /api/documentation/user-java 42 | serviceName: api-user-java-svc 43 | servicePort: 80 44 | - path: /api/docs/user-java 45 | serviceName: api-user-java-svc 46 | servicePort: 80 47 | - path: /api/swagger-resources 48 | serviceName: api-user-java-svc 49 | servicePort: 80 50 | - path: /api/api-docs 51 | serviceName: api-user-java-svc 52 | servicePort: 80 53 | 54 | tls: [] 55 | # - secretName: chart-example-tls 56 | # hosts: 57 | # - chart-example.local 58 | 59 | resources: {} 60 | # We usually recommend not to specify default resources and to leave this as a conscious 61 | # choice for the user. This also increases chances charts run on environments with little 62 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 63 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 64 | # limits: 65 | # cpu: 100m 66 | # memory: 128Mi 67 | # requests: 68 | # cpu: 100m 69 | # memory: 128Mi 70 | 71 | nodeSelector: {} 72 | 73 | tolerations: [] 74 | 75 | affinity: {} -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A chart for deploying the My Driving User API 3 | name: mydrive-user 4 | version: 1.0.0 5 | -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user/README.md: -------------------------------------------------------------------------------- 1 | # My Driving User API 2 | 3 | ## Description 4 | 5 | This installs the application on a Kubernetes cluster with the following components: 6 | 7 | 1. A Service to allow public ingress on Port 80 8 | 2. Creates a deployment that manages 2 pods (or more!) 9 | 10 | ## Installation instructions 11 | 12 | 1. Install Kubernetes Helm (any method is fine!) https://github.com/kubernetes/helm/blob/master/docs/install.md 13 | 14 | 2. Make sure tiller (the server side component to Helm) is up to date: 15 | ```helm init --upgrade``` 16 | 17 | 3. Make any changes you'd like to the `charts/mydrive-user/values.yaml` fil. If you change the image tag, make sure the image exists on ACR or docker hub. 18 | 19 | 4. Run the following commands in a terminal window in the `charts/mydrive-user/` folder. 20 | 21 | ```bash 22 | 23 | helm install . -f values.yaml 24 | 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: sql 5 | type: Opaque 6 | data: 7 | sql_user: 8 | sql_password: 9 | sql_server: 10 | sql_dbname: 11 | -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user/templates/NOTES.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balteravishay/SharedDevOpsComponents/b73326f18561d32276c0eeb9f1cb0ab978faa81b/docs/demo/nodejs/charts/mydrive-user/templates/NOTES.txt -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "chart.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | */}} 13 | {{- define "chart.fullname" -}} 14 | {{- $name := default .Chart.Name .Values.nameOverride -}} 15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 16 | {{- end -}} 17 | -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ .Values.name }} 5 | spec: 6 | replicas: 2 7 | selector: 8 | matchLabels: 9 | app: {{ .Values.name }} 10 | template: 11 | metadata: 12 | labels: 13 | app: {{ .Values.name }} 14 | run: {{ .Values.name }} 15 | spec: 16 | containers: 17 | - name: {{ .Values.name }} 18 | image: "{{ .Values.repository }}:{{ .Values.tag }}" 19 | ports: 20 | - containerPort: 3000 -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: {{ .Values.name }}-ingress 5 | annotations: 6 | kubernetes.io/ingress.class: nginx 7 | nginx.ingress.kubernetes.io/ssl-redirect: "false" 8 | spec: 9 | rules: 10 | - host: {{ .Values.host }} 11 | http: 12 | paths: 13 | - path: / 14 | backend: 15 | serviceName: {{ .Values.name }}-svc 16 | servicePort: 80 17 | -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ .Values.name }}-svc 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: 3000 9 | protocol: TCP 10 | name: http 11 | selector: 12 | app: {{ .Values.name }} 13 | run: {{ .Values.name }} -------------------------------------------------------------------------------- /docs/demo/nodejs/charts/mydrive-user/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for chart. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | replicaCount: 2 5 | name: nginx 6 | repository: nginx 7 | tag: __DOCKER_TAG__ -------------------------------------------------------------------------------- /docs/demo/nodejs/config/sqlConfig.js: -------------------------------------------------------------------------------- 1 | const sqlConfig = { 2 | userName: process.env.SQL_USER || '', 3 | password: process.env.SQL_PASSWORD || '', 4 | server: process.env.SQL_SERVER || '', // You can use 'localhost\\instance' to connect to named instance 5 | options: { 6 | encrypt: true, // Use this if you're on Windows Azure 7 | database: process.env.SQL_DBNAME || 'mydrivingdb', 8 | MultipleActiveResultSets: false, 9 | TrustServerCertificate: false, 10 | rowCollectionOnDone: true 11 | // Persist Security Info=False;Connection Timeout=30 12 | } 13 | }; 14 | 15 | exports = module.exports = sqlConfig; 16 | -------------------------------------------------------------------------------- /docs/demo/nodejs/config/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "description": "API for the user profile in the My Driving example app. https://github.com/Azure-Samples/openhack-devops", 5 | "version": "0.1.0", 6 | "title": "My Driving User Profile API" 7 | }, 8 | "basePath": "/api", 9 | "schemes": [ 10 | "http" 11 | ], 12 | "consumes": [ 13 | "application/json" 14 | ], 15 | "produces": [ 16 | "application/json" 17 | ], 18 | "paths": { 19 | "/healthcheck/user": { 20 | "x-swagger-router-controller": "healthcheck", 21 | "get": { 22 | "description": "Returns healthcheck for systems looking to ensure API is up and operational", 23 | "responses": { 24 | "200": { 25 | "description": "Service is healthy", 26 | "schema": { 27 | "$ref": "#/definitions/Healthcheck" 28 | } 29 | }, 30 | "default": { 31 | "description": "An error occurred", 32 | "schema": { 33 | "$ref": "#/definitions/error_response_default" 34 | } 35 | } 36 | } 37 | } 38 | }, 39 | "/user": { 40 | "get": { 41 | "description": "List all user profiles", 42 | "operationId": "getAllUsers", 43 | "parameters": [], 44 | "responses": { 45 | "200": { 46 | "description": "List of all users", 47 | "schema": { 48 | "type": "array", 49 | "items": { 50 | "$ref": "#/definitions/Profile" 51 | } 52 | } 53 | }, 54 | "default": { 55 | "description": "An error occurred", 56 | "schema": { 57 | "$ref": "#/definitions/inline_response_default" 58 | } 59 | } 60 | }, 61 | "x-swagger-router-controller": "user" 62 | } 63 | }, 64 | "/user/{userID}": { 65 | "parameters": [ 66 | { 67 | "name": "userID", 68 | "in": "path", 69 | "description": "User's unique ID", 70 | "type": "string", 71 | "required": true 72 | } 73 | ], 74 | "get": { 75 | "description": "Get a User Profile by ID", 76 | "operationId": "userGET", 77 | "responses": { 78 | "200": { 79 | "description": "List of profiles", 80 | "schema": { 81 | "type": "array", 82 | "items": { 83 | "$ref": "#/definitions/Profile" 84 | } 85 | } 86 | }, 87 | "default": { 88 | "description": "An error occurred", 89 | "schema": { 90 | "$ref": "#/definitions/inline_response_default" 91 | } 92 | } 93 | }, 94 | "x-swagger-router-controller": "user" 95 | }, 96 | "post": { 97 | "description": "Declares and creates a new profile", 98 | "operationId": "userPOST", 99 | "parameters": [ 100 | { 101 | "in": "body", 102 | "name": "_profile", 103 | "description": "Details of the profile", 104 | "required": true, 105 | "schema": { 106 | "$ref": "#/definitions/Profile" 107 | } 108 | } 109 | ], 110 | "responses": { 111 | "201": { 112 | "description": "Creation successful", 113 | "schema": { 114 | "$ref": "#/definitions/Profile" 115 | }, 116 | "headers": { 117 | "location": { 118 | "type": "string" 119 | } 120 | } 121 | }, 122 | "default": { 123 | "description": "An error occurred", 124 | "schema": { 125 | "$ref": "#/definitions/inline_response_default" 126 | } 127 | } 128 | }, 129 | "x-swagger-router-controller": "user" 130 | }, 131 | "patch": { 132 | "description": "Update User", 133 | "operationId": "updateUser", 134 | "responses": { 135 | "200": { 136 | "description": "User Updated", 137 | "schema": { 138 | "$ref": "#/definitions/Profile" 139 | } 140 | }, 141 | "404": { 142 | "description": "User profile not found" 143 | }, 144 | "default": { 145 | "description": "Unknown Error", 146 | "schema": { 147 | "$ref": "#/definitions/error_response_default" 148 | } 149 | } 150 | } 151 | }, 152 | "delete": { 153 | "description": "Delete User By ID", 154 | "operationId": "userDELETE", 155 | "responses": { 156 | "204": { 157 | "description": "User Deleted" 158 | }, 159 | "404": { 160 | "description": "User not found" 161 | }, 162 | "default": { 163 | "description": "Unknown Error", 164 | "schema": { 165 | "$ref": "#/definitions/error_response_default" 166 | } 167 | } 168 | } 169 | } 170 | } 171 | }, 172 | "definitions": { 173 | "Healthcheck": { 174 | "type": "object", 175 | "properties": { 176 | "message": { 177 | "type": "string", 178 | "description": "" 179 | }, 180 | "status": { 181 | "type": "string", 182 | "description": "" 183 | } 184 | } 185 | }, 186 | "Profile": { 187 | "type": "object", 188 | "properties": { 189 | "Id": { 190 | "type": "string", 191 | "description": "User's unique identity" 192 | }, 193 | "FirstName": { 194 | "type": "string", 195 | "minLength": 0, 196 | "maxLength": 50, 197 | "pattern": "^[A-Za-z \u0000-][a-zA-Z \u0000-]*$" 198 | }, 199 | "LastName": { 200 | "type": "string", 201 | "minLength": 0, 202 | "maxLength": 80, 203 | "pattern": "^[A-Za-z \u0000-][a-zA-Z \u0000-]*$" 204 | }, 205 | "UserId": { 206 | "type": "string", 207 | "description": "User's identity" 208 | }, 209 | "ProfilePictureUri": { 210 | "type": "string", 211 | "format": "binary", 212 | "description": "User's Profile picture" 213 | }, 214 | "Rating": { 215 | "type": "integer", 216 | "description": "User's rating" 217 | }, 218 | "Ranking": { 219 | "type": "integer", 220 | "description": "User's ranking" 221 | }, 222 | "TotalDistance": { 223 | "type": "number", 224 | "format": "float", 225 | "description": "User's total distance traveled" 226 | }, 227 | "TotalTrips": { 228 | "type": "integer", 229 | "format": "long", 230 | "description": "User's total number of trips" 231 | }, 232 | "TotalTime": { 233 | "type": "integer", 234 | "format": "long", 235 | "description": "User's total driving time" 236 | }, 237 | "HardStops": { 238 | "type": "integer", 239 | "format": "long", 240 | "description": "User's total number of hard stops" 241 | }, 242 | "HardAccelerations": { 243 | "type": "integer", 244 | "format": "long", 245 | "description": "User's total number of hard accelerations" 246 | }, 247 | "FuelConsumption": { 248 | "type": "number", 249 | "format": "float", 250 | "description": "User's amount of fuel consumed" 251 | }, 252 | "MaxSpeed": { 253 | "type": "number", 254 | "format": "float", 255 | "description": "User's maximum speed" 256 | }, 257 | "CreatedAt": { 258 | "type": "string", 259 | "format": "date" 260 | }, 261 | "UpdatedAt": { 262 | "type": "string", 263 | "format": "date" 264 | }, 265 | "Deleted": { 266 | "type": "boolean", 267 | "description": "Whether the user has been deleted or not." 268 | } 269 | } 270 | }, 271 | "error_response_default": { 272 | "type": "object", 273 | "properties": { 274 | "status": { 275 | "description": "Error code (if available)", 276 | "type": "integer", 277 | "format": "int32" 278 | }, 279 | "message": { 280 | "description": "Error Message", 281 | "type": "string" 282 | } 283 | } 284 | }, 285 | "inline_response_default": { 286 | "required": [ 287 | "message", 288 | "status" 289 | ], 290 | "properties": { 291 | "status": { 292 | "type": "integer", 293 | "format": "int32" 294 | }, 295 | "message": { 296 | "type": "string" 297 | } 298 | } 299 | } 300 | } 301 | } -------------------------------------------------------------------------------- /docs/demo/nodejs/data/healthcheck/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Mockgen = require('../mockgen.js'); 3 | /** 4 | * Operations on /healthcheck/user 5 | */ 6 | module.exports = { 7 | /** 8 | * summary: 9 | * description: Returns healthcheck for systems looking to ensure API is up and operational 10 | * parameters: 11 | * produces: 12 | * responses: 200, default 13 | * operationId: 14 | */ 15 | get: { 16 | 200: function (req, res, callback) { 17 | res.json({ 18 | message: 'healthcheck', 19 | status: 'healthy' 20 | }); 21 | callback; 22 | }, 23 | default: function (req, res, callback) { 24 | /** 25 | * Using mock data generator module. 26 | * Replace this by actual data for the api. 27 | */ 28 | Mockgen().responses({ 29 | path: '/healthcheck/user', 30 | operation: 'get', 31 | response: 'default' 32 | }, callback); 33 | } 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /docs/demo/nodejs/data/mockgen.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Swagmock = require('swagmock'); 3 | var Path = require('path'); 4 | var apiPath = Path.resolve(__dirname, '../config/swagger.json'); 5 | var mockgen; 6 | 7 | module.exports = function () { 8 | /** 9 | * Cached mock generator 10 | */ 11 | mockgen = mockgen || Swagmock(apiPath); 12 | return mockgen; 13 | }; 14 | -------------------------------------------------------------------------------- /docs/demo/nodejs/data/queries.js: -------------------------------------------------------------------------------- 1 | exports.INSERT_USER_PROFILE = 2 | `INSERT INTO userprofiles \ 3 | (\ 4 | Id,\ 5 | FirstName,\ 6 | LastName,\ 7 | UserId,\ 8 | ProfilePictureUri,\ 9 | Rating,\ 10 | Ranking,\ 11 | TotalDistance,\ 12 | TotalTrips,\ 13 | TotalTime,\ 14 | HardStops,\ 15 | HardAccelerations,\ 16 | FuelConsumption,\ 17 | MaxSpeed,\ 18 | CreatedAt,\ 19 | UpdatedAt,\ 20 | Deleted\ 21 | ) \ 22 | SELECT \ 23 | Id,\ 24 | FirstName,\ 25 | LastName,\ 26 | UserId,\ 27 | ProfilePictureUri,\ 28 | Rating,\ 29 | Ranking,\ 30 | TotalDistance,\ 31 | TotalTrips,\ 32 | TotalTime,\ 33 | HardStops,\ 34 | HardAccelerations,\ 35 | FuelConsumption,\ 36 | MaxSpeed,\ 37 | GETDATE(),\ 38 | GETDATE(),\ 39 | Deleted \ 40 | FROM OPENJSON(@UserProfileJson) \ 41 | WITH ( 42 | Id nvarchar(128),\ 43 | FirstName nvarchar(max),\ 44 | LastName nvarchar(max),\ 45 | UserId nvarchar(max),\ 46 | ProfilePictureUri nvarchar(max),\ 47 | Rating int,\ 48 | Ranking int,\ 49 | TotalDistance float(53),\ 50 | TotalTrips bigint,\ 51 | TotalTime bigint,\ 52 | HardStops bigint,\ 53 | HardAccelerations bigint,\ 54 | FuelConsumption float(53),\ 55 | MaxSpeed float(53),\ 56 | Deleted bit\ 57 | ) AS JSON`; 58 | 59 | exports.SELECT_USER_PROFILE_BY_ID= 60 | 'select * from userprofiles where id = @user_profile_id FOR JSON PATH'; 61 | 62 | exports.SELECT_USER_PROFILES= 63 | 'select * FROM userprofiles FOR JSON PATH'; 64 | 65 | exports.DELETE_USER_PROFILE= 66 | 'UPDATE userprofiles SET Deleted = 1 WHERE id = @user_profile_id'; -------------------------------------------------------------------------------- /docs/demo/nodejs/data/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Mockgen = require('./mockgen.js'); 3 | var queries = require('./queries'); 4 | /** 5 | * Operations on /user 6 | */ 7 | module.exports = { 8 | /** 9 | * summary: 10 | * description: List all user profiles 11 | * parameters: 12 | * produces: 13 | * responses: 200, default 14 | * operationId: getAllUsers 15 | */ 16 | get: { 17 | 200: function (req, res, callback) { 18 | 19 | req.sql(queries.SELECT_USER_PROFILES) 20 | .into(res); 21 | callback; 22 | 23 | }, 24 | default: function (req, res, callback) { 25 | 26 | Mockgen().responses({ 27 | path: '/user', 28 | operation: 'get', 29 | response: 'default' 30 | }, callback); 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /docs/demo/nodejs/data/user/{userID}.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Mockgen = require('../mockgen.js'); 3 | var TYPES = require('tedious').TYPES; 4 | var queries = require('../queries'); 5 | /** 6 | * Operations on /user/{userID} 7 | */ 8 | module.exports = { 9 | /** 10 | * summary: 11 | * description: Get a User Profile by ID 12 | * parameters: 13 | * produces: 14 | * responses: 200, default 15 | * operationId: userGET 16 | */ 17 | get: { 18 | 200: function (req, res, callback) { 19 | 20 | req.sql(queries.SELECT_USER_PROFILE_BY_ID) 21 | .param('user_profile_id', req.params.userID, TYPES.NVarChar) 22 | .into(res, '{}'); 23 | callback; 24 | 25 | }, 26 | default: function (req, res, callback) { 27 | /** 28 | * Using mock data generator module. 29 | * Replace this by factual data for the api. 30 | */ 31 | Mockgen().responses({ 32 | path: '/user/{userID}', 33 | operation: 'get', 34 | response: 'default' 35 | }, callback); 36 | } 37 | }, 38 | /** 39 | * summary: 40 | * description: Declares and creates a new profile 41 | * parameters: _profile 42 | * produces: 43 | * responses: 201, default 44 | * operationId: userPOST 45 | */ 46 | post: { 47 | 201: function (req, res, callback) { 48 | 49 | req.sql(queries.INSERT_USER_PROFILE) 50 | .param('UserProfileJson', req.body, TYPES.NVarChar) 51 | .exec(res); 52 | callback; 53 | 54 | }, 55 | default: function (req, res, callback) { 56 | /** 57 | * Using mock data generator module. 58 | * Replace this by actual data for the api. 59 | */ 60 | Mockgen().responses({ 61 | path: '/user/{userID}', 62 | operation: 'post', 63 | response: 'default' 64 | }, callback); 65 | } 66 | }, 67 | /** 68 | * summary: 69 | * description: Update User 70 | * parameters: 71 | * produces: 72 | * responses: 200, 404, default 73 | * operationId: updateUser 74 | */ 75 | patch: { 76 | 200: function (req, res, callback) { 77 | 78 | req.sql('EXEC UpdateProductFromJson @id, @json') 79 | .param('json', req.body, TYPES.NVarChar) 80 | .param('id', req.params.id, TYPES.Int) 81 | .exec(res); 82 | callback; 83 | 84 | }, 85 | 404: function (req, res, callback) { 86 | /** 87 | * Using mock data generator module. 88 | * Replace this by actual data for the api. 89 | */ 90 | Mockgen().responses({ 91 | path: '/user/{userID}', 92 | operation: 'patch', 93 | response: '404' 94 | }, callback); 95 | }, 96 | default: function (req, res, callback) { 97 | /** 98 | * Using mock data generator module. 99 | * Replace this by actual data for the api. 100 | */ 101 | Mockgen().responses({ 102 | path: '/user/{userID}', 103 | operation: 'patch', 104 | response: 'default' 105 | }, callback); 106 | } 107 | }, 108 | /** 109 | * summary: 110 | * description: Delete User By ID 111 | * parameters: 112 | * produces: 113 | * responses: 204, 404, default 114 | * operationId: userDELETE 115 | */ 116 | delete: { 117 | 204: function (req, res, callback) { 118 | var tempmessage = ''; 119 | var resmessage = tempmessage.concat('User profile ',req.params.userID,' deleted'); 120 | req.sql(queries.DELETE_USER_PROFILE) 121 | .param('user_profile_id', req.params.userID, TYPES.NVarChar) 122 | .into(res, resmessage); 123 | callback; 124 | 125 | }, 126 | 404: function (req, res, callback) { 127 | /** 128 | * Using mock data generator module. 129 | * Replace this by actual data for the api. 130 | */ 131 | Mockgen().responses({ 132 | path: '/user/{userID}', 133 | operation: 'delete', 134 | response: '404' 135 | }, callback); 136 | }, 137 | default: function (req, res, callback) { 138 | /** 139 | * Using mock data generator module. 140 | * Replace this by actual data for the api. 141 | */ 142 | Mockgen().responses({ 143 | path: '/user/{userID}', 144 | operation: 'delete', 145 | response: 'default' 146 | }, callback); 147 | } 148 | } 149 | }; 150 | -------------------------------------------------------------------------------- /docs/demo/nodejs/handlers/healthcheck/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var dataProvider = require('../../data/healthcheck/user.js'); 3 | /** 4 | * Operations on /healthcheck/user 5 | */ 6 | module.exports = { 7 | /** 8 | * summary: 9 | * description: Returns healthcheck for systems looking to ensure API is up and operational 10 | * parameters: 11 | * produces: 12 | * responses: 200, default 13 | */ 14 | get: function (req, res, next) { 15 | /** 16 | * Get the data for response 200 17 | * For response `default` status 200 is used. 18 | */ 19 | var status = 200; 20 | var provider = dataProvider['get']['200']; 21 | provider(req, res, function (err, data) { 22 | if (err) { 23 | next(err); 24 | return; 25 | } 26 | res.status(status).send(data && data.responses); 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /docs/demo/nodejs/handlers/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var dataProvider = require('../data/user.js'); 3 | /** 4 | * Operations on /user 5 | */ 6 | module.exports = { 7 | /** 8 | * summary: 9 | * description: List all user profiles 10 | * parameters: 11 | * produces: 12 | * responses: 200, default 13 | */ 14 | get: function getAllUsers(req, res, next) { 15 | /** 16 | * Get the data for response 200 17 | * For response `default` status 200 is used. 18 | */ 19 | var status = 200; 20 | var provider = dataProvider['get']['200']; 21 | provider(req, res, function (err, data) { 22 | if (err) { 23 | next(err); 24 | return; 25 | } 26 | res.status(status).send(data && data.responses); 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /docs/demo/nodejs/handlers/user/{userID}.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var dataProvider = require('../../data/user/{userID}.js'); 3 | /** 4 | * Operations on /user/{userID} 5 | */ 6 | module.exports = { 7 | /** 8 | * summary: 9 | * description: Get a User Profile by ID 10 | * parameters: 11 | * produces: 12 | * responses: 200, default 13 | */ 14 | get: function userGET(req, res, next) { 15 | /** 16 | * Get the data for response 200 17 | * For response `default` status 200 is used. 18 | */ 19 | var status = 200; 20 | var provider = dataProvider['get']['200']; 21 | provider(req, res, function (err, data) { 22 | if (err) { 23 | next(err); 24 | return; 25 | } 26 | res.status(status).send(data && data.responses); 27 | }); 28 | }, 29 | /** 30 | * summary: 31 | * description: Declares and creates a new profile 32 | * parameters: _profile 33 | * produces: 34 | * responses: 201, default 35 | */ 36 | post: function userPOST(req, res, next) { 37 | /** 38 | * Get the data for response 201 39 | * For response `default` status 200 is used. 40 | */ 41 | var status = 201; 42 | var provider = dataProvider['post']['201']; 43 | provider(req, res, function (err, data) { 44 | if (err) { 45 | next(err); 46 | return; 47 | } 48 | res.status(status).send(data && data.responses); 49 | }); 50 | }, 51 | /** 52 | * summary: 53 | * description: Update User 54 | * parameters: 55 | * produces: 56 | * responses: 200, 404, default 57 | */ 58 | patch: function updateUser(req, res, next) { 59 | /** 60 | * Get the data for response 200 61 | * For response `default` status 200 is used. 62 | */ 63 | var status = 200; 64 | var provider = dataProvider['patch']['200']; 65 | provider(req, res, function (err, data) { 66 | if (err) { 67 | next(err); 68 | return; 69 | } 70 | res.status(status).send(data && data.responses); 71 | }); 72 | }, 73 | /** 74 | * summary: 75 | * description: Delete User By ID 76 | * parameters: 77 | * produces: 78 | * responses: 204, 404, default 79 | */ 80 | delete: function userDELETE(req, res, next) { 81 | /** 82 | * Get the data for response 204 83 | * For response `default` status 200 is used. 84 | */ 85 | var status = 204; 86 | var provider = dataProvider['delete']['204']; 87 | provider(req, res, function (err, data) { 88 | if (err) { 89 | next(err); 90 | return; 91 | } 92 | res.status(status).send(data && data.responses); 93 | }); 94 | } 95 | }; 96 | -------------------------------------------------------------------------------- /docs/demo/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mydriving-user-api", 3 | "description": "This is the User API for the MyDriving service", 4 | "version": "1.0.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/Azure-Samples/openhack-devops-team.git" 8 | }, 9 | "bugs": "http://github.com/Azure-Samples/openhack-devops-team/issues", 10 | "dependencies": { 11 | "body-parser": "^1.18.3", 12 | "express": "^4.16.3", 13 | "express4-tedious": "^0.3.0", 14 | "morgan": "^1.9.0", 15 | "swaggerize-express": "^4.0.5", 16 | "swagmock": "1.0.0", 17 | "swagger-ui-express": "^3.0.10", 18 | "tedious": "^2.6.4" 19 | }, 20 | "devDependencies": { 21 | "eslint": "^5", 22 | "is-my-json-valid": "^2.17.2", 23 | "js-yaml": "^3.12.0", 24 | "nyc": "^13.0.1", 25 | "supertest": "^3.1.0", 26 | "swagger-parser": "^4.1.0", 27 | "tap-junit": "^2.0.0", 28 | "tape": "^4.9.1" 29 | }, 30 | "nyc": { 31 | "check-coverage": true, 32 | "per-file": false, 33 | "lines": 25, 34 | "statements": 8, 35 | "functions": 9, 36 | "branches": 1, 37 | "reporter": [ 38 | "cobertura", 39 | "html" 40 | ], 41 | "require": [], 42 | "cache": true, 43 | "all": true, 44 | "temp-directory": "./reports-tmp", 45 | "report-dir": "./reports" 46 | }, 47 | "scripts": { 48 | "test": "tape 'tests/**/*.js' | tap-junit --output reports --name userprofile-report", 49 | "cover": "nyc tape -- 'tests/**/*.js' --cov", 50 | "lint": "eslint .", 51 | "regenerate": "yo swaggerize:test --framework express --apiPath './config/swagger.json'" 52 | }, 53 | "generator-swaggerize": { 54 | "version": "4.1.0" 55 | }, 56 | "main": "./server" 57 | } 58 | -------------------------------------------------------------------------------- /docs/demo/nodejs/readme.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub. 4 | 5 | This api uses node 8.x LTS and express as the server and c 6 | 7 | ## Running the server 8 | 9 | To run the server, run: 10 | 11 | ```shell 12 | npm start 13 | ``` 14 | 15 | ### To view the Swagger UI interface 16 | 17 | ```shell 18 | open http://localhost:8080/api/docs/user 19 | ``` 20 | 21 | ### To execute the unit tests 22 | 23 | ```shell 24 | npm run test 25 | ``` 26 | 27 | There will be a junit formatted report file called userprofile-report.xml under the current userprofile directory `/reports` subfolder. -------------------------------------------------------------------------------- /docs/demo/nodejs/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Http = require('http'); 4 | var Express = require('express'); 5 | var BodyParser = require('body-parser'); 6 | var Swaggerize = require('swaggerize-express'); 7 | var Path = require('path'); 8 | var tediousExpress = require('express4-tedious'); 9 | var sqlConfig = require('./config/sqlConfig'); 10 | var morgan = require('morgan'); 11 | const swaggerUi = require('swagger-ui-express'); 12 | const swaggerDocument = require('./config/swagger.json'); 13 | 14 | var App = Express(); 15 | 16 | var Server = Http.createServer(App); 17 | 18 | var logger = morgan(':remote-addr [:date[web]] :method :url HTTP/:http-version :status :res[content-length] :referrer :user-agent :response-time ms'); 19 | 20 | App.use(logger); 21 | 22 | App.use(function (req, res, next) { 23 | req.sql = tediousExpress(sqlConfig); 24 | next(); 25 | }); 26 | 27 | App.use(BodyParser.json()); 28 | App.use(BodyParser.urlencoded({ 29 | extended: true 30 | })); 31 | 32 | App.use(Swaggerize({ 33 | api: Path.resolve('./config/swagger.json'), 34 | handlers: Path.resolve('./handlers') 35 | })); 36 | 37 | App.use('/api/docs/user', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); 38 | 39 | Server.listen(8080, function () { 40 | App.swagger.api.host = this.address().address + ':' + this.address().port; 41 | /* eslint-disable no-console */ 42 | console.log('App running on %s:%d', this.address().address, this.address().port); 43 | /* eslint-disable no-console */ 44 | }); 45 | -------------------------------------------------------------------------------- /docs/demo/nodejs/swagger/swagger.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | swagger: "2.0" 3 | info: 4 | description: "API for the user profile in the My Driving example app. https://github.com/Azure-Samples/openhack-devops-team" 5 | version: "0.1.0" 6 | title: "My Driving User Profile API" 7 | basePath: "/api" 8 | schemes: 9 | - "http" 10 | consumes: 11 | - "application/json" 12 | produces: 13 | - "application/json" 14 | paths: 15 | /healthcheck/user: 16 | x-swagger-router-controller: healthcheck 17 | get: 18 | description: "Returns healthcheck for systems looking to ensure API is up and operational" 19 | responses: 20 | 200: 21 | description: "Service is healthy" 22 | schema: 23 | $ref: "#/definitions/Healthcheck" 24 | default: 25 | description: "An error occurred" 26 | schema: 27 | $ref: "#/definitions/error_response_default" 28 | /user: 29 | get: 30 | description: "List all user profiles" 31 | operationId: "getAllUsers" 32 | parameters: [] 33 | responses: 34 | 200: 35 | description: "List of all users" 36 | schema: 37 | type: "array" 38 | items: 39 | $ref: "#/definitions/Profile" 40 | default: 41 | description: "An error occurred" 42 | schema: 43 | $ref: "#/definitions/inline_response_default" 44 | x-swagger-router-controller: "user" 45 | /user/{userID}: 46 | parameters: 47 | - 48 | name: "userID" 49 | in: "path" 50 | description: "User's unique ID" 51 | type: "string" 52 | required: true 53 | get: 54 | description: "Get a User Profile by ID" 55 | operationId: "userGET" 56 | responses: 57 | 200: 58 | description: "List of profiles" 59 | schema: 60 | type: "array" 61 | items: 62 | $ref: "#/definitions/Profile" 63 | default: 64 | description: "An error occurred" 65 | schema: 66 | $ref: "#/definitions/inline_response_default" 67 | x-swagger-router-controller: "user" 68 | post: 69 | description: "Declares and creates a new profile" 70 | operationId: "userPOST" 71 | parameters: 72 | - in: "body" 73 | name: "_profile" 74 | description: "Details of the profile" 75 | required: true 76 | schema: 77 | $ref: "#/definitions/Profile" 78 | responses: 79 | 201: 80 | description: "Creation successful" 81 | schema: 82 | $ref: "#/definitions/Profile" 83 | headers: 84 | location: 85 | type: "string" 86 | default: 87 | description: "An error occurred" 88 | schema: 89 | $ref: "#/definitions/inline_response_default" 90 | x-swagger-router-controller: "user" 91 | patch: 92 | description: "Update User" 93 | operationId: "updateUser" 94 | responses: 95 | 200: 96 | description: "User Updated" 97 | schema: 98 | $ref: "#/definitions/Profile" 99 | 404: 100 | description: "User profile not found" 101 | default: 102 | description: "Unknown Error" 103 | schema: 104 | $ref: "#/definitions/error_response_default" 105 | delete: 106 | description: "Delete User By ID" 107 | operationId: "userDELETE" 108 | responses: 109 | 204: 110 | description: "User Deleted" 111 | 404: 112 | description: "User not found" 113 | default: 114 | description: "Unknown Error" 115 | schema: 116 | $ref: "#/definitions/error_response_default" 117 | definitions: 118 | Healthcheck: 119 | type: "object" 120 | properties: 121 | message: 122 | type: "string" 123 | description: "" 124 | status: 125 | type: "string" 126 | description: "" 127 | Profile: 128 | type: "object" 129 | properties: 130 | Id: 131 | type: "string" 132 | description: "User's unique identity" 133 | FirstName: 134 | type: "string" 135 | minLength: 0 136 | maxLength: 50 137 | pattern: "^[A-Za-z \0-\x7f][a-zA-Z \0-\x7f]*$" 138 | LastName: 139 | type: "string" 140 | minLength: 0 141 | maxLength: 80 142 | pattern: "^[A-Za-z \0-\x7f][a-zA-Z \0-\x7f]*$" 143 | UserId: 144 | type: "string" 145 | description: "User's identity" 146 | ProfilePictureUri: 147 | type: "string" 148 | format: "binary" 149 | description: "User's Profile picture" 150 | Rating: 151 | type: "integer" 152 | description: "User's rating" 153 | Ranking: 154 | type: "integer" 155 | description: "User's ranking" 156 | TotalDistance: 157 | type: "number" 158 | format: "float" 159 | description: "User's total distance traveled" 160 | TotalTrips: 161 | type: "integer" 162 | format: "long" 163 | description: "User's total number of trips" 164 | TotalTime: 165 | type: "integer" 166 | format: "long" 167 | description: "User's total driving time" 168 | HardStops: 169 | type: "integer" 170 | format: "long" 171 | description: "User's total number of hard stops" 172 | HardAccelerations: 173 | type: "integer" 174 | format: "long" 175 | description: "User's total number of hard accelerations" 176 | FuelConsumption: 177 | type: "number" 178 | format: "float" 179 | description: "User's amount of fuel consumed" 180 | MaxSpeed: 181 | type: "number" 182 | format: "float" 183 | description: "User's maximum speed" 184 | CreatedAt: 185 | type: "string" 186 | format: "date" 187 | UpdatedAt: 188 | type: "string" 189 | format: "date" 190 | Deleted: 191 | type: "boolean" 192 | description: "Whether the user has been deleted or not." 193 | error_response_default: 194 | type: "object" 195 | properties: 196 | status: 197 | description: "Error code (if available)" 198 | type: "integer" 199 | format: "int32" 200 | message: 201 | description: "Error Message" 202 | type: "string" 203 | inline_response_default: 204 | required: 205 | - "message" 206 | - "status" 207 | properties: 208 | status: 209 | type: "integer" 210 | format: "int32" 211 | message: 212 | type: "string" 213 | -------------------------------------------------------------------------------- /docs/demo/nodejs/tests/healthcheck/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Test = require('tape'); 3 | var Express = require('express'); 4 | var BodyParser = require('body-parser'); 5 | var Swaggerize = require('swaggerize-express'); 6 | var Path = require('path'); 7 | var Request = require('supertest'); 8 | var Mockgen = require('../../data/mockgen.js'); 9 | var Parser = require('swagger-parser'); 10 | /** 11 | * Test for /healthcheck/user 12 | */ 13 | Test('/healthcheck/user', function (t) { 14 | var apiPath = Path.resolve(__dirname, '../../config/swagger.json'); 15 | var App = Express(); 16 | App.use(BodyParser.json()); 17 | App.use(BodyParser.urlencoded({ 18 | extended: true 19 | })); 20 | App.use(Swaggerize({ 21 | api: apiPath, 22 | handlers: Path.resolve(__dirname, '../../handlers') 23 | })); 24 | Parser.validate(apiPath, function (err, api) { 25 | t.error(err, 'No parse error'); 26 | t.ok(api, 'Valid swagger api'); 27 | /** 28 | * summary: 29 | * description: Returns healthcheck for systems looking to ensure API is up and operational 30 | * parameters: 31 | * produces: 32 | * responses: 200, default 33 | */ 34 | t.test('test get operation', function (t) { 35 | Mockgen().requests({ 36 | path: '/healthcheck/user', 37 | operation: 'get' 38 | }, function (err, mock) { 39 | var request; 40 | t.error(err); 41 | t.ok(mock); 42 | t.ok(mock.request); 43 | //Get the resolved path from mock request 44 | //Mock request Path templates({}) are resolved using path parameters 45 | request = Request(App) 46 | .get('/api' + mock.request.path); 47 | if (mock.request.body) { 48 | //Send the request body 49 | request = request.send(mock.request.body); 50 | } else if (mock.request.formData){ 51 | //Send the request form data 52 | request = request.send(mock.request.formData); 53 | //Set the Content-Type as application/x-www-form-urlencoded 54 | request = request.set('Content-Type', 'application/x-www-form-urlencoded'); 55 | } 56 | // If headers are present, set the headers. 57 | if (mock.request.headers && mock.request.headers.length > 0) { 58 | Object.keys(mock.request.headers).forEach(function (headerName) { 59 | request = request.set(headerName, mock.request.headers[headerName]); 60 | }); 61 | } 62 | request.end(function (err, res) { 63 | t.error(err, 'No error'); 64 | t.ok(res.statusCode === 200, 'Ok response status'); 65 | var Validator = require('is-my-json-valid'); 66 | var validate = Validator(api.paths['/healthcheck/user']['get']['responses']['200']['schema']); 67 | var response = res.body; 68 | if (Object.keys(response).length <= 0) { 69 | response = res.text; 70 | } 71 | t.ok(validate(response), 'Valid response'); 72 | t.error(validate.errors, 'No validation errors'); 73 | t.end(); 74 | }); 75 | }); 76 | }); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /docs/demo/nodejs/tests/user.js_: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Test = require('tape'); 3 | var Express = require('express'); 4 | var BodyParser = require('body-parser'); 5 | var Swaggerize = require('swaggerize-express'); 6 | var Path = require('path'); 7 | var Request = require('supertest'); 8 | var Mockgen = require('../data/mockgen.js'); 9 | var Parser = require('swagger-parser'); 10 | /** 11 | * Test for /user 12 | */ 13 | Test('/user', function (t) { 14 | var apiPath = Path.resolve(__dirname, '../config/swagger.json'); 15 | var App = Express(); 16 | App.use(BodyParser.json()); 17 | App.use(BodyParser.urlencoded({ 18 | extended: true 19 | })); 20 | App.use(Swaggerize({ 21 | api: apiPath, 22 | handlers: Path.resolve(__dirname, '../handlers') 23 | })); 24 | Parser.validate(apiPath, function (err, api) { 25 | t.error(err, 'No parse error'); 26 | t.ok(api, 'Valid swagger api'); 27 | /** 28 | * summary: 29 | * description: List all user profiles 30 | * parameters: 31 | * produces: 32 | * responses: 200, default 33 | */ 34 | t.test('test getAllUsers get operation', function (t) { 35 | Mockgen().requests({ 36 | path: '/user', 37 | operation: 'get' 38 | }, function (err, mock) { 39 | var request; 40 | t.error(err); 41 | t.ok(mock); 42 | t.ok(mock.request); 43 | //Get the resolved path from mock request 44 | //Mock request Path templates({}) are resolved using path parameters 45 | request = Request(App) 46 | .get('/api' + mock.request.path); 47 | if (mock.request.body) { 48 | //Send the request body 49 | request = request.send(mock.request.body); 50 | } else if (mock.request.formData){ 51 | //Send the request form data 52 | request = request.send(mock.request.formData); 53 | //Set the Content-Type as application/x-www-form-urlencoded 54 | request = request.set('Content-Type', 'application/x-www-form-urlencoded'); 55 | } 56 | // If headers are present, set the headers. 57 | if (mock.request.headers && mock.request.headers.length > 0) { 58 | Object.keys(mock.request.headers).forEach(function (headerName) { 59 | request = request.set(headerName, mock.request.headers[headerName]); 60 | }); 61 | } 62 | request.end(function (err, res) { 63 | t.error(err, 'No error'); 64 | t.ok(res.statusCode === 200, 'Ok response status'); 65 | var Validator = require('is-my-json-valid'); 66 | var validate = Validator(api.paths['/user']['get']['responses']['200']['schema']); 67 | var response = res.body; 68 | if (Object.keys(response).length <= 0) { 69 | response = res.text; 70 | } 71 | t.ok(validate(response), 'Valid response'); 72 | t.error(validate.errors, 'No validation errors'); 73 | t.end(); 74 | }); 75 | }); 76 | }); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /docs/demo/nodejs/tests/user/{userID}.js_: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Test = require('tape'); 3 | var Express = require('express'); 4 | var BodyParser = require('body-parser'); 5 | var Swaggerize = require('swaggerize-express'); 6 | var Path = require('path'); 7 | var Request = require('supertest'); 8 | var Mockgen = require('../../data/mockgen.js'); 9 | var Parser = require('swagger-parser'); 10 | /** 11 | * Test for /user/{userID} 12 | */ 13 | Test('/user/{userID}', function (t) { 14 | var apiPath = Path.resolve(__dirname, '../../config/swagger.json'); 15 | var App = Express(); 16 | App.use(BodyParser.json()); 17 | App.use(BodyParser.urlencoded({ 18 | extended: true 19 | })); 20 | App.use(Swaggerize({ 21 | api: apiPath, 22 | handlers: Path.resolve(__dirname, '../../handlers') 23 | })); 24 | Parser.validate(apiPath, function (err, api) { 25 | t.error(err, 'No parse error'); 26 | t.ok(api, 'Valid swagger api'); 27 | /** 28 | * summary: 29 | * description: Get a User Profile by ID 30 | * parameters: 31 | * produces: 32 | * responses: 200, default 33 | */ 34 | t.test('test userGET get operation', function (t) { 35 | Mockgen().requests({ 36 | path: '/user/{userID}', 37 | operation: 'get' 38 | }, function (err, mock) { 39 | var request; 40 | t.error(err); 41 | t.ok(mock); 42 | t.ok(mock.request); 43 | //Get the resolved path from mock request 44 | //Mock request Path templates({}) are resolved using path parameters 45 | request = Request(App) 46 | .get('/api' + mock.request.path); 47 | if (mock.request.body) { 48 | //Send the request body 49 | request = request.send(mock.request.body); 50 | } else if (mock.request.formData){ 51 | //Send the request form data 52 | request = request.send(mock.request.formData); 53 | //Set the Content-Type as application/x-www-form-urlencoded 54 | request = request.set('Content-Type', 'application/x-www-form-urlencoded'); 55 | } 56 | // If headers are present, set the headers. 57 | if (mock.request.headers && mock.request.headers.length > 0) { 58 | Object.keys(mock.request.headers).forEach(function (headerName) { 59 | request = request.set(headerName, mock.request.headers[headerName]); 60 | }); 61 | } 62 | request.end(function (err, res) { 63 | t.error(err, 'No error'); 64 | t.ok(res.statusCode === 200, 'Ok response status'); 65 | var Validator = require('is-my-json-valid'); 66 | var validate = Validator(api.paths['/user/{userID}']['get']['responses']['200']['schema']); 67 | var response = res.body; 68 | if (Object.keys(response).length <= 0) { 69 | response = res.text; 70 | } 71 | t.ok(validate(response), 'Valid response'); 72 | t.error(validate.errors, 'No validation errors'); 73 | t.end(); 74 | }); 75 | }); 76 | });/** 77 | * summary: 78 | * description: Declares and creates a new profile 79 | * parameters: _profile 80 | * produces: 81 | * responses: 201, default 82 | */ 83 | t.test('test userPOST post operation', function (t) { 84 | Mockgen().requests({ 85 | path: '/user/{userID}', 86 | operation: 'post' 87 | }, function (err, mock) { 88 | var request; 89 | t.error(err); 90 | t.ok(mock); 91 | t.ok(mock.request); 92 | //Get the resolved path from mock request 93 | //Mock request Path templates({}) are resolved using path parameters 94 | request = Request(App) 95 | .post('/api' + mock.request.path); 96 | if (mock.request.body) { 97 | //Send the request body 98 | request = request.send(mock.request.body); 99 | } else if (mock.request.formData){ 100 | //Send the request form data 101 | request = request.send(mock.request.formData); 102 | //Set the Content-Type as application/x-www-form-urlencoded 103 | request = request.set('Content-Type', 'application/x-www-form-urlencoded'); 104 | } 105 | // If headers are present, set the headers. 106 | if (mock.request.headers && mock.request.headers.length > 0) { 107 | Object.keys(mock.request.headers).forEach(function (headerName) { 108 | request = request.set(headerName, mock.request.headers[headerName]); 109 | }); 110 | } 111 | request.end(function (err, res) { 112 | t.error(err, 'No error'); 113 | t.ok(res.statusCode === 201, 'Ok response status'); 114 | var Validator = require('is-my-json-valid'); 115 | var validate = Validator(api.paths['/user/{userID}']['post']['responses']['201']['schema']); 116 | var response = res.body; 117 | if (Object.keys(response).length <= 0) { 118 | response = res.text; 119 | } 120 | t.ok(validate(response), 'Valid response'); 121 | t.error(validate.errors, 'No validation errors'); 122 | t.end(); 123 | }); 124 | }); 125 | });/** 126 | * summary: 127 | * description: Update User 128 | * parameters: 129 | * produces: 130 | * responses: 200, 404, default 131 | */ 132 | t.test('test updateUser patch operation', function (t) { 133 | Mockgen().requests({ 134 | path: '/user/{userID}', 135 | operation: 'patch' 136 | }, function (err, mock) { 137 | var request; 138 | t.error(err); 139 | t.ok(mock); 140 | t.ok(mock.request); 141 | //Get the resolved path from mock request 142 | //Mock request Path templates({}) are resolved using path parameters 143 | request = Request(App) 144 | .patch('/api' + mock.request.path); 145 | if (mock.request.body) { 146 | //Send the request body 147 | request = request.send(mock.request.body); 148 | } else if (mock.request.formData){ 149 | //Send the request form data 150 | request = request.send(mock.request.formData); 151 | //Set the Content-Type as application/x-www-form-urlencoded 152 | request = request.set('Content-Type', 'application/x-www-form-urlencoded'); 153 | } 154 | // If headers are present, set the headers. 155 | if (mock.request.headers && mock.request.headers.length > 0) { 156 | Object.keys(mock.request.headers).forEach(function (headerName) { 157 | request = request.set(headerName, mock.request.headers[headerName]); 158 | }); 159 | } 160 | request.end(function (err, res) { 161 | t.error(err, 'No error'); 162 | t.ok(res.statusCode === 200, 'Ok response status'); 163 | var Validator = require('is-my-json-valid'); 164 | var validate = Validator(api.paths['/user/{userID}']['patch']['responses']['200']['schema']); 165 | var response = res.body; 166 | if (Object.keys(response).length <= 0) { 167 | response = res.text; 168 | } 169 | t.ok(validate(response), 'Valid response'); 170 | t.error(validate.errors, 'No validation errors'); 171 | t.end(); 172 | }); 173 | }); 174 | });/** 175 | * summary: 176 | * description: Delete User By ID 177 | * parameters: 178 | * produces: 179 | * responses: 204, 404, default 180 | */ 181 | t.test('test userDELETE delete operation', function (t) { 182 | Mockgen().requests({ 183 | path: '/user/{userID}', 184 | operation: 'delete' 185 | }, function (err, mock) { 186 | var request; 187 | t.error(err); 188 | t.ok(mock); 189 | t.ok(mock.request); 190 | //Get the resolved path from mock request 191 | //Mock request Path templates({}) are resolved using path parameters 192 | request = Request(App) 193 | .delete('/api' + mock.request.path); 194 | if (mock.request.body) { 195 | //Send the request body 196 | request = request.send(mock.request.body); 197 | } else if (mock.request.formData){ 198 | //Send the request form data 199 | request = request.send(mock.request.formData); 200 | //Set the Content-Type as application/x-www-form-urlencoded 201 | request = request.set('Content-Type', 'application/x-www-form-urlencoded'); 202 | } 203 | // If headers are present, set the headers. 204 | if (mock.request.headers && mock.request.headers.length > 0) { 205 | Object.keys(mock.request.headers).forEach(function (headerName) { 206 | request = request.set(headerName, mock.request.headers[headerName]); 207 | }); 208 | } 209 | request.end(function (err, res) { 210 | t.error(err, 'No error'); 211 | t.ok(res.statusCode === 204, 'Ok response status'); 212 | t.end(); 213 | }); 214 | }); 215 | }); 216 | }); 217 | }); 218 | -------------------------------------------------------------------------------- /docs/devops_components.md: -------------------------------------------------------------------------------- 1 | # Sharable DevOps Comopnnts 2 | -------------------------------------------------------------------------------- /pipeline-template.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | linuxAgentPool: '' 3 | windowsAgentPool: '' 4 | buildType: '' 5 | projectName: '' 6 | codeRootDir: '' 7 | deployType: '' 8 | helmCharts: '' 9 | initSteps: [] 10 | deploySteps: [] 11 | docker: true 12 | helm: true 13 | imageName: '' 14 | verbose: false 15 | PR: { 16 | enabled: '', 17 | gate: '', 18 | credscan: '', 19 | codescan: '', 20 | vulnerabilitiesscan: '' 21 | } 22 | CI: { 23 | enabled: '', 24 | azureContainerRegistry: '', 25 | azureContainerRegistryName: '', 26 | azureSubscription: '', 27 | scan: '', 28 | gate: '' 29 | } 30 | AUTOMATION: { 31 | enabled: '', 32 | azureSubscription: '', 33 | azureContainerRegistry: '', 34 | azureContainerRegistryName: '', 35 | environmentNamespace: '', 36 | namespace: '', 37 | upgradeParameters: '', 38 | } 39 | FUNCTIONAL: { 40 | enabled: '', 41 | azureSubscription: '', 42 | azureContainerRegistry: '', 43 | azureContainerRegistryName: '', 44 | environmentNamespace: '', 45 | namespace: '', 46 | upgradeParameters: '', 47 | testType: '' 48 | } 49 | NONFUNCTIONAL: { 50 | enabled: '', 51 | azureSubscription: '', 52 | azureContainerRegistry: '', 53 | azureContainerRegistryName: '', 54 | environmentNamespace: '', 55 | namespace: '', 56 | upgradeParameters: '', 57 | } 58 | 59 | stages: 60 | - ${{ if eq(parameters.PR.enabled, 'true') }}: 61 | - stage: PR 62 | displayName: 'Pull Request Validations' 63 | jobs: 64 | - job: PRJobLinux 65 | displayName: Pull Request Validation on Linux 66 | pool: 67 | vmImage: ${{parameters.linuxAgentPool}} 68 | variables: 69 | helmChartsFolder: ${{parameters.codeRootDir}}/${{parameters.helmCharts}}/${{parameters.projectName}} 70 | steps: 71 | - ${{parameters.initSteps}} 72 | 73 | - template: Templates/Build/build.yml 74 | parameters: 75 | buildType: ${{parameters.buildType}} 76 | runTests: slim 77 | buildGoSettingsFile: ${{parameters.buildGoSettingsFile}} 78 | buildMavenSettingsFile: ${{parameters.buildMavenSettingsFile}} 79 | verbose: ${{parameters.verboses}} 80 | codeRootDir: ${{parameters.codeRootDir}} 81 | 82 | - ${{ if eq(parameters.helm, 'true') }}: 83 | 84 | - template: Templates/Common/helm-init.yml 85 | parameters: 86 | connectToAcr: false 87 | - template: Templates/Build/helm-lint.yml 88 | parameters: 89 | helmChartsFolder: $(helmChartsFolder) 90 | gate: '${{parameters.PR.gate}}' 91 | 92 | - ${{ if eq(parameters.PR.codescan, 'true') }}: 93 | - template: Templates/Scan/code-scan.yml 94 | parameters: 95 | buildType: ${{parameters.buildType}} 96 | sonarEndpoint: ${{parameters.PR.codeScanEndpoint}} 97 | projectName: ${{parameters.projectName}} 98 | codeRootDir: ${{parameters.codeRootDir}} 99 | 100 | - ${{ if eq(parameters.PR.vulnerabilitiesscan, 'true') }}: 101 | - template: Templates/Scan/vulnerabilities-scan.yml 102 | parameters: 103 | buildType: ${{parameters.buildType}} 104 | sonarEndpoint: ${{parameters.vulnerabilitiesScanEndpoint}} 105 | projectName: ${{parameters.projectName}} 106 | codeRootDir: ${{parameters.codeRootDir}} 107 | 108 | - job: PRJobWindows 109 | displayName: Pull Request Validation on Windows 110 | dependsOn: PRJobLinux 111 | pool: 112 | vmImage: ${{parameters.windowsAgentPool}} 113 | steps: 114 | - ${{ if eq(parameters.PR.credscan, 'true') }}: 115 | - template: Templates/Scan/cred-scan.yml 116 | parameters: 117 | codeRootDir: ${{parameters.codeRootDir}} 118 | 119 | - ${{ if eq(parameters.CI.enabled, 'true') }}: 120 | - stage: CI 121 | displayName: 'Continuous Integration' 122 | jobs: 123 | - job: CIJob 124 | displayName: Building Testing Packaging and publishing artifacts 125 | pool: 126 | vmImage: ${{parameters.linuxAgentPool}} 127 | variables: 128 | helmChartsFolder: ${{parameters.codeRootDir}}/${{parameters.helmCharts}}/${{parameters.projectName}} 129 | steps: 130 | - ${{parameters.initSteps}} 131 | 132 | - template: Templates/Build/build.yml 133 | parameters: 134 | buildType: ${{parameters.buildType}} 135 | runTests: full 136 | buildGoSettingsFile: ${{parameters.buildGoSettingsFile}} 137 | buildMavenSettingsFile: ${{parameters.buildMavenSettingsFile}} 138 | verbose: ${{parameters.verboses}} 139 | codeRootDir: ${{parameters.codeRootDir}} 140 | 141 | - ${{ if eq(parameters.docker, 'true') }}: 142 | - template: Templates/Build/docker-build-scan-push.yml 143 | parameters: 144 | azureContainerRegistry: ${{parameters.CI.azureContainerRegistry}} 145 | codeRootDir: ${{parameters.codeRootDir}} 146 | scan: ${{parameters.CI.scan}} 147 | tag: $(Build.BuildId) 148 | imageName: ${{parameters.imageName}} 149 | 150 | - ${{ if eq(parameters.helm, 'true') }}: 151 | - template: Templates/Common/helm-init.yml 152 | parameters: 153 | connectToAcr: true 154 | azureSubscription: ${{parameters.CI.azureSubscription}} 155 | azureContainerRegistryName: ${{parameters.CI.azureContainerRegistryName}} 156 | 157 | - template: Templates/Build/publish-helm-chart.yml 158 | parameters: 159 | chartVersion: $(Build.BuildId) 160 | ArtifactStagingDirectory: $(Build.ArtifactStagingDirectory) 161 | helmChartsFolder: $(helmChartsFolder) 162 | azureContainerRegistryName: ${{parameters.CI.azureContainerRegistryName}} 163 | azureSubscription: ${{parameters.CI.azureSubscription}} 164 | projectName: ${{parameters.projectName}} 165 | 166 | 167 | - ${{ if eq(parameters.AUTOMATION.enabled, 'true') }}: 168 | - stage: Automation 169 | displayName: 'Deploy to Automation environment' 170 | dependsOn: CI 171 | jobs: 172 | - deployment: Init 173 | displayName: Initialize Environment Automation 174 | pool: 175 | vmImage: ${{parameters.linuxAgentPool}} 176 | environment: ${{parameters.AUTOMATION.environmentNamespace}} 177 | strategy: 178 | runOnce: 179 | deploy: 180 | steps: 181 | - template: Templates/Deploy/deploy-to-environment.yml 182 | parameters: 183 | deployType: ${{parameters.deployType}} 184 | projectName: ${{parameters.projectName}} 185 | releaseName: ${{parameters.projectName}}-${{parameters.AUTOMATION.namespace}} 186 | chartVersion: $(Build.BuildId) 187 | namespace: ${{parameters.AUTOMATION.namespace}} 188 | azureContainerRegistryName: ${{parameters.AUTOMATION.azureContainerRegistryName}} 189 | azureSubscription: ${{parameters.AUTOMATION.azureSubscription}} 190 | upgradeParameters: ${{parameters.AUTOMATION.upgradeParameters}} 191 | deploySteps: ${{parameters.deploySteps}} 192 | - job: Tests 193 | dependsOn: Init 194 | pool: 195 | vmImage: ${{parameters.linuxAgentPool}} 196 | displayName: 'Automation Testing' 197 | steps: 198 | - template: Templates/Test/smoke-tests.yml 199 | parameters: 200 | dependsOn: Init 201 | scenario: ${{parameters.AUTOMATION.postmanScenario}} 202 | testResultsFiles: ${{parameters.AUTOMATION.postmanTestResults}} 203 | endpoint: ${{parameters.AUTOMATION.dnsName}} 204 | gate: ${{parameters.AUTOMATION.gate}} 205 | 206 | - ${{ if eq(parameters.FUNCTIONAL.enabled, 'true') }}: 207 | - stage: Functional 208 | displayName: 'Deploy to Functional environment' 209 | dependsOn: Automation 210 | jobs: 211 | - deployment: Init 212 | displayName: Initialize Environment Functional 213 | pool: 214 | vmImage: ${{parameters.linuxAgentPool}} 215 | environment: ${{parameters.FUNCTIONAL.environmentNamespace}} 216 | strategy: 217 | runOnce: 218 | deploy: 219 | steps: 220 | - template: Templates/Deploy/deploy-to-environment.yml 221 | parameters: 222 | deployType: ${{parameters.deployType}} 223 | projectName: ${{parameters.projectName}} 224 | releaseName: ${{parameters.projectName}}-${{parameters.FUNCTIONAL.namespace}} 225 | chartVersion: $(Build.BuildId) 226 | namespace: ${{parameters.FUNCTIONAL.namespace}} 227 | azureContainerRegistryName: ${{parameters.FUNCTIONAL.azureContainerRegistryName}} 228 | azureSubscription: ${{parameters.FUNCTIONAL.azureSubscription}} 229 | upgradeParameters: ${{parameters.FUNCTIONAL.upgradeParameters}} 230 | deploySteps: ${{parameters.deploySteps}} 231 | - job: Tests 232 | dependsOn: Init 233 | pool: 234 | vmImage: ${{parameters.linuxAgentPool}} 235 | displayName: 'Functional Testing' 236 | steps: 237 | - ${{ if eq(parameters.Functional.testType, 'gatling') }}: 238 | - template: Templates/Test/gatling-functional-tests.yml 239 | parameters: 240 | dependsOn: Init 241 | scenario: ${{parameters.FUNCTIONAL.gatlingScenario}} 242 | gate: ${{parameters.FUNCTIONAL.gate}} 243 | - ${{ if eq(parameters.Functional.testType, 'postman') }}: 244 | - template: Templates/Test/postman-functional-tests.yml 245 | parameters: 246 | dependsOn: Init 247 | scenario: ${{parameters.FUNCTIONAL.postmanScenario}} 248 | gate: ${{parameters.FUNCTIONAL.gate}} 249 | 250 | - ${{ if eq(parameters.NONFUNCTIONAL.enabled, 'true') }}: 251 | - stage: NonFunctional 252 | displayName: 'Deploy to non Functional environment' 253 | dependsOn: Functional 254 | jobs: 255 | - deployment: Init 256 | displayName: Initialize Environment non Functional 257 | pool: 258 | vmImage: ${{parameters.linuxAgentPool}} 259 | environment: ${{parameters.NONFUNCTIONAL.environmentNamespace}} 260 | strategy: 261 | runOnce: 262 | deploy: 263 | steps: 264 | - template: Templates/Deploy/deploy-to-environment.yml 265 | parameters: 266 | deployType: ${{parameters.deployType}} 267 | projectName: ${{parameters.projectName}} 268 | releaseName: ${{parameters.projectName}}-${{parameters.NONFUNCTIONAL.namespace}} 269 | chartVersion: $(Build.BuildId) 270 | namespace: ${{parameters.NONFUNCTIONAL.namespace}} 271 | azureContainerRegistryName: ${{parameters.FUNCTIONAL.azureContainerRegistryName}} 272 | azureSubscription: ${{parameters.NONFUNCTIONAL.azureSubscription}} 273 | upgradeParameters: ${{parameters.NONFUNCTIONAL.upgradeParameters}} 274 | deploySteps: ${{parameters.deploySteps}} 275 | - job: Tests 276 | dependsOn: Init 277 | pool: 278 | vmImage: ${{parameters.linuxAgentPool}} 279 | displayName: 'Performance and Stress Tests' 280 | steps: 281 | - template: Templates/Test/jmeter-performance-tests.yml 282 | parameters: 283 | dependsOn: Init 284 | scenario: ${{parameters.NONFUNCTIONAL.jmeterScenario}} 285 | gate: ${{parameters.NONFUNCTIONAL.gate}} 286 | --------------------------------------------------------------------------------