From 18fe1e0f53996f4011e7bbb91e3862d763f503db Mon Sep 17 00:00:00 2001 From: Elvin Morales Date: Wed, 5 Dec 2018 18:51:13 -0800 Subject: [PATCH 01/19] Reference timer to prevent GC --- iothub-manager/RecurringTasksAgent/Agent.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iothub-manager/RecurringTasksAgent/Agent.cs b/iothub-manager/RecurringTasksAgent/Agent.cs index 4b463b7..d3444c9 100644 --- a/iothub-manager/RecurringTasksAgent/Agent.cs +++ b/iothub-manager/RecurringTasksAgent/Agent.cs @@ -25,6 +25,7 @@ namespace Microsoft.Azure.IoTSolutions.IotHubManager.RecurringTasksAgent private readonly IDeviceProperties deviceProperties; private readonly ILogger log; + private Timer cacheUpdateTimer; public Agent( IDeviceProperties deviceProperties, @@ -66,7 +67,7 @@ namespace Microsoft.Azure.IoTSolutions.IotHubManager.RecurringTasksAgent try { this.log.Info("Scheduling a DeviceProperties cache update", () => new { CACHE_UPDATE_SECS }); - var unused = new Timer( + this.cacheUpdateTimer = new Timer( this.UpdateDevicePropertiesCache, null, 1000 * CACHE_UPDATE_SECS, From d8b372e6c90a62109796810d6f03bcdd23dc75db Mon Sep 17 00:00:00 2001 From: Elvin Morales Date: Wed, 12 Dec 2018 10:47:40 -0800 Subject: [PATCH 02/19] Trigger new build --- iothub-manager/RecurringTasksAgent/Agent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iothub-manager/RecurringTasksAgent/Agent.cs b/iothub-manager/RecurringTasksAgent/Agent.cs index d3444c9..36a0913 100644 --- a/iothub-manager/RecurringTasksAgent/Agent.cs +++ b/iothub-manager/RecurringTasksAgent/Agent.cs @@ -72,7 +72,7 @@ namespace Microsoft.Azure.IoTSolutions.IotHubManager.RecurringTasksAgent null, 1000 * CACHE_UPDATE_SECS, Timeout.Infinite); - this.log.Info("DeviceProperties Cache update scheduled", () => new { CACHE_UPDATE_SECS }); + this.log.Info("DeviceProperties Cache update scheduled.", () => new { CACHE_UPDATE_SECS }); } catch (Exception e) { From ac517f937b2fb5e71469d14e5a3f7e058153bf7c Mon Sep 17 00:00:00 2001 From: sushilraje <39531904+sushilraje@users.noreply.github.com> Date: Tue, 8 Jan 2019 17:02:30 -0800 Subject: [PATCH 03/19] Bug fix for the packages test --- .../Controllers/PackageControllerTest.cs | 18 +++++++++++++----- config/scripts/build | 9 ++++++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/config/WebService.Test/Controllers/PackageControllerTest.cs b/config/WebService.Test/Controllers/PackageControllerTest.cs index 921824a..9053cae 100644 --- a/config/WebService.Test/Controllers/PackageControllerTest.cs +++ b/config/WebService.Test/Controllers/PackageControllerTest.cs @@ -45,7 +45,8 @@ namespace WebService.Test.Controllers IFormFile file = null; if (isValidFileProvided) { - file = this.CreateSampleFile(filename); + bool isEdgePackage = (type == "EdgeManifest") ? true : false; + file = this.CreateSampleFile(filename, isEdgePackage); } Enum.TryParse(type, out PackageType pckgType); @@ -195,13 +196,20 @@ namespace WebService.Test.Controllers Times.Once); } - private FormFile CreateSampleFile(string filename) + private FormFile CreateSampleFile(string filename, bool isEdgePackage) { + var admPackage = "{\"id\":\"dummy\",\"content\":{\"deviceContent\":{}}}"; + var edgePackage = "{\"id\":\"dummy\",\"content\":{\"modulesContent\":{}}}"; + var stream = new MemoryStream(); - stream.WriteByte(100); - stream.Flush(); + var writer = new StreamWriter(stream); + var package = isEdgePackage ? edgePackage : admPackage; + + writer.Write(package); + writer.Flush(); stream.Position = 0; - return new FormFile(stream, 0, 1, "file", filename); + + return new FormFile(stream, 0, package.Length, "file", filename); } } } diff --git a/config/scripts/build b/config/scripts/build index 65b3c18..283f03a 100755 --- a/config/scripts/build +++ b/config/scripts/build @@ -33,10 +33,13 @@ run_tests() { cd $APP_HOME header "Running tests..." - PROJECTS=$(dotnet sln list | grep 'csproj$' | grep '\.Test') + PROJECTS=$(dotnet sln config.sln list | grep '\.Test') + header "Found projects $PROJECTS" for PROJ in $PROJECTS; do - echo "-- $PROJ" - dotnet test --configuration $CONFIGURATION $PROJ + PROJ_MF=${PROJ//\\/\/} + # Cleaning up special charcters from the array element. + PROJ_MF=$(echo $PROJ_MF | sed 's/[^a-zA-Z\.\/]//g') + dotnet test --configuration $CONFIGURATION $PROJ_MF done } From b366a08c8d9f9b47677f87e6a8d3debab92c61f4 Mon Sep 17 00:00:00 2001 From: sushilraje <39531904+sushilraje@users.noreply.github.com> Date: Wed, 9 Jan 2019 11:25:34 -0800 Subject: [PATCH 04/19] Build script fixes for running tests on VS2017 machine(s). --- .../TelemetryRulesAgent/Models/AsaRefDataRule.cs | 2 +- asa-manager/scripts/build | 14 +++++++------- auth/scripts/build | 9 ++++++--- config/scripts/build | 10 +++++----- device-telemetry/scripts/build | 12 ++++++------ iothub-manager/scripts/build | 12 ++++++------ storage-adapter/scripts/build | 12 ++++++------ 7 files changed, 37 insertions(+), 34 deletions(-) diff --git a/asa-manager/TelemetryRulesAgent/Models/AsaRefDataRule.cs b/asa-manager/TelemetryRulesAgent/Models/AsaRefDataRule.cs index 92b033c..faa5e29 100644 --- a/asa-manager/TelemetryRulesAgent/Models/AsaRefDataRule.cs +++ b/asa-manager/TelemetryRulesAgent/Models/AsaRefDataRule.cs @@ -152,7 +152,7 @@ namespace Microsoft.Azure.IoTSolutions.AsaManager.TelemetryRulesAgent.Models this.Fields.Add(c.Field); } - if (rule.Actions != null && rule.Actions.Count > 0) + if (rule.Actions != null && rule.Actions.Count >= 0) { this.Actions = rule.Actions; } diff --git a/asa-manager/scripts/build b/asa-manager/scripts/build index a46bf33..589d70a 100755 --- a/asa-manager/scripts/build +++ b/asa-manager/scripts/build @@ -21,10 +21,7 @@ PCS_CACHE="/tmp/azure/iotpcs/.cache" compile() { check_dependency_dotnet - - cd $APP_HOME - ./scripts/env-vars-check - + header "Downloading dependencies..." dotnet restore @@ -37,10 +34,13 @@ run_tests() { cd $APP_HOME header "Running tests..." - PROJECTS=$(dotnet sln list | grep 'csproj$' | grep '\.Test') + PROJECTS=$(dotnet sln asa-manager.sln list | grep '\.Test') + for PROJ in $PROJECTS; do - echo "-- $PROJ" - dotnet test --configuration $CONFIGURATION $PROJ + PROJ=${PROJ//\\/\/} + PROJ=$(echo $PROJ | sed 's/[^a-zA-Z\.\/]//g') + echo "-- $PROJ" + dotnet test --configuration $CONFIGURATION $PROJ done } diff --git a/auth/scripts/build b/auth/scripts/build index e2a1cfa..22927fd 100755 --- a/auth/scripts/build +++ b/auth/scripts/build @@ -34,10 +34,13 @@ run_tests() { cd $APP_HOME header "Running tests..." - PROJECTS=$(dotnet sln list | grep 'csproj$' | grep '\.Test') + PROJECTS=$(dotnet sln auth.sln list | grep '\.Test') + for PROJ in $PROJECTS; do - echo "-- $PROJ" - dotnet test --configuration $CONFIGURATION $PROJ + PROJ=${PROJ//\\/\/} + PROJ=$(echo $PROJ | sed 's/[^a-zA-Z\.\/]//g') + echo "-- $PROJ" + dotnet test --configuration $CONFIGURATION $PROJ done } diff --git a/config/scripts/build b/config/scripts/build index 283f03a..5458f26 100755 --- a/config/scripts/build +++ b/config/scripts/build @@ -34,12 +34,12 @@ run_tests() { cd $APP_HOME header "Running tests..." PROJECTS=$(dotnet sln config.sln list | grep '\.Test') - header "Found projects $PROJECTS" + for PROJ in $PROJECTS; do - PROJ_MF=${PROJ//\\/\/} - # Cleaning up special charcters from the array element. - PROJ_MF=$(echo $PROJ_MF | sed 's/[^a-zA-Z\.\/]//g') - dotnet test --configuration $CONFIGURATION $PROJ_MF + PROJ=${PROJ//\\/\/} + PROJ=$(echo $PROJ | sed 's/[^a-zA-Z\.\/]//g') + echo "-- $PROJ" + dotnet test --configuration $CONFIGURATION $PROJ done } diff --git a/device-telemetry/scripts/build b/device-telemetry/scripts/build index 2ccb9fd..41c353f 100755 --- a/device-telemetry/scripts/build +++ b/device-telemetry/scripts/build @@ -22,9 +22,6 @@ PCS_CACHE="/tmp/azure/iotpcs/.cache" compile() { check_dependency_dotnet - cd $APP_HOME - ./scripts/env-vars-check - header "Downloading dependencies..." dotnet restore @@ -37,10 +34,13 @@ run_tests() { cd $APP_HOME header "Running tests..." - PROJECTS=$(dotnet sln list | grep 'csproj$' | grep '\.Test') + PROJECTS=$(dotnet sln telemetry.sln list | grep '\.Test') + for PROJ in $PROJECTS; do - echo "-- $PROJ" - dotnet test --configuration $CONFIGURATION $PROJ + PROJ=${PROJ//\\/\/} + PROJ=$(echo $PROJ | sed 's/[^a-zA-Z\.\/]//g') + echo "-- $PROJ" + dotnet test --configuration $CONFIGURATION $PROJ done } diff --git a/iothub-manager/scripts/build b/iothub-manager/scripts/build index 7ed00ca..68750f4 100755 --- a/iothub-manager/scripts/build +++ b/iothub-manager/scripts/build @@ -20,9 +20,6 @@ PCS_CACHE="/tmp/azure/iotpcs/.cache" compile() { check_dependency_dotnet - cd $APP_HOME - ./scripts/env-vars-check - header "Downloading dependencies..." dotnet restore @@ -35,10 +32,13 @@ run_tests() { cd $APP_HOME header "Running tests..." - PROJECTS=$(dotnet sln list | grep 'csproj$' | grep '\.Test') + PROJECTS=$(dotnet sln iothub-manager.sln list | grep '\.Test') + for PROJ in $PROJECTS; do - echo "-- $PROJ" - dotnet test --configuration $CONFIGURATION $PROJ + PROJ=${PROJ//\\/\/} + PROJ=$(echo $PROJ | sed 's/[^a-zA-Z\.\/]//g') + echo "-- $PROJ" + dotnet test --configuration $CONFIGURATION $PROJ done } diff --git a/storage-adapter/scripts/build b/storage-adapter/scripts/build index a1d99fe..6bf7cac 100755 --- a/storage-adapter/scripts/build +++ b/storage-adapter/scripts/build @@ -22,9 +22,6 @@ PCS_CACHE="/tmp/azure/iotpcs/.cache" compile() { check_dependency_dotnet - cd $APP_HOME - ./scripts/env-vars-check - header "Downloading dependencies..." dotnet restore @@ -37,10 +34,13 @@ run_tests() { cd $APP_HOME header "Running tests..." - PROJECTS=$(dotnet sln list | grep 'csproj$' | grep '\.Test') + PROJECTS=$(dotnet sln storage-adapter.sln list | grep '\.Test') + for PROJ in $PROJECTS; do - echo "-- $PROJ" - dotnet test --configuration $CONFIGURATION $PROJ + PROJ=${PROJ//\\/\/} + PROJ=$(echo $PROJ | sed 's/[^a-zA-Z\.\/]//g') + echo "-- $PROJ" + dotnet test --configuration $CONFIGURATION $PROJ done } From 2d769612ba3cf21a1350b47808266f3e2acd4cfa Mon Sep 17 00:00:00 2001 From: sushilraje <39531904+sushilraje@users.noreply.github.com> Date: Wed, 9 Jan 2019 14:58:48 -0800 Subject: [PATCH 05/19] continuous deployment artifacts --- docs/CICD.md | 2 + scripts/cd/asamanager/.helmignore | 21 +++++ scripts/cd/asamanager/Chart.yaml | 5 ++ scripts/cd/asamanager/templates/NOTES.txt | 19 +++++ scripts/cd/asamanager/templates/_helpers.tpl | 32 ++++++++ .../cd/asamanager/templates/deployment.yaml | 76 ++++++++++++++++++ scripts/cd/asamanager/templates/ingress.yaml | 39 +++++++++ scripts/cd/asamanager/templates/secrets.yaml | 12 +++ scripts/cd/asamanager/templates/service.yaml | 19 +++++ scripts/cd/asamanager/values.yaml | 75 +++++++++++++++++ scripts/cd/auth/.helmignore | 21 +++++ scripts/cd/auth/Chart.yaml | 5 ++ scripts/cd/auth/templates/NOTES.txt | 19 +++++ scripts/cd/auth/templates/_helpers.tpl | 32 ++++++++ scripts/cd/auth/templates/deployment.yaml | 76 ++++++++++++++++++ scripts/cd/auth/templates/ingress.yaml | 39 +++++++++ scripts/cd/auth/templates/secrets.yaml | 12 +++ scripts/cd/auth/templates/service.yaml | 19 +++++ scripts/cd/auth/values.yaml | 66 +++++++++++++++ scripts/cd/config/.helmignore | 21 +++++ scripts/cd/config/Chart.yaml | 5 ++ scripts/cd/config/templates/NOTES.txt | 19 +++++ scripts/cd/config/templates/_helpers.tpl | 32 ++++++++ scripts/cd/config/templates/deployment.yaml | 76 ++++++++++++++++++ scripts/cd/config/templates/ingress.yaml | 39 +++++++++ scripts/cd/config/templates/secrets.yaml | 12 +++ scripts/cd/config/templates/service.yaml | 19 +++++ scripts/cd/config/values.yaml | 70 ++++++++++++++++ scripts/cd/delete.sh | 26 ++++++ scripts/cd/deploy.sh | 39 +++++++++ scripts/cd/devicetelemetry/.helmignore | 21 +++++ scripts/cd/devicetelemetry/Chart.yaml | 5 ++ .../cd/devicetelemetry/templates/NOTES.txt | 19 +++++ .../cd/devicetelemetry/templates/_helpers.tpl | 32 ++++++++ .../devicetelemetry/templates/deployment.yaml | 79 ++++++++++++++++++ .../cd/devicetelemetry/templates/ingress.yaml | 39 +++++++++ .../cd/devicetelemetry/templates/secrets.yaml | 12 +++ .../cd/devicetelemetry/templates/service.yaml | 19 +++++ scripts/cd/devicetelemetry/values.yaml | 80 +++++++++++++++++++ scripts/cd/helm_install.sh | 5 ++ scripts/cd/ingress/certificates.yaml | 17 ++++ scripts/cd/ingress/cluster-issuer.yaml | 11 +++ scripts/cd/ingress/configure-dns.sh | 13 +++ scripts/cd/ingress/helm-rbac.yaml | 18 +++++ scripts/cd/ingress/helm-setup.sh | 7 ++ scripts/cd/ingress/install-nginx-ingress.sh | 58 ++++++++++++++ scripts/cd/ingress/rm-ingress.yaml | 53 ++++++++++++ scripts/cd/iothubmanager/.helmignore | 21 +++++ scripts/cd/iothubmanager/Chart.yaml | 5 ++ scripts/cd/iothubmanager/templates/NOTES.txt | 19 +++++ .../cd/iothubmanager/templates/_helpers.tpl | 32 ++++++++ .../iothubmanager/templates/deployment.yaml | 76 ++++++++++++++++++ .../cd/iothubmanager/templates/ingress.yaml | 39 +++++++++ .../cd/iothubmanager/templates/secrets.yaml | 12 +++ .../cd/iothubmanager/templates/service.yaml | 19 +++++ scripts/cd/iothubmanager/values.yaml | 68 ++++++++++++++++ scripts/cd/reverse-proxy/.helmignore | 21 +++++ scripts/cd/reverse-proxy/Chart.yaml | 5 ++ scripts/cd/reverse-proxy/templates/NOTES.txt | 19 +++++ .../cd/reverse-proxy/templates/_helpers.tpl | 32 ++++++++ .../reverse-proxy/templates/deployment.yaml | 79 ++++++++++++++++++ .../cd/reverse-proxy/templates/ingress.yaml | 39 +++++++++ .../cd/reverse-proxy/templates/secrets.yaml | 12 +++ .../cd/reverse-proxy/templates/service.yaml | 23 ++++++ scripts/cd/reverse-proxy/values.yaml | 70 ++++++++++++++++ scripts/cd/simulation/.helmignore | 21 +++++ scripts/cd/simulation/Chart.yaml | 5 ++ scripts/cd/simulation/templates/NOTES.txt | 19 +++++ scripts/cd/simulation/templates/_helpers.tpl | 32 ++++++++ .../cd/simulation/templates/deployment.yaml | 79 ++++++++++++++++++ scripts/cd/simulation/templates/ingress.yaml | 39 +++++++++ scripts/cd/simulation/templates/secrets.yaml | 12 +++ scripts/cd/simulation/templates/service.yaml | 19 +++++ scripts/cd/simulation/values.yaml | 72 +++++++++++++++++ scripts/cd/storageadapter/.helmignore | 21 +++++ scripts/cd/storageadapter/Chart.yaml | 5 ++ scripts/cd/storageadapter/templates/NOTES.txt | 19 +++++ .../cd/storageadapter/templates/_helpers.tpl | 32 ++++++++ .../storageadapter/templates/deployment.yaml | 77 ++++++++++++++++++ .../cd/storageadapter/templates/ingress.yaml | 39 +++++++++ .../cd/storageadapter/templates/secrets.yam1 | 12 +++ .../cd/storageadapter/templates/secrets.yaml | 12 +++ .../cd/storageadapter/templates/service.yaml | 19 +++++ scripts/cd/storageadapter/values.yaml | 68 ++++++++++++++++ scripts/cd/webui/.helmignore | 21 +++++ scripts/cd/webui/Chart.yaml | 5 ++ scripts/cd/webui/templates/NOTES.txt | 19 +++++ scripts/cd/webui/templates/_helpers.tpl | 32 ++++++++ scripts/cd/webui/templates/deployment.yaml | 74 +++++++++++++++++ scripts/cd/webui/templates/ingress.yaml | 39 +++++++++ scripts/cd/webui/templates/secrets.yaml | 12 +++ scripts/cd/webui/templates/service.yaml | 19 +++++ scripts/cd/webui/values.yaml | 65 +++++++++++++++ scripts/{ => ci}/vsts/buildimages.sh | 0 scripts/{ => ci}/vsts/buildservices.sh | 0 scripts/{ => ci}/vsts/identifymstobuild.sh | 0 scripts/{ => ci}/vsts/imagestobuild | 0 scripts/{ => ci}/vsts/tagdockerimages.sh | 0 scripts/{ => local}/createsolution | 0 99 files changed, 2914 insertions(+) create mode 100644 docs/CICD.md create mode 100644 scripts/cd/asamanager/.helmignore create mode 100644 scripts/cd/asamanager/Chart.yaml create mode 100644 scripts/cd/asamanager/templates/NOTES.txt create mode 100644 scripts/cd/asamanager/templates/_helpers.tpl create mode 100644 scripts/cd/asamanager/templates/deployment.yaml create mode 100644 scripts/cd/asamanager/templates/ingress.yaml create mode 100644 scripts/cd/asamanager/templates/secrets.yaml create mode 100644 scripts/cd/asamanager/templates/service.yaml create mode 100644 scripts/cd/asamanager/values.yaml create mode 100644 scripts/cd/auth/.helmignore create mode 100644 scripts/cd/auth/Chart.yaml create mode 100644 scripts/cd/auth/templates/NOTES.txt create mode 100644 scripts/cd/auth/templates/_helpers.tpl create mode 100644 scripts/cd/auth/templates/deployment.yaml create mode 100644 scripts/cd/auth/templates/ingress.yaml create mode 100644 scripts/cd/auth/templates/secrets.yaml create mode 100644 scripts/cd/auth/templates/service.yaml create mode 100644 scripts/cd/auth/values.yaml create mode 100644 scripts/cd/config/.helmignore create mode 100644 scripts/cd/config/Chart.yaml create mode 100644 scripts/cd/config/templates/NOTES.txt create mode 100644 scripts/cd/config/templates/_helpers.tpl create mode 100644 scripts/cd/config/templates/deployment.yaml create mode 100644 scripts/cd/config/templates/ingress.yaml create mode 100644 scripts/cd/config/templates/secrets.yaml create mode 100644 scripts/cd/config/templates/service.yaml create mode 100644 scripts/cd/config/values.yaml create mode 100755 scripts/cd/delete.sh create mode 100755 scripts/cd/deploy.sh create mode 100644 scripts/cd/devicetelemetry/.helmignore create mode 100644 scripts/cd/devicetelemetry/Chart.yaml create mode 100644 scripts/cd/devicetelemetry/templates/NOTES.txt create mode 100644 scripts/cd/devicetelemetry/templates/_helpers.tpl create mode 100644 scripts/cd/devicetelemetry/templates/deployment.yaml create mode 100644 scripts/cd/devicetelemetry/templates/ingress.yaml create mode 100644 scripts/cd/devicetelemetry/templates/secrets.yaml create mode 100644 scripts/cd/devicetelemetry/templates/service.yaml create mode 100644 scripts/cd/devicetelemetry/values.yaml create mode 100755 scripts/cd/helm_install.sh create mode 100644 scripts/cd/ingress/certificates.yaml create mode 100644 scripts/cd/ingress/cluster-issuer.yaml create mode 100755 scripts/cd/ingress/configure-dns.sh create mode 100644 scripts/cd/ingress/helm-rbac.yaml create mode 100755 scripts/cd/ingress/helm-setup.sh create mode 100755 scripts/cd/ingress/install-nginx-ingress.sh create mode 100644 scripts/cd/ingress/rm-ingress.yaml create mode 100644 scripts/cd/iothubmanager/.helmignore create mode 100644 scripts/cd/iothubmanager/Chart.yaml create mode 100644 scripts/cd/iothubmanager/templates/NOTES.txt create mode 100644 scripts/cd/iothubmanager/templates/_helpers.tpl create mode 100644 scripts/cd/iothubmanager/templates/deployment.yaml create mode 100644 scripts/cd/iothubmanager/templates/ingress.yaml create mode 100644 scripts/cd/iothubmanager/templates/secrets.yaml create mode 100644 scripts/cd/iothubmanager/templates/service.yaml create mode 100644 scripts/cd/iothubmanager/values.yaml create mode 100644 scripts/cd/reverse-proxy/.helmignore create mode 100644 scripts/cd/reverse-proxy/Chart.yaml create mode 100644 scripts/cd/reverse-proxy/templates/NOTES.txt create mode 100644 scripts/cd/reverse-proxy/templates/_helpers.tpl create mode 100644 scripts/cd/reverse-proxy/templates/deployment.yaml create mode 100644 scripts/cd/reverse-proxy/templates/ingress.yaml create mode 100644 scripts/cd/reverse-proxy/templates/secrets.yaml create mode 100644 scripts/cd/reverse-proxy/templates/service.yaml create mode 100644 scripts/cd/reverse-proxy/values.yaml create mode 100644 scripts/cd/simulation/.helmignore create mode 100644 scripts/cd/simulation/Chart.yaml create mode 100644 scripts/cd/simulation/templates/NOTES.txt create mode 100644 scripts/cd/simulation/templates/_helpers.tpl create mode 100644 scripts/cd/simulation/templates/deployment.yaml create mode 100644 scripts/cd/simulation/templates/ingress.yaml create mode 100644 scripts/cd/simulation/templates/secrets.yaml create mode 100644 scripts/cd/simulation/templates/service.yaml create mode 100644 scripts/cd/simulation/values.yaml create mode 100644 scripts/cd/storageadapter/.helmignore create mode 100644 scripts/cd/storageadapter/Chart.yaml create mode 100644 scripts/cd/storageadapter/templates/NOTES.txt create mode 100644 scripts/cd/storageadapter/templates/_helpers.tpl create mode 100644 scripts/cd/storageadapter/templates/deployment.yaml create mode 100644 scripts/cd/storageadapter/templates/ingress.yaml create mode 100644 scripts/cd/storageadapter/templates/secrets.yam1 create mode 100644 scripts/cd/storageadapter/templates/secrets.yaml create mode 100644 scripts/cd/storageadapter/templates/service.yaml create mode 100644 scripts/cd/storageadapter/values.yaml create mode 100644 scripts/cd/webui/.helmignore create mode 100644 scripts/cd/webui/Chart.yaml create mode 100644 scripts/cd/webui/templates/NOTES.txt create mode 100644 scripts/cd/webui/templates/_helpers.tpl create mode 100644 scripts/cd/webui/templates/deployment.yaml create mode 100644 scripts/cd/webui/templates/ingress.yaml create mode 100644 scripts/cd/webui/templates/secrets.yaml create mode 100644 scripts/cd/webui/templates/service.yaml create mode 100644 scripts/cd/webui/values.yaml rename scripts/{ => ci}/vsts/buildimages.sh (100%) rename scripts/{ => ci}/vsts/buildservices.sh (100%) rename scripts/{ => ci}/vsts/identifymstobuild.sh (100%) rename scripts/{ => ci}/vsts/imagestobuild (100%) rename scripts/{ => ci}/vsts/tagdockerimages.sh (100%) rename scripts/{ => local}/createsolution (100%) diff --git a/docs/CICD.md b/docs/CICD.md new file mode 100644 index 0000000..0e6172a --- /dev/null +++ b/docs/CICD.md @@ -0,0 +1,2 @@ + CI/CD for Remote Monitoring + === diff --git a/scripts/cd/asamanager/.helmignore b/scripts/cd/asamanager/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/scripts/cd/asamanager/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/scripts/cd/asamanager/Chart.yaml b/scripts/cd/asamanager/Chart.yaml new file mode 100644 index 0000000..7bb32f3 --- /dev/null +++ b/scripts/cd/asamanager/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: asamanager +version: 0.1.0 \ No newline at end of file diff --git a/scripts/cd/asamanager/templates/NOTES.txt b/scripts/cd/asamanager/templates/NOTES.txt new file mode 100644 index 0000000..c969491 --- /dev/null +++ b/scripts/cd/asamanager/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "asamanager.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "asamanager.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "asamanager.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "asamanager.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl port-forward $POD_NAME 9024:9024 +{{- end }} diff --git a/scripts/cd/asamanager/templates/_helpers.tpl b/scripts/cd/asamanager/templates/_helpers.tpl new file mode 100644 index 0000000..ec299af --- /dev/null +++ b/scripts/cd/asamanager/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "asamanager.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "asamanager.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "asamanager.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/scripts/cd/asamanager/templates/deployment.yaml b/scripts/cd/asamanager/templates/deployment.yaml new file mode 100644 index 0000000..208b70c --- /dev/null +++ b/scripts/cd/asamanager/templates/deployment.yaml @@ -0,0 +1,76 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "asamanager.fullname" . }} + labels: + app: {{ template "asamanager.name" . }} + chart: {{ template "asamanager.chart" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "asamanager.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "asamanager.name" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + annotations: + buildID: {{ .Values.buildID }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.internalPort }} + protocol: TCP + {{- if not .Values.disableProbes }} + livenessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 120 + initialDelaySeconds: 300 + readinessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 120 + initialDelaySeconds: 300 + {{- end }} + env: + {{- $root := . }} + {{- range $ref, $values := .Values.secrets }} + {{- range $key, $value := $values }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ template "asamanager.fullname" $root }}-{{ $ref | lower }} + key: {{ $key }} + {{- end }} + {{- end }} + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/scripts/cd/asamanager/templates/ingress.yaml b/scripts/cd/asamanager/templates/ingress.yaml new file mode 100644 index 0000000..34b4b12 --- /dev/null +++ b/scripts/cd/asamanager/templates/ingress.yaml @@ -0,0 +1,39 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "asamanager.fullname" . -}} +{{- $servicePort := .Values.service.port -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "asamanager.name" . }} + chart: {{ template "asamanager.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $ingressPath }} + backend: + serviceName: {{ $fullName }} + servicePort: http + {{- end }} +{{- end }} diff --git a/scripts/cd/asamanager/templates/secrets.yaml b/scripts/cd/asamanager/templates/secrets.yaml new file mode 100644 index 0000000..c5a3aab --- /dev/null +++ b/scripts/cd/asamanager/templates/secrets.yaml @@ -0,0 +1,12 @@ +{{- $root := . }} +{{- range $name, $values := .Values.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "asamanager.fullname" $root }}-{{ $name | lower }} +data: + {{- range $key, $value := $values }} + {{ $key }}: {{ $value | b64enc }} + {{- end }} +--- +{{- end }} diff --git a/scripts/cd/asamanager/templates/service.yaml b/scripts/cd/asamanager/templates/service.yaml new file mode 100644 index 0000000..de6a82d --- /dev/null +++ b/scripts/cd/asamanager/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "asamanager.fullname" . }} + labels: + app: {{ template "asamanager.name" . }} + chart: {{ template "asamanager.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: http + selector: + app: {{ template "asamanager.name" . }} + release: {{ .Release.Name }} diff --git a/scripts/cd/asamanager/values.yaml b/scripts/cd/asamanager/values.yaml new file mode 100644 index 0000000..bfcd241 --- /dev/null +++ b/scripts/cd/asamanager/values.yaml @@ -0,0 +1,75 @@ +# Default values for asamanager. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +fullnameOverride: asamanager +replicaCount: 1 +image: + repository: azureiotpcs/asa-manager-dotnet + tag: testing + pullPolicy: Always +imagePullSecrets: [] + # Optionally specify an array of imagePullSecrets. + # Secrets must be manually created in the namespace. + # ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod + # + # This uses credentials from secret "myRegistryKeySecretName". + # - name: myRegistryKeySecretName +service: + type: ClusterIP + externalPort: 80 + internalPort: 9024 + +ingress: + enabled: true + #annotations: + # kubernetes.io/ingress.class: addon-http-application-routing + # kubernetes.io/tls-acme: "true" + path: /asamanager + hosts: + - DNSNAME + # hosts: + # - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local +secrets: + asamanager: + PCS_TELEMETRY_DOCUMENTDB_CONNSTRING: PCS_TELEMETRY_DOCUMENTDB_CONNSTRING + PCS_TELEMETRY_WEBSERVICE_URL: PCS_TELEMETRY_WEBSERVICE_URL + PCS_CONFIG_WEBSERVICE_URL: PCS_CONFIG_WEBSERVICE_URL + PCS_IOTHUBMANAGER_WEBSERVICE_URL: PCS_IOTHUBMANAGER_WEBSERVICE_URL + PCS_ASA_DATA_AZUREBLOB_ACCOUNT: PCS_ASA_DATA_AZUREBLOB_ACCOUNT + PCS_ASA_DATA_AZUREBLOB_KEY: PCS_ASA_DATA_AZUREBLOB_KEY + PCS_ASA_DATA_AZUREBLOB_ENDPOINT_SUFFIX: PCS_ASA_DATA_AZUREBLOB_ENDPOINT_SUFFIX + PCS_EVENTHUB_CONNSTRING: PCS_EVENTHUB_CONNSTRING + PCS_EVENTHUB_NAME: PCS_EVENTHUB_NAME + PCS_TELEMETRY_STORAGE_TYPE: PCS_TELEMETRY_STORAGE_TYPE + PCS_AUTH_REQUIRED: PCS_AUTH_REQUIRED + # Optionally specify a set of secret objects whose values + # will be injected as environment variables by default. + # You should add this section to a file like secrets.yaml + # that is explicitly NOT committed to source code control + # and then include it as part of your helm install step. + # ref: https://kubernetes.io/docs/concepts/configuration/secret/ + # + # This creates a secret "mysecret" and injects "mypassword" + # as the environment variable mysecret_mypassword=password. + # mysecret: + # mypassword: password +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/scripts/cd/auth/.helmignore b/scripts/cd/auth/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/scripts/cd/auth/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/scripts/cd/auth/Chart.yaml b/scripts/cd/auth/Chart.yaml new file mode 100644 index 0000000..0884ef8 --- /dev/null +++ b/scripts/cd/auth/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: auth +version: 0.1.0 diff --git a/scripts/cd/auth/templates/NOTES.txt b/scripts/cd/auth/templates/NOTES.txt new file mode 100644 index 0000000..a94fd6c --- /dev/null +++ b/scripts/cd/auth/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "auth.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "auth.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "auth.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "auth.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/scripts/cd/auth/templates/_helpers.tpl b/scripts/cd/auth/templates/_helpers.tpl new file mode 100644 index 0000000..9bf7efd --- /dev/null +++ b/scripts/cd/auth/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "auth.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "auth.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "auth.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/scripts/cd/auth/templates/deployment.yaml b/scripts/cd/auth/templates/deployment.yaml new file mode 100644 index 0000000..f602e9b --- /dev/null +++ b/scripts/cd/auth/templates/deployment.yaml @@ -0,0 +1,76 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "auth.fullname" . }} + labels: + app: {{ template "auth.name" . }} + chart: {{ template "auth.chart" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "auth.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "auth.name" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + annotations: + buildID: {{ .Values.buildID }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.internalPort }} + protocol: TCP + {{- if not .Values.disableProbes }} + livenessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 120 + initialDelaySeconds: 300 + readinessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 120 + initialDelaySeconds: 300 + {{- end }} + env: + {{- $root := . }} + {{- range $ref, $values := .Values.secrets }} + {{- range $key, $value := $values }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ template "auth.fullname" $root }}-{{ $ref | lower }} + key: {{ $key }} + {{- end }} + {{- end }} + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/scripts/cd/auth/templates/ingress.yaml b/scripts/cd/auth/templates/ingress.yaml new file mode 100644 index 0000000..db108f3 --- /dev/null +++ b/scripts/cd/auth/templates/ingress.yaml @@ -0,0 +1,39 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "auth.fullname" . -}} +{{- $servicePort := .Values.service.port -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "auth.name" . }} + chart: {{ template "auth.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $ingressPath }} + backend: + serviceName: {{ $fullName }} + servicePort: http + {{- end }} +{{- end }} diff --git a/scripts/cd/auth/templates/secrets.yaml b/scripts/cd/auth/templates/secrets.yaml new file mode 100644 index 0000000..8a6ea43 --- /dev/null +++ b/scripts/cd/auth/templates/secrets.yaml @@ -0,0 +1,12 @@ +{{- $root := . }} +{{- range $name, $values := .Values.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "auth.fullname" $root }}-{{ $name | lower }} +data: + {{- range $key, $value := $values }} + {{ $key }}: {{ $value | b64enc }} + {{- end }} +--- +{{- end }} diff --git a/scripts/cd/auth/templates/service.yaml b/scripts/cd/auth/templates/service.yaml new file mode 100644 index 0000000..5f63fff --- /dev/null +++ b/scripts/cd/auth/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "auth.fullname" . }} + labels: + app: {{ template "auth.name" . }} + chart: {{ template "auth.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: http + selector: + app: {{ template "auth.name" . }} + release: {{ .Release.Name }} diff --git a/scripts/cd/auth/values.yaml b/scripts/cd/auth/values.yaml new file mode 100644 index 0000000..36db49b --- /dev/null +++ b/scripts/cd/auth/values.yaml @@ -0,0 +1,66 @@ +# Default values for auth. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +fullnameOverride: auth +replicaCount: 1 +image: + repository: azureiotpcs/pcs-auth-dotnet + tag: testing + pullPolicy: Always +imagePullSecrets: [] + # Optionally specify an array of imagePullSecrets. + # Secrets must be manually created in the namespace. + # ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod + # + # This uses credentials from secret "myRegistryKeySecretName". + # - name: myRegistryKeySecretName +service: + type: ClusterIP + externalPort: 80 + internalPort: 9001 + +ingress: + enabled: true + #annotations: + #kubernetes.io/ingress.class: addon-http-application-routing + # kubernetes.io/tls-acme: "true" + path: /auth + hosts: + - DNSNAME + # hosts: + # - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local +secrets: + auth: + PCS_AUTH_AUDIENCE: PCS_AUTH_AUDIENCE + PCS_AUTH_ISSUER: PCS_AUTH_ISSUER + # Optionally specify a set of secret objects whose values + # will be injected as environment variables by default. + # You should add this section to a file like secrets.yaml + # that is explicitly NOT committed to source code control + # and then include it as part of your helm install step. + # ref: https://kubernetes.io/docs/concepts/configuration/secret/ + # + # This creates a secret "mysecret" and injects "mypassword" + # as the environment variable mysecret_mypassword=password. + # mysecret: + # mypassword: password +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/scripts/cd/config/.helmignore b/scripts/cd/config/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/scripts/cd/config/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/scripts/cd/config/Chart.yaml b/scripts/cd/config/Chart.yaml new file mode 100644 index 0000000..ffffe8d --- /dev/null +++ b/scripts/cd/config/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: config +version: 0.1.0 diff --git a/scripts/cd/config/templates/NOTES.txt b/scripts/cd/config/templates/NOTES.txt new file mode 100644 index 0000000..25640e5 --- /dev/null +++ b/scripts/cd/config/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "config.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "config.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "config.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "config.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/scripts/cd/config/templates/_helpers.tpl b/scripts/cd/config/templates/_helpers.tpl new file mode 100644 index 0000000..8bcfab3 --- /dev/null +++ b/scripts/cd/config/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "config.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "config.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "config.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/scripts/cd/config/templates/deployment.yaml b/scripts/cd/config/templates/deployment.yaml new file mode 100644 index 0000000..5d12bfc --- /dev/null +++ b/scripts/cd/config/templates/deployment.yaml @@ -0,0 +1,76 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "config.fullname" . }} + labels: + app: {{ template "config.name" . }} + chart: {{ template "config.chart" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "config.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "config.name" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + annotations: + buildID: {{ .Values.buildID }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.internalPort }} + protocol: TCP + {{- if not .Values.disableProbes }} + livenessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 120 + initialDelaySeconds: 300 + readinessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 120 + initialDelaySeconds: 300 + {{- end }} + env: + {{- $root := . }} + {{- range $ref, $values := .Values.secrets }} + {{- range $key, $value := $values }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ template "config.fullname" $root }}-{{ $ref | lower }} + key: {{ $key }} + {{- end }} + {{- end }} + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/scripts/cd/config/templates/ingress.yaml b/scripts/cd/config/templates/ingress.yaml new file mode 100644 index 0000000..f7652fc --- /dev/null +++ b/scripts/cd/config/templates/ingress.yaml @@ -0,0 +1,39 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "config.fullname" . -}} +{{- $servicePort := .Values.service.port -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "config.name" . }} + chart: {{ template "config.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $ingressPath }} + backend: + serviceName: {{ $fullName }} + servicePort: http + {{- end }} +{{- end }} diff --git a/scripts/cd/config/templates/secrets.yaml b/scripts/cd/config/templates/secrets.yaml new file mode 100644 index 0000000..5b5fa33 --- /dev/null +++ b/scripts/cd/config/templates/secrets.yaml @@ -0,0 +1,12 @@ +{{- $root := . }} +{{- range $name, $values := .Values.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "config.fullname" $root }}-{{ $name | lower }} +data: + {{- range $key, $value := $values }} + {{ $key }}: {{ $value | b64enc }} + {{- end }} +--- +{{- end }} diff --git a/scripts/cd/config/templates/service.yaml b/scripts/cd/config/templates/service.yaml new file mode 100644 index 0000000..20814ef --- /dev/null +++ b/scripts/cd/config/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "config.fullname" . }} + labels: + app: {{ template "config.name" . }} + chart: {{ template "config.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: http + selector: + app: {{ template "config.name" . }} + release: {{ .Release.Name }} diff --git a/scripts/cd/config/values.yaml b/scripts/cd/config/values.yaml new file mode 100644 index 0000000..4449361 --- /dev/null +++ b/scripts/cd/config/values.yaml @@ -0,0 +1,70 @@ +# Default values for config. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +fullnameOverride: config +replicaCount: 1 +image: + repository: azureiotpcs/pcs-config-dotnet + tag: testing + pullPolicy: Always +imagePullSecrets: [] + # Optionally specify an array of imagePullSecrets. + # Secrets must be manually created in the namespace. + # ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod + # + # This uses credentials from secret "myRegistryKeySecretName". + # - name: myRegistryKeySecretName +service: + type: ClusterIP + externalPort: 80 + internalPort: 9005 + +ingress: + enabled: true + #annotations: + #kubernetes.io/ingress.class: addon-http-application-routing + # kubernetes.io/tls-acme: "true" + path: /config + hosts: + - DNSNAME + # hosts: + # - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local +secrets: + config: + PCS_STORAGEADAPTER_WEBSERVICE_URL: PCS_STORAGEADAPTER_WEBSERVICE_URL + PCS_DEVICESIMULATION_WEBSERVICE_URL: PCS_DEVICESIMULATION_WEBSERVICE_URL + PCS_TELEMETRY_WEBSERVICE_URL: PCS_TELEMETRY_WEBSERVICE_URL + PCS_AZUREMAPS_KEY: PCS_AZUREMAPS_KEY + PCS_AUTH_WEBSERVICE_URL: PCS_AUTH_WEBSERVICE_URL + PCS_AUTH_REQUIRED: PCS_AUTH_REQUIRED + # Optionally specify a set of secret objects whose values + # will be injected as environment variables by default. + # You should add this section to a file like secrets.yaml + # that is explicitly NOT committed to source code control + # and then include it as part of your helm install step. + # ref: https://kubernetes.io/docs/concepts/configuration/secret/ + # + # This creates a secret "mysecret" and injects "mypassword" + # as the environment variable mysecret_mypassword=password. + # mysecret: + # mypassword: password +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/scripts/cd/delete.sh b/scripts/cd/delete.sh new file mode 100755 index 0000000..335390e --- /dev/null +++ b/scripts/cd/delete.sh @@ -0,0 +1,26 @@ +#!/bin/bash +####### helm delete release +echo "Config current context" +kubectl config current-context + +echo "Deleting services from above context" +helm delete storageadapter +helm delete telemetry +helm delete iothubmanager +helm delete simulation +helm delete auth +helm delete webui +helm delete asamanager +helm delete config +####### helm purgedelete to ensure if above cmds don't work +helm del --purge storageadapter +helm del --purge telemetry +helm del --purge iothubmanager +helm del --purge simulation +helm del --purge auth +helm del --purge webui +helm del --purge asamanager +helm del --purge config +##################### Delete any other resources +kubectl delete secret storageadapter-storageadapter +exit 0 diff --git a/scripts/cd/deploy.sh b/scripts/cd/deploy.sh new file mode 100755 index 0000000..786f48a --- /dev/null +++ b/scripts/cd/deploy.sh @@ -0,0 +1,39 @@ +#!/bin/bash +source .env +####### Install helm charts +install() { +# helm install --name storageadapter storageadapter/charts/storageadapter/ --set secrets.storageadapter.PCS_STORAGEADAPTER_DOCUMENTDB_CONNSTRING=${PCS_STORAGEADAPTER_DOCUMENTDB_CONNSTRING} --set-string secrets.storageadapter.PCS_AUTH_REQUIRED=${PCS_AUTH_REQUIRED} + + helm install --name telemetry telemetry/charts/devicetelemetry/ --set secrets.devicetelemetry.PCS_TELEMETRY_DOCUMENTDB_CONNSTRING=${PCS_TELEMETRY_DOCUMENTDB_CONNSTRING} --set secrets.devicetelemetry.PCS_STORAGEADAPTER_WEBSERVICE_URL=${PCS_STORAGEADAPTER_WEBSERVICE_URL} --set secrets.devicetelemetry.PCS_STORAGEADAPTER_WEBSERVICE_URL=${PCS_STORAGEADAPTER_WEBSERVICE_URL} --set secrets.devicetelemetry.PCS_AUTH_WEBSERVICE_URL=${PCS_AUTH_WEBSERVICE_URL} --set secrets.devicetelemetry.PCS_AAD_TENANT=${PCS_AAD_TENANT} --set secrets.devicetelemetry.PCS_AAD_APPID=${PCS_AAD_APPID} --set secrets.devicetelemetry.PCS_AAD_APPSECRET=${PCS_AAD_APPSECRET} --set secrets.devicetelemetry.PCS_TELEMETRY_STORAGE_TYPE=${PCS_TELEMETRY_STORAGE_TYPE} --set secrets.devicetelemetry.PCS_TSI_FQDN=${PCS_TSI_FQDN} --set-string secrets.devicetelemetry.PCS_AUTH_REQUIRED=${PCS_AUTH_REQUIRED} + + helm install --name iothubmanager iothubmanager/charts/iothubmanager/ --set secrets.iothubmanager.PCS_IOTHUB_CONNSTRING=${PCS_IOTHUB_CONNSTRING} --set secrets.iothubmanager.PCS_AUTH_WEBSERVICE_URL=${PCS_AUTH_WEBSERVICE_URL} --set secrets.iothubmanager.PCS_STORAGEADAPTER_WEBSERVICE_URL=${PCS_STORAGEADAPTER_WEBSERVICE_URL} --set-string secrets.iothubmanager.PCS_AUTH_REQUIRED=${PCS_AUTH_REQUIRED} + + helm install --name simulation simulation/charts/simulation/ --set secrets.simulation.PCS_IOTHUB_CONNSTRING=${PCS_IOTHUB_CONNSTRING} --set secrets.simulation.PCS_STORAGEADAPTER_WEBSERVICE_URL=${PCS_STORAGEADAPTER_WEBSERVICE_URL} --set-string secrets.simulation.PCS_AUTH_REQUIRED=${PCS_AUTH_REQUIRED} + + helm install --name config config/charts/config --set secrets.config.PCS_DEVICESIMULATION_WEBSERVICE_URL=${PCS_DEVICESIMULATION_WEBSERVICE_URL} --set secrets.config.PCS_STORAGEADAPTER_WEBSERVICE_URL=${PCS_STORAGEADAPTER_WEBSERVICE_URL} --set secrets.config.PCS_TELEMETRY_WEBSERVICE_URL=${PCS_TELEMETRY_WEBSERVICE_URL} --set secrets.config.PCS_AZUREMAPS_KEY=${PCS_AZUREMAPS_KEY} --set secrets.config.PCS_AUTH_WEBSERVICE_URL=${PCS_AUTH_WEBSERVICE_URL} --set-string secrets.config.PCS_AUTH_REQUIRED=${PCS_AUTH_REQUIRED} --debug + + helm install --name asamanager asamanager/charts/asamanager/ --set secrets.asamanager.PCS_ASA_DATA_AZUREBLOB_ACCOUNT=${PCS_ASA_DATA_AZUREBLOB_ACCOUNT} --set secrets.asamanager.PCS_ASA_DATA_AZUREBLOB_KEY=${PCS_ASA_DATA_AZUREBLOB_KEY} --set secrets.asamanager.PCS_ASA_DATA_AZUREBLOB_ENDPOINT_SUFFIX=${PCS_ASA_DATA_AZUREBLOB_ENDPOINT_SUFFIX} --set secrets.asamanager.PCS_EVENTHUB_CONNSTRING=${PCS_EVENTHUB_CONNSTRING} --set secrets.asamanager.PCS_EVENTHUB_NAME=${PCS_EVENTHUB_NAME} --set secrets.asamanager.PCS_TELEMETRY_DOCUMENTDB_CONNSTRING=${PCS_TELEMETRY_DOCUMENTDB_CONNSTRING} --set secrets.asamanager.PCS_TELEMETRY_WEBSERVICE_URL=${PCS_TELEMETRY_WEBSERVICE_URL} --set secrets.asamanager.PCS_CONFIG_WEBSERVICE_URL=${PCS_CONFIG_WEBSERVICE_URL} --set secrets.asamanager.PCS_IOTHUBMANAGER_WEBSERVICE_URL=${PCS_IOTHUBMANAGER_WEBSERVICE_URL} --set secrets.asamanager.PCS_TELEMETRY_STORAGE_TYPE=${PCS_TELEMETRY_STORAGE_TYPE} --set-string secrets.asamanager.PCS_AUTH_REQUIRED=${PCS_AUTH_REQUIRED} --debug + + helm install --name auth auth/charts/auth/ --set secrets.auth.PCS_AUTH_AUDIENCE=${PCS_AUTH_AUDIENCE} --set secrets.auth.PCS_AUTH_ISSUER=${PCS_AUTH_ISSUER} + + helm install --name webui webui/charts/webui/ --set REACT_APP_BASE_SERVICE_URL=${REACT_APP_BASE_SERVICE_URL} + + helm install --name reverse-proxy reverse-proxy/charts/reverse-proxy/ +} +########################## + +upgrade() { + helm upgrade --install --force storageadapter storageadapter/ --set secrets.storageadapter.PCS_STORAGEADAPTER_DOCUMENTDB_CONNSTRING=${PCS_STORAGEADAPTER_DOCUMENTDB_CONNSTRING} --set-string secrets.storageadapter.PCS_AUTH_REQUIRED=${PCS_AUTH_REQUIRED} --debug +} + + +must_run_once_more(){ + #### Thisstep is required due to bug in helm https://github.com/helm/helm/issues/1479 + helm install --name storageadapter storageadapter/ --set secrets.storageadapter.PCS_STORAGEADAPTER_DOCUMENTDB_CONNSTRING=${PCS_STORAGEADAPTER_DOCUMENTDB_CONNSTRING} --set-string secrets.storageadapter.PCS_AUTH_REQUIRED=${PCS_AUTH_REQUIRED} + exit 0 +} + + +must_run_once_more + +install diff --git a/scripts/cd/devicetelemetry/.helmignore b/scripts/cd/devicetelemetry/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/scripts/cd/devicetelemetry/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/scripts/cd/devicetelemetry/Chart.yaml b/scripts/cd/devicetelemetry/Chart.yaml new file mode 100644 index 0000000..870596c --- /dev/null +++ b/scripts/cd/devicetelemetry/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: devicetelemetry +version: 0.1.0 diff --git a/scripts/cd/devicetelemetry/templates/NOTES.txt b/scripts/cd/devicetelemetry/templates/NOTES.txt new file mode 100644 index 0000000..0f40647 --- /dev/null +++ b/scripts/cd/devicetelemetry/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "devicetelemetry.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "devicetelemetry.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "devicetelemetry.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "devicetelemetry.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/scripts/cd/devicetelemetry/templates/_helpers.tpl b/scripts/cd/devicetelemetry/templates/_helpers.tpl new file mode 100644 index 0000000..7cadc5b --- /dev/null +++ b/scripts/cd/devicetelemetry/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "devicetelemetry.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "devicetelemetry.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "devicetelemetry.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/scripts/cd/devicetelemetry/templates/deployment.yaml b/scripts/cd/devicetelemetry/templates/deployment.yaml new file mode 100644 index 0000000..d1c529e --- /dev/null +++ b/scripts/cd/devicetelemetry/templates/deployment.yaml @@ -0,0 +1,79 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "devicetelemetry.fullname" . }} + labels: + app: {{ template "devicetelemetry.name" . }} + chart: {{ template "devicetelemetry.chart" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "devicetelemetry.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "devicetelemetry.name" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + annotations: + buildID: {{ .Values.buildID }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.internalPort }} + protocol: TCP + {{- if .Values.probes.enabled }} + livenessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 900 + timeoutSeconds: 300 + successThreshold: 60 + initialDelaySeconds: 900 + readinessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 900 + timeoutSeconds: 300 + initialDelaySeconds: 900 + {{- end }} + env: + {{- $root := . }} + {{- range $ref, $values := .Values.secrets }} + {{- range $key, $value := $values }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ template "devicetelemetry.fullname" $root }}-{{ $ref | lower }} + key: {{ $key }} + {{- end }} + {{- end }} + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/scripts/cd/devicetelemetry/templates/ingress.yaml b/scripts/cd/devicetelemetry/templates/ingress.yaml new file mode 100644 index 0000000..1df1740 --- /dev/null +++ b/scripts/cd/devicetelemetry/templates/ingress.yaml @@ -0,0 +1,39 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "devicetelemetry.fullname" . -}} +{{- $servicePort := .Values.service.externalPort -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "devicetelemetry.name" . }} + chart: {{ template "devicetelemetry.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $ingressPath }} + backend: + serviceName: {{ $fullName }} + servicePort: {{ $servicePort }} + {{- end }} +{{- end }} diff --git a/scripts/cd/devicetelemetry/templates/secrets.yaml b/scripts/cd/devicetelemetry/templates/secrets.yaml new file mode 100644 index 0000000..ae0b25d --- /dev/null +++ b/scripts/cd/devicetelemetry/templates/secrets.yaml @@ -0,0 +1,12 @@ +{{- $root := . }} +{{- range $name, $values := .Values.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "devicetelemetry.fullname" $root }}-{{ $name | lower }} +data: + {{- range $key, $value := $values }} + {{ $key }}: {{ $value | b64enc}} + {{- end }} +--- +{{- end }} diff --git a/scripts/cd/devicetelemetry/templates/service.yaml b/scripts/cd/devicetelemetry/templates/service.yaml new file mode 100644 index 0000000..1f86a7d --- /dev/null +++ b/scripts/cd/devicetelemetry/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "devicetelemetry.fullname" . }} + labels: + app: {{ template "devicetelemetry.name" . }} + chart: {{ template "devicetelemetry.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: http + selector: + app: {{ template "devicetelemetry.name" . }} + release: {{ .Release.Name }} diff --git a/scripts/cd/devicetelemetry/values.yaml b/scripts/cd/devicetelemetry/values.yaml new file mode 100644 index 0000000..4a28765 --- /dev/null +++ b/scripts/cd/devicetelemetry/values.yaml @@ -0,0 +1,80 @@ +# Default values for devicetelemetry. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +fullnameOverride: devicetelemetry +replicaCount: 1 +image: + repository: azureiotpcs/telemetry-dotnet + tag: testing + pullPolicy: Always +imagePullSecrets: [] + # Optionally specify an array of imagePullSecrets. + # Secrets must be manually created in the namespace. + # ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod + # + # This uses credentials from secret "myRegistryKeySecretName". + # - name: myRegistryKeySecretName +service: + type: ClusterIP + externalPort: 80 + internalPort: 9004 + +#disableProbes: +# enabled: false +ingress: + enabled: true + #annotations: + #kubernetes.io/ingress.class: addon-http-application-routing + # kubernetes.io/tls-acme: "true" + path: /devicetelemetry + hosts: + - DNSNAME + # hosts: + # - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local +secrets: + devicetelemetry: + PCS_TELEMETRY_DOCUMENTDB_CONNSTRING: PCS_TELEMETRY_DOCUMENTDB_CONNSTRING + PCS_STORAGEADAPTER_WEBSERVICE_URL: PCS_STORAGEADAPTER_WEBSERVICE_URL + PCS_AUTH_WEBSERVICE_URL: PCS_AUTH_WEBSERVICE_URL + PCS_AAD_TENANT: PCS_AAD_TENANT + PCS_AAD_APPID: PCS_AAD_APPID + PCS_AAD_APPSECRET: PCS_AAD_APPSECRET + PCS_TELEMETRY_STORAGE_TYPE: PCS_TELEMETRY_STORAGE_TYPE + PCS_TSI_FQDN: PCS_TSI_FQDN + PCS_DIAGNOSTICS_WEBSERVICE_URL: PCS_DIAGNOSTICS_WEBSERVICE_URL + PCS_AUTH_REQUIRED: PCS_AUTH_REQUIRED + # Optionally specify a set of secret objects whose values + # will be injected as environment variables by default. + # You should add this section to a file like secrets.yaml + # that is explicitly NOT committed to source code control + # and then include it as part of your helm install step. + # ref: https://kubernetes.io/docs/concepts/configuration/secret/ + # + # This creates a secret "mysecret" and injects "mypassword" + # as the environment variable mysecret_mypassword=password. + # mysecret: + # mypassword: password + # +probes: + enabled: false + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/scripts/cd/helm_install.sh b/scripts/cd/helm_install.sh new file mode 100755 index 0000000..b9c3041 --- /dev/null +++ b/scripts/cd/helm_install.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +sh delete.sh +sleep 5 +sh deploy.sh diff --git a/scripts/cd/ingress/certificates.yaml b/scripts/cd/ingress/certificates.yaml new file mode 100644 index 0000000..325026c --- /dev/null +++ b/scripts/cd/ingress/certificates.yaml @@ -0,0 +1,17 @@ +apiVersion: certmanager.k8s.io/v1alpha1 +kind: Certificate +metadata: + name: tls-secret +spec: + secretName: tls-secret + dnsNames: + - DNSGIVENNAME.REGION.cloudapp.azure.com + acme: + config: + - http01: + ingressClass: nginx + domains: + - DNSGIVENNAME.REGION.cloudapp.azure.com + issuerRef: + name: letsencrypt-prod + kind: ClusterIssuer diff --git a/scripts/cd/ingress/cluster-issuer.yaml b/scripts/cd/ingress/cluster-issuer.yaml new file mode 100644 index 0000000..2580b47 --- /dev/null +++ b/scripts/cd/ingress/cluster-issuer.yaml @@ -0,0 +1,11 @@ +apiVersion: certmanager.k8s.io/v1alpha1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: nobody@nobody.com + privateKeySecretRef: + name: letsencrypt-prod + http01: {} diff --git a/scripts/cd/ingress/configure-dns.sh b/scripts/cd/ingress/configure-dns.sh new file mode 100755 index 0000000..c1889c0 --- /dev/null +++ b/scripts/cd/ingress/configure-dns.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Public IP address of your ingress controller +IP="NGINXIP" + +# Name to associate with public IP address +DNSNAME="DNSGIVENNAME" + +# Get the resource-id of the public ip +PUBLICIPID=$(az network public-ip list --query "[?ipAddress!=null]|[?contains(ipAddress, '$IP')].[id]" --output tsv) + +# Update public ip address with DNS name +az network public-ip update --ids $PUBLICIPID --dns-name $DNSNAME diff --git a/scripts/cd/ingress/helm-rbac.yaml b/scripts/cd/ingress/helm-rbac.yaml new file mode 100644 index 0000000..c5ae63a --- /dev/null +++ b/scripts/cd/ingress/helm-rbac.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tiller + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: tiller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: tiller + namespace: kube-system diff --git a/scripts/cd/ingress/helm-setup.sh b/scripts/cd/ingress/helm-setup.sh new file mode 100755 index 0000000..f3dbd3c --- /dev/null +++ b/scripts/cd/ingress/helm-setup.sh @@ -0,0 +1,7 @@ +#!/bin/bash + + +kubectl create -f helm-rbac.yaml + +helm init --service-account tiller + diff --git a/scripts/cd/ingress/install-nginx-ingress.sh b/scripts/cd/ingress/install-nginx-ingress.sh new file mode 100755 index 0000000..224c167 --- /dev/null +++ b/scripts/cd/ingress/install-nginx-ingress.sh @@ -0,0 +1,58 @@ +#!/bin/bash + + +helm install stable/nginx-ingress --namespace kube-system + +echo "Waiting for nginx controller to acquire PUBLIC ip ....." +sleep 20 +########### +###HTTPS Controller +########## + +IP=`kubectl get service -l app=nginx-ingress --namespace kube-system | sed -n '/controller/s/ \+/ /gp' | cut -d" " -f4` + +echo "INGRESS NGINX public IP is:" +echo $IP + +sed -i.bak s/NGINXIP/$IP/g configure-dns.sh + +sed -i.bak1 s/DNSGIVENNAME/$1/g configure-dns.sh + +echo "Configuring DNS ...." +sh configure-dns.sh + +mv configure-dns.sh.bak configure-dns.sh + +rm -rf configure-dns.sh.bak1 + +echo "Installing cert manager" + +helm install stable/cert-manager --set ingressShim.defaultIssuerName=letsencrypt --set ingressShim.defaultIssuerKind=ClusterIssuer + +echo "Creating SSL cert issuer in cluster ..." + +kubectl apply -f cluster-issuer.yaml + +sed -i.bak s/DNSGIVENNAME/$1/g certificates.yaml + +sed -i.bak1 s/REGION/$2/g certificates.yaml + +mv certificates.yaml.bak certificates.yaml + +rm -rf certificates.yaml.bak1 + +echo "Creating certificates resources ...." + +kubectl apply -f certificates.yaml + +echo "Creating Remote Monitoring Nginx controller" + +sed -i.bak s/DNSGIVENNAME/$1/g rm-ingress.yaml + +sed -i.bak s/REGION/$2/g rm-ingress.yaml + +kubectl apply -f rm-ingress.yaml + +mv rm-ingress.yaml.bak rm-ingress.yaml + +rm -rf rm-ingress.yaml.bak1 diff --git a/scripts/cd/ingress/rm-ingress.yaml b/scripts/cd/ingress/rm-ingress.yaml new file mode 100644 index 0000000..9eee557 --- /dev/null +++ b/scripts/cd/ingress/rm-ingress.yaml @@ -0,0 +1,53 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: rm-ingress + annotations: + kubernetes.io/ingress.class: nginx + certmanager.k8s.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + backend: + serviceName: webui + servicePort: 10080 + tls: + - hosts: + - DNSGIVENNAME.REGION.cloudapp.azure.com + secretName: tls-secret + rules: + - host: DNSGIVENNAME.REGION.cloudapp.azure.com + http: + paths: + - path: /storageadapter/(.*) + backend: + serviceName: storageadapter + servicePort: 80 + - path: /iothubmanager/* + backend: + serviceName: iothubmanager + servicePort: 80 + - path: /telemetry/* + backend: + serviceName: devicetelemetry + servicePort: 80 + - path: /devicesimulation/* + backend: + serviceName: simulation + servicePort: 80 + - path: /config/* + backend: + serviceName: config + servicePort: 80 + - path: /asamanager/(.*) + backend: + serviceName: asamanager + servicePort: 80 + - path: /auth/(.*) + backend: + serviceName: auth + servicePort: 80 + - path: / + backend: + serviceName: webui + servicePort: 10080 + diff --git a/scripts/cd/iothubmanager/.helmignore b/scripts/cd/iothubmanager/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/scripts/cd/iothubmanager/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/scripts/cd/iothubmanager/Chart.yaml b/scripts/cd/iothubmanager/Chart.yaml new file mode 100644 index 0000000..e0798df --- /dev/null +++ b/scripts/cd/iothubmanager/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: iothubmanager +version: 0.1.0 diff --git a/scripts/cd/iothubmanager/templates/NOTES.txt b/scripts/cd/iothubmanager/templates/NOTES.txt new file mode 100644 index 0000000..0cbf847 --- /dev/null +++ b/scripts/cd/iothubmanager/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "iothubmanager.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "iothubmanager.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "iothubmanager.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "iothubmanager.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/scripts/cd/iothubmanager/templates/_helpers.tpl b/scripts/cd/iothubmanager/templates/_helpers.tpl new file mode 100644 index 0000000..51e0110 --- /dev/null +++ b/scripts/cd/iothubmanager/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "iothubmanager.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "iothubmanager.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "iothubmanager.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/scripts/cd/iothubmanager/templates/deployment.yaml b/scripts/cd/iothubmanager/templates/deployment.yaml new file mode 100644 index 0000000..b298200 --- /dev/null +++ b/scripts/cd/iothubmanager/templates/deployment.yaml @@ -0,0 +1,76 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "iothubmanager.fullname" . }} + labels: + app: {{ template "iothubmanager.name" . }} + chart: {{ template "iothubmanager.chart" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "iothubmanager.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "iothubmanager.name" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + annotations: + buildID: {{ .Values.buildID }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.internalPort }} + protocol: TCP + {{- if not .Values.disableProbes }} + livenessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 120 + initialDelaySeconds: 300 + readinessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 120 + initialDelaySeconds: 300 + {{- end }} + env: + {{- $root := . }} + {{- range $ref, $values := .Values.secrets }} + {{- range $key, $value := $values }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ template "iothubmanager.fullname" $root }}-{{ $ref | lower }} + key: {{ $key }} + {{- end }} + {{- end }} + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/scripts/cd/iothubmanager/templates/ingress.yaml b/scripts/cd/iothubmanager/templates/ingress.yaml new file mode 100644 index 0000000..1aee9a9 --- /dev/null +++ b/scripts/cd/iothubmanager/templates/ingress.yaml @@ -0,0 +1,39 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "iothubmanager.fullname" . -}} +{{- $servicePort := .Values.service.port -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "iothubmanager.name" . }} + chart: {{ template "iothubmanager.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $ingressPath }} + backend: + serviceName: {{ $fullName }} + servicePort: http + {{- end }} +{{- end }} diff --git a/scripts/cd/iothubmanager/templates/secrets.yaml b/scripts/cd/iothubmanager/templates/secrets.yaml new file mode 100644 index 0000000..e4941d8 --- /dev/null +++ b/scripts/cd/iothubmanager/templates/secrets.yaml @@ -0,0 +1,12 @@ +{{- $root := . }} +{{- range $name, $values := .Values.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "iothubmanager.fullname" $root }}-{{ $name | lower }} +data: + {{- range $key, $value := $values }} + {{ $key }}: {{ $value | b64enc }} + {{- end }} +--- +{{- end }} diff --git a/scripts/cd/iothubmanager/templates/service.yaml b/scripts/cd/iothubmanager/templates/service.yaml new file mode 100644 index 0000000..311da16 --- /dev/null +++ b/scripts/cd/iothubmanager/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "iothubmanager.fullname" . }} + labels: + app: {{ template "iothubmanager.name" . }} + chart: {{ template "iothubmanager.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: http + selector: + app: {{ template "iothubmanager.name" . }} + release: {{ .Release.Name }} diff --git a/scripts/cd/iothubmanager/values.yaml b/scripts/cd/iothubmanager/values.yaml new file mode 100644 index 0000000..4026ca6 --- /dev/null +++ b/scripts/cd/iothubmanager/values.yaml @@ -0,0 +1,68 @@ +# Default values for iothubmanager. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +fullnameOverride: iothubmanager +replicaCount: 1 +image: + repository: azureiotpcs/iothub-manager-dotnet + tag: testing + pullPolicy: Always +imagePullSecrets: [] + # Optionally specify an array of imagePullSecrets. + # Secrets must be manually created in the namespace. + # ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod + # + # This uses credentials from secret "myRegistryKeySecretName". + # - name: myRegistryKeySecretName +service: + type: ClusterIP + externalPort: 80 + internalPort: 9002 + +ingress: + enabled: true + #annotations: + #kubernetes.io/ingress.class: addon-http-application-routing + # kubernetes.io/tls-acme: "true" + path: /iothubmanager + hosts: + - DNSNAME + # hosts: + # - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.locam +secrets: + iothubmanager: + PCS_AUTH_WEBSERVICE_URL: PCS_AUTH_WEBSERVICE_URL + PCS_IOTHUB_CONNSTRING: PCS_IOTHUB_CONNSTRING + PCS_STORAGEADAPTER_WEBSERVICE_URL: PCS_STORAGEADAPTER_WEBSERVICE_URL + PCS_AUTH_REQUIRED: PCS_AUTH_REQUIRED + # Optionally specify a set of secret objects whose values + # will be injected as environment variables by default. + # You should add this section to a file like secrets.yaml + # that is explicitly NOT committed to source code control + # and then include it as part of your helm install step. + # ref: https://kubernetes.io/docs/concepts/configuration/secret/ + # + # This creates a secret "mysecret" and injects "mypassword" + # as the environment variable mysecret_mypassword=password. + # mysecret: + # mypassword: password +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/scripts/cd/reverse-proxy/.helmignore b/scripts/cd/reverse-proxy/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/scripts/cd/reverse-proxy/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/scripts/cd/reverse-proxy/Chart.yaml b/scripts/cd/reverse-proxy/Chart.yaml new file mode 100644 index 0000000..b411223 --- /dev/null +++ b/scripts/cd/reverse-proxy/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: reverse-proxy +version: 0.1.0 diff --git a/scripts/cd/reverse-proxy/templates/NOTES.txt b/scripts/cd/reverse-proxy/templates/NOTES.txt new file mode 100644 index 0000000..54ee352 --- /dev/null +++ b/scripts/cd/reverse-proxy/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "reverse-proxy.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "reverse-proxy.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "reverse-proxy.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "reverse-proxy.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/scripts/cd/reverse-proxy/templates/_helpers.tpl b/scripts/cd/reverse-proxy/templates/_helpers.tpl new file mode 100644 index 0000000..a81b502 --- /dev/null +++ b/scripts/cd/reverse-proxy/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "reverse-proxy.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "reverse-proxy.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "reverse-proxy.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/scripts/cd/reverse-proxy/templates/deployment.yaml b/scripts/cd/reverse-proxy/templates/deployment.yaml new file mode 100644 index 0000000..621b26b --- /dev/null +++ b/scripts/cd/reverse-proxy/templates/deployment.yaml @@ -0,0 +1,79 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "reverse-proxy.fullname" . }} + labels: + app: {{ template "reverse-proxy.name" . }} + chart: {{ template "reverse-proxy.chart" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "reverse-proxy.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "reverse-proxy.name" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + annotations: + buildID: {{ .Values.buildID }} + spec: + volumes: + - name: certs-volume + configMap: + name: cert-config + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: certs-volume + mountPath: /app/certs + ports: + - name: http + containerPort: 10080 + protocol: TCP + {{- if .Values.probes.enabled }} + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http + {{- end }} + env: + {{- $root := . }} + {{- range $ref, $values := .Values.secrets }} + {{- range $key, $value := $values }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ template "reverse-proxy.fullname" $root }}-{{ $ref | lower }} + key: {{ $key }} + {{- end }} + {{- end }} + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/scripts/cd/reverse-proxy/templates/ingress.yaml b/scripts/cd/reverse-proxy/templates/ingress.yaml new file mode 100644 index 0000000..8bc7765 --- /dev/null +++ b/scripts/cd/reverse-proxy/templates/ingress.yaml @@ -0,0 +1,39 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "reverse-proxy.fullname" . -}} +{{- $servicePort := .Values.service.port -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "reverse-proxy.name" . }} + chart: {{ template "reverse-proxy.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $ingressPath }} + backend: + serviceName: {{ $fullName }} + servicePort: http + {{- end }} +{{- end }} diff --git a/scripts/cd/reverse-proxy/templates/secrets.yaml b/scripts/cd/reverse-proxy/templates/secrets.yaml new file mode 100644 index 0000000..ad7d65c --- /dev/null +++ b/scripts/cd/reverse-proxy/templates/secrets.yaml @@ -0,0 +1,12 @@ +{{- $root := . }} +{{- range $name, $values := .Values.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "reverse-proxy.fullname" $root }}-{{ $name | lower }} +data: + {{- range $key, $value := $values }} + {{ $key }}: {{ $value | b64enc }} + {{- end }} +--- +{{- end }} diff --git a/scripts/cd/reverse-proxy/templates/service.yaml b/scripts/cd/reverse-proxy/templates/service.yaml new file mode 100644 index 0000000..68df21f --- /dev/null +++ b/scripts/cd/reverse-proxy/templates/service.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "reverse-proxy.fullname" . }} + labels: + app: {{ template "reverse-proxy.name" . }} + chart: {{ template "reverse-proxy.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: http + - port: {{ .Values.service.externalHttpsPort }} + targetPort: {{ .Values.service.internalHttpsPort }} + protocol: TCP + name: https + selector: + app: {{ template "reverse-proxy.name" . }} + release: {{ .Release.Name }} diff --git a/scripts/cd/reverse-proxy/values.yaml b/scripts/cd/reverse-proxy/values.yaml new file mode 100644 index 0000000..5e66a9d --- /dev/null +++ b/scripts/cd/reverse-proxy/values.yaml @@ -0,0 +1,70 @@ +# Default values for reverse-proxy. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +fullnameOverride: reverse-proxy +replicaCount: 1 +image: + repository: azureiotpcs/remote-monitoring-nginx + tag: testing + pullPolicy: Always +imagePullSecrets: [] + # Optionally specify an array of imagePullSecrets. + # Secrets must be manually created in the namespace. + # ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod + # + # This uses credentials from secret "myRegistryKeySecretName". + # - name: myRegistryKeySecretName + # +probes: + enabled: false + +service: + type: ClusterIP + internalPort: 80 + externalPort: 10080 + internalHttpsPort: 443 + externalHttpsPort: 10443 + + +ingress: + enabled: false + #annotations: + #kubernetes.io/ingress.class: addon-http-application-routing + # kubernetes.io/tls-acme: "true" + path: / + hosts: + - rm-aks-test.eastus.cloudapp.azure.com + # hosts: + # - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local +secrets: {} + # Optionally specify a set of secret objects whose values + # will be injected as environment variables by default. + # You should add this section to a file like secrets.yaml + # that is explicitly NOT committed to source code control + # and then include it as part of your helm install step. + # ref: https://kubernetes.io/docs/concepts/configuration/secret/ + # + # This creates a secret "mysecret" and injects "mypassword" + # as the environment variable mysecret_mypassword=password. + # mysecret: + # mypassword: password +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/scripts/cd/simulation/.helmignore b/scripts/cd/simulation/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/scripts/cd/simulation/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/scripts/cd/simulation/Chart.yaml b/scripts/cd/simulation/Chart.yaml new file mode 100644 index 0000000..a054a03 --- /dev/null +++ b/scripts/cd/simulation/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: simulation +version: 0.1.0 diff --git a/scripts/cd/simulation/templates/NOTES.txt b/scripts/cd/simulation/templates/NOTES.txt new file mode 100644 index 0000000..8dc201b --- /dev/null +++ b/scripts/cd/simulation/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "simulation.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "simulation.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "simulation.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "simulation.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/scripts/cd/simulation/templates/_helpers.tpl b/scripts/cd/simulation/templates/_helpers.tpl new file mode 100644 index 0000000..2b06caa --- /dev/null +++ b/scripts/cd/simulation/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "simulation.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "simulation.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "simulation.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/scripts/cd/simulation/templates/deployment.yaml b/scripts/cd/simulation/templates/deployment.yaml new file mode 100644 index 0000000..fc05977 --- /dev/null +++ b/scripts/cd/simulation/templates/deployment.yaml @@ -0,0 +1,79 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "simulation.fullname" . }} + labels: + app: {{ template "simulation.name" . }} + chart: {{ template "simulation.chart" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "simulation.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "simulation.name" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + annotations: + buildID: {{ .Values.buildID }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.internalPort }} + protocol: TCP + {{- if .Values.probes.enabled }} + livenessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 600 + timeoutSeconds: 10 + successThreshold: 5 + initialDelaySeconds: 300 + readinessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 600 + timeoutSeconds: 10 + initialDelaySeconds: 300 + {{- end }} + env: + {{- $root := . }} + {{- range $ref, $values := .Values.secrets }} + {{- range $key, $value := $values }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ template "simulation.fullname" $root }}-{{ $ref | lower }} + key: {{ $key }} + {{- end }} + {{- end }} + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/scripts/cd/simulation/templates/ingress.yaml b/scripts/cd/simulation/templates/ingress.yaml new file mode 100644 index 0000000..2d64841 --- /dev/null +++ b/scripts/cd/simulation/templates/ingress.yaml @@ -0,0 +1,39 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "simulation.fullname" . -}} +{{- $servicePort := .Values.service.port -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "simulation.name" . }} + chart: {{ template "simulation.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $ingressPath }} + backend: + serviceName: {{ $fullName }} + servicePort: http + {{- end }} +{{- end }} diff --git a/scripts/cd/simulation/templates/secrets.yaml b/scripts/cd/simulation/templates/secrets.yaml new file mode 100644 index 0000000..2071656 --- /dev/null +++ b/scripts/cd/simulation/templates/secrets.yaml @@ -0,0 +1,12 @@ +{{- $root := . }} +{{- range $name, $values := .Values.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "simulation.fullname" $root }}-{{ $name | lower }} +data: + {{- range $key, $value := $values }} + {{ $key }}: {{ $value | b64enc }} + {{- end }} +--- +{{- end }} diff --git a/scripts/cd/simulation/templates/service.yaml b/scripts/cd/simulation/templates/service.yaml new file mode 100644 index 0000000..e970e1b --- /dev/null +++ b/scripts/cd/simulation/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "simulation.fullname" . }} + labels: + app: {{ template "simulation.name" . }} + chart: {{ template "simulation.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: http + selector: + app: {{ template "simulation.name" . }} + release: {{ .Release.Name }} diff --git a/scripts/cd/simulation/values.yaml b/scripts/cd/simulation/values.yaml new file mode 100644 index 0000000..190d4b4 --- /dev/null +++ b/scripts/cd/simulation/values.yaml @@ -0,0 +1,72 @@ +# Default values for simulation. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +fullnameOverride: simulation +replicaCount: 1 +image: + repository: azureiotpcs/device-simulation-dotnet + tag: DS-1.0.2 + pullPolicy: Always +imagePullSecrets: [] + # Optionally specify an array of imagePullSecrets. + # Secrets must be manually created in the namespace. + # ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod + # + # This uses credentials from secret "myRegistryKeySecretName". + # - name: myRegistryKeySecretName +service: + type: ClusterIP + externalPort: 80 + internalPort: 9003 + +ingress: + enabled: true + #annotations: + #kubernetes.io/ingress.class: addon-http-application-routing + # kubernetes.io/tls-acme: "true" + path: /simulation + hosts: + - DNSNAME + # hosts: + # - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local +secrets: + simulation: + PCS_IOTHUB_CONNSTRING: PCS_IOTHUB_CONNSTRING + PCS_STORAGEADAPTER_WEBSERVICE_URL: PCS_STORAGEADAPTER_WEBSERVICE_URL + PCS_AUTH_REQUIRED: PCS_AUTH_REQUIRED + # Optionally specify a set of secret objects whose values + # will be injected as environment variables by default. + # You should add this section to a file like secrets.yaml + # that is explicitly NOT committed to source code control + # and then include it as part of your helm install step. + # ref: https://kubernetes.io/docs/concepts/configuration/secret/ + # + # This creates a secret "mysecret" and injects "mypassword" + # as the environment variable mysecret_mypassword=password. + # mysecret: + # mypassword: password +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + # +probes: + enabled: false + + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/scripts/cd/storageadapter/.helmignore b/scripts/cd/storageadapter/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/scripts/cd/storageadapter/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/scripts/cd/storageadapter/Chart.yaml b/scripts/cd/storageadapter/Chart.yaml new file mode 100644 index 0000000..9da3a58 --- /dev/null +++ b/scripts/cd/storageadapter/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: storageadapter +version: 0.1.0 diff --git a/scripts/cd/storageadapter/templates/NOTES.txt b/scripts/cd/storageadapter/templates/NOTES.txt new file mode 100644 index 0000000..81fe19d --- /dev/null +++ b/scripts/cd/storageadapter/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "storageadapter.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "storageadapter.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "storageadapter.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "storageadapter.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/scripts/cd/storageadapter/templates/_helpers.tpl b/scripts/cd/storageadapter/templates/_helpers.tpl new file mode 100644 index 0000000..8d82073 --- /dev/null +++ b/scripts/cd/storageadapter/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "storageadapter.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "storageadapter.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "storageadapter.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/scripts/cd/storageadapter/templates/deployment.yaml b/scripts/cd/storageadapter/templates/deployment.yaml new file mode 100644 index 0000000..263bc09 --- /dev/null +++ b/scripts/cd/storageadapter/templates/deployment.yaml @@ -0,0 +1,77 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "storageadapter.fullname" . }} + labels: + app: {{ template "storageadapter.name" . }} + chart: {{ template "storageadapter.chart" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "storageadapter.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "storageadapter.name" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + annotations: + buildID: {{ .Values.buildID }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.internalPort }} + protocol: TCP + {{- if not .Values.disableProbes }} + livenessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 120 + initialDelaySeconds: 300 + readinessProbe: + httpGet: + path: /v1/status + port: http + periodSeconds: 120 + initialDelaySeconds: 300 + {{- end }} + env: + {{- $root := . }} + {{- range $ref, $values := .Values.secrets }} + {{- range $key, $value := $values }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ template "storageadapter.fullname" $root }}-{{ $ref | lower }} + key: {{ $key }} + {{- end }} + {{- end }} + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} + diff --git a/scripts/cd/storageadapter/templates/ingress.yaml b/scripts/cd/storageadapter/templates/ingress.yaml new file mode 100644 index 0000000..ecffd1b --- /dev/null +++ b/scripts/cd/storageadapter/templates/ingress.yaml @@ -0,0 +1,39 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "storageadapter.fullname" . -}} +{{- $servicePort := .Values.service.externalPort -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "storageadapter.name" . }} + chart: {{ template "storageadapter.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $ingressPath }} + backend: + serviceName: {{ $fullName }} + servicePort: {{ $servicePort }} + {{- end }} +{{- end }} diff --git a/scripts/cd/storageadapter/templates/secrets.yam1 b/scripts/cd/storageadapter/templates/secrets.yam1 new file mode 100644 index 0000000..761869d --- /dev/null +++ b/scripts/cd/storageadapter/templates/secrets.yam1 @@ -0,0 +1,12 @@ +{{- $root := . }} +{{- range $name, $values := .Values.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "storageadapter.fullname" $root }}-{{ $name | lower }} +data: + {{- range $key, $value := $values }} + {{ $key }}: {{ $value | b64enc }} + {{- end }} +--- +{{- end }} diff --git a/scripts/cd/storageadapter/templates/secrets.yaml b/scripts/cd/storageadapter/templates/secrets.yaml new file mode 100644 index 0000000..761869d --- /dev/null +++ b/scripts/cd/storageadapter/templates/secrets.yaml @@ -0,0 +1,12 @@ +{{- $root := . }} +{{- range $name, $values := .Values.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "storageadapter.fullname" $root }}-{{ $name | lower }} +data: + {{- range $key, $value := $values }} + {{ $key }}: {{ $value | b64enc }} + {{- end }} +--- +{{- end }} diff --git a/scripts/cd/storageadapter/templates/service.yaml b/scripts/cd/storageadapter/templates/service.yaml new file mode 100644 index 0000000..e14b471 --- /dev/null +++ b/scripts/cd/storageadapter/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "storageadapter.fullname" . }} + labels: + app: {{ template "storageadapter.name" . }} + chart: {{ template "storageadapter.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: http + selector: + app: {{ template "storageadapter.name" . }} + release: {{ .Release.Name }} diff --git a/scripts/cd/storageadapter/values.yaml b/scripts/cd/storageadapter/values.yaml new file mode 100644 index 0000000..40d126c --- /dev/null +++ b/scripts/cd/storageadapter/values.yaml @@ -0,0 +1,68 @@ +# Default values for storageadapter. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +fullnameOverride: storageadapter +replicaCount: 1 +image: + repository: azureiotpcs/pcs-storage-adapter-dotnet + tag: testing + pullPolicy: Always +imagePullSecrets: [] + # Optionally specify an array of imagePullSecrets. + # Secrets must be manually created in the namespace. + # ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod + # + # This uses credentials from secret "myRegistryKeySecretName". + # - name: myRegistryKeySecretName +service: + type: ClusterIP + externalPort: 80 + internalPort: 9022 + + #disableProbes: + #enabled: false + +ingress: + enabled: true + #annotations: + #kubernetes.io/ingress.class: addon-http-application-routing + # kubernetes.io/tls-acme: "true" + path: /storageadapter + hosts: + - DNSNAME + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local +secrets: + storageadapter: + PCS_STORAGEADAPTER_DOCUMENTDB_CONNSTRING: PCS_STORAGEADAPTER_DOCUMENTDB_CONNSTRING + PCS_AUTH_REQUIRED: PCS_AUTH_REQUIRED + # Optionally specify a set of secret objects whose values + # will be injected as environment variables by default. + # You should add this section to a file like secrets.yaml + # that is explicitly NOT committed to source code control + # and then include it as part of your helm install step. + # ref: https://kubernetes.io/docs/concepts/configuration/secret/ + # + # This creates a secret "mysecret" and injects "mypassword" + # as the environment variable mysecret_mypassword=password. + # mysecret: + # mypassword: password +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi +nodeSelector: {} + +tolerations: [] + +affinity: {} + diff --git a/scripts/cd/webui/.helmignore b/scripts/cd/webui/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/scripts/cd/webui/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/scripts/cd/webui/Chart.yaml b/scripts/cd/webui/Chart.yaml new file mode 100644 index 0000000..b6ac62b --- /dev/null +++ b/scripts/cd/webui/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: webui +version: 0.1.0 diff --git a/scripts/cd/webui/templates/NOTES.txt b/scripts/cd/webui/templates/NOTES.txt new file mode 100644 index 0000000..205633d --- /dev/null +++ b/scripts/cd/webui/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "webui.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "webui.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "webui.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "webui.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/scripts/cd/webui/templates/_helpers.tpl b/scripts/cd/webui/templates/_helpers.tpl new file mode 100644 index 0000000..41a229c --- /dev/null +++ b/scripts/cd/webui/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "webui.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "webui.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "webui.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/scripts/cd/webui/templates/deployment.yaml b/scripts/cd/webui/templates/deployment.yaml new file mode 100644 index 0000000..6eabdd4 --- /dev/null +++ b/scripts/cd/webui/templates/deployment.yaml @@ -0,0 +1,74 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "webui.fullname" . }} + labels: + app: {{ template "webui.name" . }} + chart: {{ template "webui.chart" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "webui.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "webui.name" . }} + draft: {{ default "draft-app" .Values.draft }} + release: {{ .Release.Name }} + annotations: + buildID: {{ .Values.buildID }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.internalPort }} + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + periodSeconds: 120 + initialDelaySeconds: 300 + readinessProbe: + httpGet: + path: / + port: http + periodSeconds: 120 + initialDelaySeconds: 300 + env: + {{- $root := . }} + {{- range $ref, $values := .Values.secrets }} + {{- range $key, $value := $values }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ template "webui.fullname" $root }}-{{ $ref | lower }} + key: {{ $key }} + {{- end }} + {{- end }} + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/scripts/cd/webui/templates/ingress.yaml b/scripts/cd/webui/templates/ingress.yaml new file mode 100644 index 0000000..1906d50 --- /dev/null +++ b/scripts/cd/webui/templates/ingress.yaml @@ -0,0 +1,39 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "webui.fullname" . -}} +{{- $servicePort := .Values.service.port -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "webui.name" . }} + chart: {{ template "webui.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $ingressPath }} + backend: + serviceName: {{ $fullName }} + servicePort: http + {{- end }} +{{- end }} diff --git a/scripts/cd/webui/templates/secrets.yaml b/scripts/cd/webui/templates/secrets.yaml new file mode 100644 index 0000000..0f81095 --- /dev/null +++ b/scripts/cd/webui/templates/secrets.yaml @@ -0,0 +1,12 @@ +{{- $root := . }} +{{- range $name, $values := .Values.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "webui.fullname" $root }}-{{ $name | lower }} +data: + {{- range $key, $value := $values }} + {{ $key }}: {{ $value | b64enc }} + {{- end }} +--- +{{- end }} diff --git a/scripts/cd/webui/templates/service.yaml b/scripts/cd/webui/templates/service.yaml new file mode 100644 index 0000000..b0d4667 --- /dev/null +++ b/scripts/cd/webui/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "webui.fullname" . }} + labels: + app: {{ template "webui.name" . }} + chart: {{ template "webui.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: http + selector: + app: {{ template "webui.name" . }} + release: {{ .Release.Name }} diff --git a/scripts/cd/webui/values.yaml b/scripts/cd/webui/values.yaml new file mode 100644 index 0000000..0887960 --- /dev/null +++ b/scripts/cd/webui/values.yaml @@ -0,0 +1,65 @@ +# Default values for webui. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +fullnameOverride: webui +replicaCount: 1 +image: + repository: azureiotpcs/pcs-remote-monitoring-webui + tag: testing + pullPolicy: Always +imagePullSecrets: [] + # Optionally specify an array of imagePullSecrets. + # Secrets must be manually created in the namespace. + # ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod + # + # This uses credentials from secret "myRegistryKeySecretName". + # - name: myRegistryKeySecretName +service: + type: ClusterIP + internalPort: 10080 + externalPort: 10080 + +ingress: + enabled: true + #annotations: + #kubernetes.io/ingress.class: addon-http-application-routing + # kubernetes.io/tls-acme: "true" + path: /webui + hosts: + - DNSNAME + # hosts: + # - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local +secrets: + webui: + REACT_APP_BASE_SERVICE_URL: REACT_APP_BASE_SERVICE_URL + # Optionally specify a set of secret objects whose values + # will be injected as environment variables by default. + # You should add this section to a file like secrets.yaml + # that is explicitly NOT committed to source code control + # and then include it as part of your helm install step. + # ref: https://kubernetes.io/docs/concepts/configuration/secret/ + # + # This creates a secret "mysecret" and injects "mypassword" + # as the environment variable mysecret_mypassword=password. + # mysecret: + # mypassword: password +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/scripts/vsts/buildimages.sh b/scripts/ci/vsts/buildimages.sh similarity index 100% rename from scripts/vsts/buildimages.sh rename to scripts/ci/vsts/buildimages.sh diff --git a/scripts/vsts/buildservices.sh b/scripts/ci/vsts/buildservices.sh similarity index 100% rename from scripts/vsts/buildservices.sh rename to scripts/ci/vsts/buildservices.sh diff --git a/scripts/vsts/identifymstobuild.sh b/scripts/ci/vsts/identifymstobuild.sh similarity index 100% rename from scripts/vsts/identifymstobuild.sh rename to scripts/ci/vsts/identifymstobuild.sh diff --git a/scripts/vsts/imagestobuild b/scripts/ci/vsts/imagestobuild similarity index 100% rename from scripts/vsts/imagestobuild rename to scripts/ci/vsts/imagestobuild diff --git a/scripts/vsts/tagdockerimages.sh b/scripts/ci/vsts/tagdockerimages.sh similarity index 100% rename from scripts/vsts/tagdockerimages.sh rename to scripts/ci/vsts/tagdockerimages.sh diff --git a/scripts/createsolution b/scripts/local/createsolution similarity index 100% rename from scripts/createsolution rename to scripts/local/createsolution From aba30d22f3771622966a130f69d99b4d419ef11e Mon Sep 17 00:00:00 2001 From: Jill Bender Date: Tue, 15 Jan 2019 12:41:14 -0800 Subject: [PATCH 06/19] validate input for alarms and remove redundant checks --- device-telemetry/Services/Alarms.cs | 40 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/device-telemetry/Services/Alarms.cs b/device-telemetry/Services/Alarms.cs index 7295448..98c5c5a 100644 --- a/device-telemetry/Services/Alarms.cs +++ b/device-telemetry/Services/Alarms.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using System.Net; using System.Text.RegularExpressions; using System.Threading; @@ -90,11 +88,6 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services public Alarm Get(string id) { - if (Regex.IsMatch(id, INVALID_CHARACTER)) - { - throw new InvalidInputException("id contains illegal characters."); - } - Document doc = this.GetDocumentById(id); return new Alarm(doc); } @@ -160,11 +153,6 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services limit, devices, DEVICE_ID_KEY); - if (Regex.IsMatch(id, INVALID_CHARACTER)) - { - throw new InvalidInputException("id contains illegal characters."); - } - this.log.Debug("Created Alarm By Rule Query", () => new { sql }); FeedOptions queryOptions = new FeedOptions(); @@ -220,10 +208,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services public async Task UpdateAsync(string id, string status) { - if (Regex.IsMatch(id, INVALID_CHARACTER)) - { - throw new InvalidInputException("id contains illegal characters."); - } + ValidateInput(id); Document document = this.GetDocumentById(id); document.SetPropertyValue(STATUS_KEY, status); @@ -238,10 +223,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services private Document GetDocumentById(string id) { - if (Regex.IsMatch(id, INVALID_CHARACTER)) - { - throw new InvalidInputException("id contains illegal characters."); - } + ValidateInput(id); var query = new SqlQuerySpec( "SELECT * FROM c WHERE c.id=@id", @@ -268,6 +250,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services public async Task Delete(List ids) { + foreach(var id in ids) + { + ValidateInput(id); + } + Task[] taskList = new Task[ids.Count]; for (int i = 0; i < ids.Count; i++) { @@ -292,6 +279,8 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services */ public async Task DeleteAsync(string id) { + ValidateInput(id); + int retryCount = 0; while (retryCount < this.maxDeleteRetryCount) { @@ -328,5 +317,16 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services } } } + + // Check illegal characters in input + private static void ValidateInput(string input) + { + input = input.Trim(); + + if (Regex.IsMatch(input, INVALID_CHARACTER)) + { + throw new InvalidInputException($"Input '{input}' contains invalid characters."); + } + } } } From c27026248c58866ba589b4785c4f0e48969a32e8 Mon Sep 17 00:00:00 2001 From: Jill Bender Date: Tue, 15 Jan 2019 13:29:57 -0800 Subject: [PATCH 07/19] Add tests for alarm input validation --- device-telemetry/Services.Test/AlarmsTest.cs | 20 +++++++++++++++++++ device-telemetry/Services/Alarms.cs | 1 + .../Services/Helpers/QueryBuilder.cs | 5 +++++ 3 files changed, 26 insertions(+) diff --git a/device-telemetry/Services.Test/AlarmsTest.cs b/device-telemetry/Services.Test/AlarmsTest.cs index b5365a3..8a501cc 100644 --- a/device-telemetry/Services.Test/AlarmsTest.cs +++ b/device-telemetry/Services.Test/AlarmsTest.cs @@ -129,5 +129,25 @@ namespace Services.Test this.logger.Verify(l => l.Error(It.IsAny(), It.IsAny>()), Times.Once); this.logger.Verify(l => l.Warn(It.IsAny(), It.IsAny>()), Times.Exactly(2)); } + + [Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)] + public async Task ThrowsOnInvalidInput() + { + // Arrange + var xssString = ""; + var xssList = new List + { + "", + "" + }; + + // Act & Assert + await Assert.ThrowsAsync(async () => await this.alarms.DeleteAsync(xssString)); + await Assert.ThrowsAsync(async () => await this.alarms.Delete(xssList)); + await Assert.ThrowsAsync(async () => await this.alarms.UpdateAsync(xssString, xssString)); + Assert.Throws(() => this.alarms.GetCountByRule(xssString, DateTimeOffset.MaxValue, DateTimeOffset.MaxValue, xssList.ToArray())); + Assert.Throws(() => this.alarms.List(null, null, xssString, 0, 1, xssList.ToArray())); + Assert.Throws(() => this.alarms.ListByRule(xssString, DateTimeOffset.MaxValue, DateTimeOffset.MaxValue, xssString, 0, 1, xssList.ToArray())); + } } } diff --git a/device-telemetry/Services/Alarms.cs b/device-telemetry/Services/Alarms.cs index 98c5c5a..cc2f3c3 100644 --- a/device-telemetry/Services/Alarms.cs +++ b/device-telemetry/Services/Alarms.cs @@ -209,6 +209,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services public async Task UpdateAsync(string id, string status) { ValidateInput(id); + ValidateInput(status); Document document = this.GetDocumentById(id); document.SetPropertyValue(STATUS_KEY, status); diff --git a/device-telemetry/Services/Helpers/QueryBuilder.cs b/device-telemetry/Services/Helpers/QueryBuilder.cs index 2bfa752..6417dff 100644 --- a/device-telemetry/Services/Helpers/QueryBuilder.cs +++ b/device-telemetry/Services/Helpers/QueryBuilder.cs @@ -32,7 +32,12 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Helpers ValidateInput(ref schemaName); ValidateInput(ref fromProperty); ValidateInput(ref toProperty); + ValidateInput(ref order); ValidateInput(ref orderProperty); + for (int i = 0; i < devices.Length; i++) + { + ValidateInput(ref devices[i]); + } ValidateInput(ref devicesProperty); var sqlParameterCollection = new SqlParameterCollection(); From db7e9650a402925a0c273bac1d3b29dec068ef24 Mon Sep 17 00:00:00 2001 From: Jill Bender Date: Tue, 15 Jan 2019 13:59:57 -0800 Subject: [PATCH 08/19] Validate rules input and add tests for invalid input --- device-telemetry/Services.Test/RulesTest.cs | 53 +++++++++++++++++++++ device-telemetry/Services/Models/Rule.cs | 26 ++++++++++ device-telemetry/Services/Rules.cs | 31 ++++++++++-- 3 files changed, 105 insertions(+), 5 deletions(-) diff --git a/device-telemetry/Services.Test/RulesTest.cs b/device-telemetry/Services.Test/RulesTest.cs index b22e0ca..8e98464 100644 --- a/device-telemetry/Services.Test/RulesTest.cs +++ b/device-telemetry/Services.Test/RulesTest.cs @@ -405,6 +405,59 @@ namespace Services.Test this.httpClientMock.Verify(x => x.PostAsync(It.IsAny()), Times.Exactly(4)); } + [Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)] + public async Task ThrowsOnInvalidInput() + { + // Arrange + var xssString = ""; + var xssList = new List + { + "", + "" + }; + + var rule = new Rule() + { + ETag = xssString, + Id = xssString, + Name = xssString, + DateCreated = xssString, + DateModified = xssString, + Enabled = true, + Description = xssString, + GroupId = xssString, + Severity = SeverityType.Critical, + Conditions = new List + { + new Condition() + { + Field = "sample_conddition", + Operator = OperatorType.Equals, + Value = "1" + } + }, + Actions = new List + { + new EmailAction( + new Dictionary + { + { "recipients", new Newtonsoft.Json.Linq.JArray(){ "sampleEmail@gmail.com", "sampleEmail2@gmail.com" } }, + { "subject", "Test Email" }, + { "notes", "Test Email Notes." } + }) + } + }; + + // Act & Assert + await Assert.ThrowsAsync(async () => await this.rules.DeleteAsync(xssString)); + await Assert.ThrowsAsync(async () => await this.rules.DeleteAsync(xssString)); + await Assert.ThrowsAsync(async () => await this.rules.GetAsync(xssString)); + await Assert.ThrowsAsync(async () => await this.rules.GetListAsync(xssString, 0, 1, xssString, false)); + await Assert.ThrowsAsync(async () => await this.rules.GetAlarmCountForListAsync(null, null, xssString, 0, LIMIT, xssList.ToArray())); + await Assert.ThrowsAsync(async () => await this.rules.CreateAsync(rule)); + await Assert.ThrowsAsync(async () => await this.rules.UpsertIfNotDeletedAsync(rule)); + } + private void ThereAreNoRulessInStorage() { this.rulesMock.Setup(x => x.GetListAsync(null, 0, LIMIT, null, false)) diff --git a/device-telemetry/Services/Models/Rule.cs b/device-telemetry/Services/Models/Rule.cs index 6b01372..7e79b4d 100644 --- a/device-telemetry/Services/Models/Rule.cs +++ b/device-telemetry/Services/Models/Rule.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; +using System.Text.RegularExpressions; +using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Exceptions; using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Models.Actions; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -11,6 +13,8 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Models public class Rule : IComparable { private const string DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:sszzz"; + private const string INVALID_CHARACTER = @"[^A-Za-z0-9:;.,_\-]"; + // Comes from the StorageAdapter document and not the serialized rule [JsonIgnore] public string ETag { get; set; } = string.Empty; @@ -41,6 +45,28 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Models return DateTimeOffset.Parse(other.DateCreated) .CompareTo(DateTimeOffset.Parse(this.DateCreated)); } + + public void Validate() + { + ValidateInput(this.ETag); + ValidateInput(this.Id); + ValidateInput(this.Name); + ValidateInput(this.DateCreated); + ValidateInput(this.DateModified); + ValidateInput(this.Description); + ValidateInput(this.GroupId); + } + + // Check illegal characters in input + private static void ValidateInput(string input) + { + input = input.Trim(); + + if (Regex.IsMatch(input, INVALID_CHARACTER)) + { + throw new InvalidInputException($"Input '{input}' contains invalid characters."); + } + } } public enum CalculationType diff --git a/device-telemetry/Services/Rules.cs b/device-telemetry/Services/Rules.cs index d011aa9..f845287 100644 --- a/device-telemetry/Services/Rules.cs +++ b/device-telemetry/Services/Rules.cs @@ -91,6 +91,8 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services public async Task DeleteAsync(string id) { + ValidateInput(id); + Rule existing; try { @@ -125,11 +127,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services public async Task GetAsync(string id) { - if (Regex.IsMatch(id, INVALID_CHARACTER)) - { - this.log.Debug("id contains illegal characters.", () => new { id }); - throw new InvalidInputException("id contains illegal characters."); - } + ValidateInput(id); var item = await this.storage.GetAsync(STORAGE_COLLECTION, id); var rule = this.Deserialize(item.Data); @@ -147,6 +145,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services string groupId, bool includeDeleted) { + ValidateInput(order); + ValidateInput(groupId); + var data = await this.storage.GetAllAsync(STORAGE_COLLECTION); var ruleList = new List(); foreach (var item in data.Items) @@ -205,6 +206,12 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services int limit, string[] devices) { + ValidateInput(order); + foreach (var device in devices) + { + ValidateInput(device); + } + var alarmCountByRuleList = new List(); // get list of rules @@ -246,6 +253,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services { throw new InvalidInputException("Rule not provided."); } + rule.Validate(); // Ensure dates are correct rule.DateCreated = DateTimeOffset.UtcNow.ToString(DATE_FORMAT); @@ -265,6 +273,8 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services public async Task UpsertIfNotDeletedAsync(Rule rule) { + rule.Validate(); + if (rule == null) { throw new InvalidInputException("Rule not provided."); @@ -389,5 +399,16 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services return ruleCount; } + + // Check illegal characters in input + private static void ValidateInput(string input) + { + input = input.Trim(); + + if (Regex.IsMatch(input, INVALID_CHARACTER)) + { + throw new InvalidInputException($"Input '{input}' contains invalid characters."); + } + } } } From a61af06788fbd095e48ea439691d4d2fe866de31 Mon Sep 17 00:00:00 2001 From: Elvin Morales Date: Tue, 15 Jan 2019 14:05:28 -0800 Subject: [PATCH 09/19] Retrigger failed build --- iothub-manager/RecurringTasksAgent/Agent.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/iothub-manager/RecurringTasksAgent/Agent.cs b/iothub-manager/RecurringTasksAgent/Agent.cs index 36a0913..9528f1c 100644 --- a/iothub-manager/RecurringTasksAgent/Agent.cs +++ b/iothub-manager/RecurringTasksAgent/Agent.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft. All rights reserved. + +// Copyright (c) Microsoft. All rights reserved. using System; using System.Threading; @@ -22,7 +23,7 @@ namespace Microsoft.Azure.IoTSolutions.IotHubManager.RecurringTasksAgent // When generating the cache, allow some time to finish, at least one minute private const int CACHE_TIMEOUT_SECS = 90; - + private readonly IDeviceProperties deviceProperties; private readonly ILogger log; private Timer cacheUpdateTimer; @@ -72,7 +73,7 @@ namespace Microsoft.Azure.IoTSolutions.IotHubManager.RecurringTasksAgent null, 1000 * CACHE_UPDATE_SECS, Timeout.Infinite); - this.log.Info("DeviceProperties Cache update scheduled.", () => new { CACHE_UPDATE_SECS }); + this.log.Info("DeviceProperties Cache update scheduled", () => new { CACHE_UPDATE_SECS }); } catch (Exception e) { From b0e1b78b71ed50344b511fcf8f77fc1b7e0509f4 Mon Sep 17 00:00:00 2001 From: Jill Bender Date: Tue, 15 Jan 2019 16:03:22 -0800 Subject: [PATCH 10/19] Add common method for input validation, add tests for messages --- device-telemetry/Services.Test/AlarmsTest.cs | 3 -- .../Services.Test/MessagesTest.cs | 46 ++++++++++++++++--- device-telemetry/Services.Test/RulesTest.cs | 1 - device-telemetry/Services/Alarms.cs | 24 ++-------- .../Services/Helpers/InputValidator.cs | 21 +++++++++ device-telemetry/Services/Messages.cs | 8 +++- device-telemetry/Services/Models/Rule.cs | 29 ++++-------- device-telemetry/Services/Rules.cs | 25 +++------- 8 files changed, 87 insertions(+), 70 deletions(-) create mode 100644 device-telemetry/Services/Helpers/InputValidator.cs diff --git a/device-telemetry/Services.Test/AlarmsTest.cs b/device-telemetry/Services.Test/AlarmsTest.cs index 8a501cc..65273bc 100644 --- a/device-telemetry/Services.Test/AlarmsTest.cs +++ b/device-telemetry/Services.Test/AlarmsTest.cs @@ -2,9 +2,6 @@ using System; using System.Collections.Generic; -using System.Net; -using System.Net.Http.Headers; -using System.Reflection; using System.Threading.Tasks; using Microsoft.Azure.Documents; using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services; diff --git a/device-telemetry/Services.Test/MessagesTest.cs b/device-telemetry/Services.Test/MessagesTest.cs index ed83536..bc6c3e4 100644 --- a/device-telemetry/Services.Test/MessagesTest.cs +++ b/device-telemetry/Services.Test/MessagesTest.cs @@ -4,7 +4,12 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services; +using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Diagnostics; +using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Exceptions; using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Models; +using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Runtime; +using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Storage.CosmosDB; +using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Storage.TimeSeries; using Moq; using Newtonsoft.Json.Linq; using Services.Test.helpers; @@ -17,11 +22,23 @@ namespace Services.Test private const int SKIP = 0; private const int LIMIT = 1000; - private readonly Mock messages; + private readonly Mock storageClient; + private readonly Mock timeSeriesClient; + private readonly Mock logger; + + private readonly IMessages messages; public MessagesTest() { - this.messages = new Mock(); + var servicesConfig = new ServicesConfig() + { + MessagesConfig = new StorageConfig("database", "collection"), + StorageType = "tsi" + }; + this.storageClient = new Mock(); + this.timeSeriesClient = new Mock(); + this.logger = new Mock(); + this.messages = new Messages(servicesConfig, this.storageClient.Object, this.timeSeriesClient.Object, this.logger.Object); } [Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)] @@ -29,9 +46,10 @@ namespace Services.Test { // Arrange this.ThereAreNoMessagesInStorage(); + var devices = new string[] { "device1" }; // Act - var list = await this.messages.Object.ListAsync(null, null, null, SKIP, LIMIT, null); + var list = await this.messages.ListAsync(null, null, "asc", SKIP, LIMIT, devices); // Assert Assert.Empty(list.Messages); @@ -43,18 +61,34 @@ namespace Services.Test { // Arrange this.ThereAreSomeMessagesInStorage(); + var devices = new string[] { "device1" }; // Act - var list = await this.messages.Object.ListAsync(null, null, null, SKIP, LIMIT, null); + var list = await this.messages.ListAsync(null, null, "asc", SKIP, LIMIT, devices); // Assert Assert.NotEmpty(list.Messages); Assert.NotEmpty(list.Properties); } + [Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)] + public async Task ThrowsOnInvalidInput() + { + // Arrange + var xssString = ""; + var xssList = new List + { + "", + "" + }; + + // Act & Assert + await Assert.ThrowsAsync(async () => await this.messages.ListAsync(null, null, xssString, 0, LIMIT, xssList.ToArray())); + } + private void ThereAreNoMessagesInStorage() { - this.messages.Setup(x => x.ListAsync(null, null, null, SKIP, LIMIT, null)) + this.timeSeriesClient.Setup(x => x.QueryEventsAsync(null, null, It.IsAny(), SKIP, LIMIT, It.IsAny())) .ReturnsAsync(new MessageList()); } @@ -75,7 +109,7 @@ namespace Services.Test sampleProperties.Add("data.sample_unit"); sampleProperties.Add("data.sample_speed"); - this.messages.Setup(x => x.ListAsync(null, null, null, SKIP, LIMIT, null)) + this.timeSeriesClient.Setup(x => x.QueryEventsAsync(null, null, It.IsAny(), SKIP, LIMIT, It.IsAny())) .ReturnsAsync(new MessageList(sampleMessages, sampleProperties)); } } diff --git a/device-telemetry/Services.Test/RulesTest.cs b/device-telemetry/Services.Test/RulesTest.cs index 8e98464..9adfc45 100644 --- a/device-telemetry/Services.Test/RulesTest.cs +++ b/device-telemetry/Services.Test/RulesTest.cs @@ -18,7 +18,6 @@ using Moq; using Newtonsoft.Json; using Services.Test.helpers; using Xunit; -using Type = System.Type; namespace Services.Test { diff --git a/device-telemetry/Services/Alarms.cs b/device-telemetry/Services/Alarms.cs index cc2f3c3..221890d 100644 --- a/device-telemetry/Services/Alarms.cs +++ b/device-telemetry/Services/Alarms.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Net; -using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Documents; @@ -53,8 +52,6 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services public class Alarms : IAlarms { - private const string INVALID_CHARACTER = @"[^A-Za-z0-9:;.,_\-]"; - private readonly ILogger log; private readonly IStorageClient storageClient; @@ -208,8 +205,8 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services public async Task UpdateAsync(string id, string status) { - ValidateInput(id); - ValidateInput(status); + InputValidator.Validate(id); + InputValidator.Validate(status); Document document = this.GetDocumentById(id); document.SetPropertyValue(STATUS_KEY, status); @@ -224,7 +221,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services private Document GetDocumentById(string id) { - ValidateInput(id); + InputValidator.Validate(id); var query = new SqlQuerySpec( "SELECT * FROM c WHERE c.id=@id", @@ -253,7 +250,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services { foreach(var id in ids) { - ValidateInput(id); + InputValidator.Validate(id); } Task[] taskList = new Task[ids.Count]; @@ -280,7 +277,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services */ public async Task DeleteAsync(string id) { - ValidateInput(id); + InputValidator.Validate(id); int retryCount = 0; while (retryCount < this.maxDeleteRetryCount) @@ -318,16 +315,5 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services } } } - - // Check illegal characters in input - private static void ValidateInput(string input) - { - input = input.Trim(); - - if (Regex.IsMatch(input, INVALID_CHARACTER)) - { - throw new InvalidInputException($"Input '{input}' contains invalid characters."); - } - } } } diff --git a/device-telemetry/Services/Helpers/InputValidator.cs b/device-telemetry/Services/Helpers/InputValidator.cs new file mode 100644 index 0000000..e349097 --- /dev/null +++ b/device-telemetry/Services/Helpers/InputValidator.cs @@ -0,0 +1,21 @@ +using System.Text.RegularExpressions; +using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Exceptions; + +namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Helpers +{ + public class InputValidator + { + private const string INVALID_CHARACTER = @"[^A-Za-z0-9:;.,_\-]"; + + // Check illegal characters in input + public static void Validate(string input) + { + input = input.Trim(); + + if (Regex.IsMatch(input, INVALID_CHARACTER)) + { + throw new InvalidInputException($"Input '{input}' contains invalid characters."); + } + } + } +} diff --git a/device-telemetry/Services/Messages.cs b/device-telemetry/Services/Messages.cs index 140e41c..37efa0f 100644 --- a/device-telemetry/Services/Messages.cs +++ b/device-telemetry/Services/Messages.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. using System; -using System.Linq; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Client; @@ -68,6 +68,12 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services int limit, string[] devices) { + InputValidator.Validate(order); + foreach (var device in devices) + { + InputValidator.Validate(device); + } + return this.timeSeriesEnabled ? await this.GetListFromTimeSeriesAsync(from, to, order, skip, limit, devices) : this.GetListFromCosmosDb(from, to, order, skip, limit, devices); diff --git a/device-telemetry/Services/Models/Rule.cs b/device-telemetry/Services/Models/Rule.cs index 7e79b4d..c0cdc62 100644 --- a/device-telemetry/Services/Models/Rule.cs +++ b/device-telemetry/Services/Models/Rule.cs @@ -2,8 +2,7 @@ using System; using System.Collections.Generic; -using System.Text.RegularExpressions; -using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Exceptions; +using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Helpers; using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Models.Actions; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -13,7 +12,6 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Models public class Rule : IComparable { private const string DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:sszzz"; - private const string INVALID_CHARACTER = @"[^A-Za-z0-9:;.,_\-]"; // Comes from the StorageAdapter document and not the serialized rule [JsonIgnore] @@ -48,24 +46,13 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Models public void Validate() { - ValidateInput(this.ETag); - ValidateInput(this.Id); - ValidateInput(this.Name); - ValidateInput(this.DateCreated); - ValidateInput(this.DateModified); - ValidateInput(this.Description); - ValidateInput(this.GroupId); - } - - // Check illegal characters in input - private static void ValidateInput(string input) - { - input = input.Trim(); - - if (Regex.IsMatch(input, INVALID_CHARACTER)) - { - throw new InvalidInputException($"Input '{input}' contains invalid characters."); - } + InputValidator.Validate(this.ETag); + InputValidator.Validate(this.Id); + InputValidator.Validate(this.Name); + InputValidator.Validate(this.DateCreated); + InputValidator.Validate(this.DateModified); + InputValidator.Validate(this.Description); + InputValidator.Validate(this.GroupId); } } diff --git a/device-telemetry/Services/Rules.cs b/device-telemetry/Services/Rules.cs index f845287..1c5cc2c 100644 --- a/device-telemetry/Services/Rules.cs +++ b/device-telemetry/Services/Rules.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Reflection; -using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Diagnostics; using Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Exceptions; @@ -49,7 +48,6 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services public class Rules : IRules { private const string STORAGE_COLLECTION = "rules"; - private const string INVALID_CHARACTER = @"[^A-Za-z0-9:;.,_\-]"; private const string DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:sszzz"; private readonly IStorageAdapterClient storage; @@ -91,7 +89,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services public async Task DeleteAsync(string id) { - ValidateInput(id); + InputValidator.Validate(id); Rule existing; try @@ -127,7 +125,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services public async Task GetAsync(string id) { - ValidateInput(id); + InputValidator.Validate(id); var item = await this.storage.GetAsync(STORAGE_COLLECTION, id); var rule = this.Deserialize(item.Data); @@ -145,8 +143,8 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services string groupId, bool includeDeleted) { - ValidateInput(order); - ValidateInput(groupId); + InputValidator.Validate(order); + InputValidator.Validate(groupId); var data = await this.storage.GetAllAsync(STORAGE_COLLECTION); var ruleList = new List(); @@ -206,10 +204,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services int limit, string[] devices) { - ValidateInput(order); + InputValidator.Validate(order); foreach (var device in devices) { - ValidateInput(device); + InputValidator.Validate(device); } var alarmCountByRuleList = new List(); @@ -399,16 +397,5 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services return ruleCount; } - - // Check illegal characters in input - private static void ValidateInput(string input) - { - input = input.Trim(); - - if (Regex.IsMatch(input, INVALID_CHARACTER)) - { - throw new InvalidInputException($"Input '{input}' contains invalid characters."); - } - } } } From 9b6b35cba88bdccf6dcd6fb31f50639761099993 Mon Sep 17 00:00:00 2001 From: Sang Hyun Kim Date: Tue, 15 Jan 2019 16:03:39 -0800 Subject: [PATCH 11/19] Add missing env variables to run scripts --- config/scripts/docker/run | 24 +++++++++++------ config/scripts/docker/run.cmd | 10 +++++++- device-telemetry/scripts/docker/run | 34 ++++++++++++++----------- device-telemetry/scripts/docker/run.cmd | 10 +++++--- 4 files changed, 51 insertions(+), 27 deletions(-) diff --git a/config/scripts/docker/run b/config/scripts/docker/run index e9a170a..ab08c41 100755 --- a/config/scripts/docker/run +++ b/config/scripts/docker/run @@ -13,14 +13,22 @@ run_container() { echo "Starting Config service ..." docker run -it -p 9005:9005 \ - -e PCS_STORAGEADAPTER_WEBSERVICE_URL \ - -e PCS_DEVICESIMULATION_WEBSERVICE_URL \ - -e PCS_TELEMETRY_WEBSERVICE_URL \ - -e PCS_AUTH_WEBSERVICE_URL \ - -e PCS_OFFICE365_CONNECTION_URL \ - -e PCS_SOLUTION_NAME \ - -e PCS_SUBSCRIPTION_ID \ - -e PCS_ARM_ENDPOINT_URL \ + -e PCS_AUTH_WEBSERVICE_URL \ + -e PCS_STORAGEADAPTER_WEBSERVICE_URL \ + -e PCS_DEVICESIMULATION_WEBSERVICE_URL \ + -e PCS_TELEMETRY_WEBSERVICE_URL \ + -e PCS_SOLUTION_TYPE \ + -e PCS_AZUREMAPS_KEY \ + -e PCS_AUTH_ISSUER \ + -e PCS_AUTH_AUDIENCE \ + -e PCS_AUTH_REQUIRED \ + -e PCS_CORS_WHITELIST \ + -e PCS_APPLICATION_SECRET \ + -e PCS_OFFICE365_CONNECTION_URL \ + -e PCS_SOLUTION_NAME \ + -e PCS_SUBSCRIPTION_ID \ + -e PCS_ARM_ENDPOINT_URL \ + -e PCS_SEED_TEMPLATE \ "$DOCKER_IMAGE:testing" } diff --git a/config/scripts/docker/run.cmd b/config/scripts/docker/run.cmd index 2f5cb04..e273ef5 100644 --- a/config/scripts/docker/run.cmd +++ b/config/scripts/docker/run.cmd @@ -19,14 +19,22 @@ IF %ERRORLEVEL% NEQ 0 GOTO FAIL :: Start the application echo Starting Config web service ... docker run -it -p 9005:9005 ^ + -e PCS_AUTH_WEBSERVICE_URL ^ -e PCS_STORAGEADAPTER_WEBSERVICE_URL ^ -e PCS_DEVICESIMULATION_WEBSERVICE_URL ^ -e PCS_TELEMETRY_WEBSERVICE_URL ^ - -e PCS_AUTH_WEBSERVICE_URL ^ + -e PCS_SOLUTION_TYPE ^ + -e PCS_AZUREMAPS_KEY ^ + -e PCS_AUTH_ISSUER ^ + -e PCS_AUTH_AUDIENCE ^ + -e PCS_AUTH_REQUIRED ^ + -e PCS_CORS_WHITELIST ^ + -e PCS_APPLICATION_SECRET ^ -e PCS_OFFICE365_CONNECTION_URL ^ -e PCS_SOLUTION_NAME ^ -e PCS_SUBSCRIPTION_ID ^ -e PCS_ARM_ENDPOINT_URL ^ + -e PCS_SEED_TEMPLATE ^ %DOCKER_IMAGE%:testing :: - - - - - - - - - - - - - - diff --git a/device-telemetry/scripts/docker/run b/device-telemetry/scripts/docker/run index da65a6f..bf1a95a 100755 --- a/device-telemetry/scripts/docker/run +++ b/device-telemetry/scripts/docker/run @@ -16,21 +16,25 @@ run_container() { echo "Starting Telemetry service..." docker run -it -p 9004:9004 \ - -e PCS_TELEMETRY_DOCUMENTDB_CONNSTRING \ - -e PCS_STORAGEADAPTER_WEBSERVICE_URL \ - -e PCS_AUTH_WEBSERVICE_URL \ - -e PCS_AUTH_ISSUER \ - -e PCS_AUTH_AUDIENCE \ - -e PCS_AAD_TENANT \ - -e PCS_AAD_APPID \ - -e PCS_AAD_APPSECRET \ - -e PCS_TELEMETRY_STORAGE_TYPE \ - -e PCS_TSI_FQDN \ - -e PCS_AZUREBLOB_CONNSTRING \ - -e PCS_ACTION_EVENTHUB_CONNSTRING \ - -e PCS_ACTION_EVENTHUB_NAME \ - -e PCS_LOGICAPP_ENDPOINT_URL \ - -e PCS_SOLUTION_NAME \ + -e PCS_AUTH_WEBSERVICE_URL \ + -e PCS_STORAGEADAPTER_WEBSERVICE_URL \ + -e PCS_DIAGNOSTICS_WEBSERVICE_URL \ + -e PCS_TELEMETRY_DOCUMENTDB_CONNSTRING \ + -e PCS_AUTH_ISSUER \ + -e PCS_AUTH_AUDIENCE \ + -e PCS_AUTH_REQUIRED \ + -e PCS_CORS_WHITELIST \ + -e PCS_APPLICATION_SECRET \ + -e PCS_AAD_TENANT \ + -e PCS_AAD_APPID \ + -e PCS_AAD_APPSECRET \ + -e PCS_TELEMETRY_STORAGE_TYPE \ + -e PCS_TSI_FQDN \ + -e PCS_AZUREBLOB_CONNSTRING \ + -e PCS_ACTION_EVENTHUB_CONNSTRING \ + -e PCS_ACTION_EVENTHUB_NAME \ + -e PCS_LOGICAPP_ENDPOINT_URL \ + -e PCS_SOLUTION_WEBSITE_URL \ "$DOCKER_IMAGE:testing" } diff --git a/device-telemetry/scripts/docker/run.cmd b/device-telemetry/scripts/docker/run.cmd index 948418c..fef245a 100644 --- a/device-telemetry/scripts/docker/run.cmd +++ b/device-telemetry/scripts/docker/run.cmd @@ -21,11 +21,15 @@ IF %ERRORLEVEL% NEQ 0 GOTO FAIL :: Start the application echo Starting Telemetry service... docker run -it -p 9004:9004 ^ - -e PCS_TELEMETRY_DOCUMENTDB_CONNSTRING ^ + -e PCS_AUTH_WEBSERVICE_URL ^ -e PCS_STORAGEADAPTER_WEBSERVICE_URL ^ - -e PCS_AUTH_WEBSERVICE_URL ^ + -e PCS_DIAGNOSTICS_WEBSERVICE_URL ^ + -e PCS_TELEMETRY_DOCUMENTDB_CONNSTRING ^ -e PCS_AUTH_ISSUER ^ -e PCS_AUTH_AUDIENCE ^ + -e PCS_AUTH_REQUIRED ^ + -e PCS_CORS_WHITELIST ^ + -e PCS_APPLICATION_SECRET ^ -e PCS_AAD_TENANT ^ -e PCS_AAD_APPID ^ -e PCS_AAD_APPSECRET ^ @@ -35,7 +39,7 @@ docker run -it -p 9004:9004 ^ -e PCS_ACTION_EVENTHUB_CONNSTRING ^ -e PCS_ACTION_EVENTHUB_NAME ^ -e PCS_LOGICAPP_ENDPOINT_URL ^ - -e PCS_SOLUTION_NAME ^ + -e PCS_SOLUTION_WEBSITE_URL ^ %DOCKER_IMAGE%:testing :: - - - - - - - - - - - - - - From 4471e7a65e3538b65eafdf1cb560e80aa19e7698 Mon Sep 17 00:00:00 2001 From: Jill Bender Date: Tue, 15 Jan 2019 16:08:21 -0800 Subject: [PATCH 12/19] add null check for group id validation --- device-telemetry/Services/Models/Rule.cs | 2 -- device-telemetry/Services/Rules.cs | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/device-telemetry/Services/Models/Rule.cs b/device-telemetry/Services/Models/Rule.cs index c0cdc62..c697ddf 100644 --- a/device-telemetry/Services/Models/Rule.cs +++ b/device-telemetry/Services/Models/Rule.cs @@ -49,8 +49,6 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Models InputValidator.Validate(this.ETag); InputValidator.Validate(this.Id); InputValidator.Validate(this.Name); - InputValidator.Validate(this.DateCreated); - InputValidator.Validate(this.DateModified); InputValidator.Validate(this.Description); InputValidator.Validate(this.GroupId); } diff --git a/device-telemetry/Services/Rules.cs b/device-telemetry/Services/Rules.cs index 1c5cc2c..3026a5d 100644 --- a/device-telemetry/Services/Rules.cs +++ b/device-telemetry/Services/Rules.cs @@ -144,7 +144,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services bool includeDeleted) { InputValidator.Validate(order); - InputValidator.Validate(groupId); + if (!string.IsNullOrEmpty(groupId)) + { + InputValidator.Validate(groupId); + } var data = await this.storage.GetAllAsync(STORAGE_COLLECTION); var ruleList = new List(); From 5b0945003dbc22129f5a87cced2168c76b362a24 Mon Sep 17 00:00:00 2001 From: Sang Hyun Kim Date: Tue, 15 Jan 2019 16:14:00 -0800 Subject: [PATCH 13/19] fix tabs --- config/scripts/docker/run | 32 ++++++++++----------- device-telemetry/scripts/docker/run | 38 ++++++++++++------------- device-telemetry/scripts/docker/run.cmd | 2 +- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/config/scripts/docker/run b/config/scripts/docker/run index ab08c41..e457aa1 100755 --- a/config/scripts/docker/run +++ b/config/scripts/docker/run @@ -13,22 +13,22 @@ run_container() { echo "Starting Config service ..." docker run -it -p 9005:9005 \ - -e PCS_AUTH_WEBSERVICE_URL \ - -e PCS_STORAGEADAPTER_WEBSERVICE_URL \ - -e PCS_DEVICESIMULATION_WEBSERVICE_URL \ - -e PCS_TELEMETRY_WEBSERVICE_URL \ - -e PCS_SOLUTION_TYPE \ - -e PCS_AZUREMAPS_KEY \ - -e PCS_AUTH_ISSUER \ - -e PCS_AUTH_AUDIENCE \ - -e PCS_AUTH_REQUIRED \ - -e PCS_CORS_WHITELIST \ - -e PCS_APPLICATION_SECRET \ - -e PCS_OFFICE365_CONNECTION_URL \ - -e PCS_SOLUTION_NAME \ - -e PCS_SUBSCRIPTION_ID \ - -e PCS_ARM_ENDPOINT_URL \ - -e PCS_SEED_TEMPLATE \ + -e PCS_AUTH_WEBSERVICE_URL \ + -e PCS_STORAGEADAPTER_WEBSERVICE_URL \ + -e PCS_DEVICESIMULATION_WEBSERVICE_URL \ + -e PCS_TELEMETRY_WEBSERVICE_URL \ + -e PCS_SOLUTION_TYPE \ + -e PCS_AZUREMAPS_KEY \ + -e PCS_AUTH_ISSUER \ + -e PCS_AUTH_AUDIENCE \ + -e PCS_AUTH_REQUIRED \ + -e PCS_CORS_WHITELIST \ + -e PCS_APPLICATION_SECRET \ + -e PCS_OFFICE365_CONNECTION_URL \ + -e PCS_SOLUTION_NAME \ + -e PCS_SUBSCRIPTION_ID \ + -e PCS_ARM_ENDPOINT_URL \ + -e PCS_SEED_TEMPLATE \ "$DOCKER_IMAGE:testing" } diff --git a/device-telemetry/scripts/docker/run b/device-telemetry/scripts/docker/run index bf1a95a..aa92c63 100755 --- a/device-telemetry/scripts/docker/run +++ b/device-telemetry/scripts/docker/run @@ -16,25 +16,25 @@ run_container() { echo "Starting Telemetry service..." docker run -it -p 9004:9004 \ - -e PCS_AUTH_WEBSERVICE_URL \ - -e PCS_STORAGEADAPTER_WEBSERVICE_URL \ - -e PCS_DIAGNOSTICS_WEBSERVICE_URL \ - -e PCS_TELEMETRY_DOCUMENTDB_CONNSTRING \ - -e PCS_AUTH_ISSUER \ - -e PCS_AUTH_AUDIENCE \ - -e PCS_AUTH_REQUIRED \ - -e PCS_CORS_WHITELIST \ - -e PCS_APPLICATION_SECRET \ - -e PCS_AAD_TENANT \ - -e PCS_AAD_APPID \ - -e PCS_AAD_APPSECRET \ - -e PCS_TELEMETRY_STORAGE_TYPE \ - -e PCS_TSI_FQDN \ - -e PCS_AZUREBLOB_CONNSTRING \ - -e PCS_ACTION_EVENTHUB_CONNSTRING \ - -e PCS_ACTION_EVENTHUB_NAME \ - -e PCS_LOGICAPP_ENDPOINT_URL \ - -e PCS_SOLUTION_WEBSITE_URL \ + -e PCS_AUTH_WEBSERVICE_URL \ + -e PCS_STORAGEADAPTER_WEBSERVICE_URL \ + -e PCS_DIAGNOSTICS_WEBSERVICE_URL \ + -e PCS_TELEMETRY_DOCUMENTDB_CONNSTRING \ + -e PCS_AUTH_ISSUER \ + -e PCS_AUTH_AUDIENCE \ + -e PCS_AUTH_REQUIRED \ + -e PCS_CORS_WHITELIST \ + -e PCS_APPLICATION_SECRET \ + -e PCS_AAD_TENANT \ + -e PCS_AAD_APPID \ + -e PCS_AAD_APPSECRET \ + -e PCS_TELEMETRY_STORAGE_TYPE \ + -e PCS_TSI_FQDN \ + -e PCS_AZUREBLOB_CONNSTRING \ + -e PCS_ACTION_EVENTHUB_CONNSTRING \ + -e PCS_ACTION_EVENTHUB_NAME \ + -e PCS_LOGICAPP_ENDPOINT_URL \ + -e PCS_SOLUTION_WEBSITE_URL \ "$DOCKER_IMAGE:testing" } diff --git a/device-telemetry/scripts/docker/run.cmd b/device-telemetry/scripts/docker/run.cmd index fef245a..e9be934 100644 --- a/device-telemetry/scripts/docker/run.cmd +++ b/device-telemetry/scripts/docker/run.cmd @@ -21,7 +21,7 @@ IF %ERRORLEVEL% NEQ 0 GOTO FAIL :: Start the application echo Starting Telemetry service... docker run -it -p 9004:9004 ^ - -e PCS_AUTH_WEBSERVICE_URL ^ + -e PCS_AUTH_WEBSERVICE_URL ^ -e PCS_STORAGEADAPTER_WEBSERVICE_URL ^ -e PCS_DIAGNOSTICS_WEBSERVICE_URL ^ -e PCS_TELEMETRY_DOCUMENTDB_CONNSTRING ^ From ee58c57491fcfa4bb45ba2089caf9c5f7c68eab0 Mon Sep 17 00:00:00 2001 From: Sang Hyun Kim Date: Tue, 15 Jan 2019 16:30:07 -0800 Subject: [PATCH 14/19] fix another spaces --- config/scripts/docker/run | 32 ++++++++++++------------ device-telemetry/scripts/docker/run | 38 ++++++++++++++--------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/config/scripts/docker/run b/config/scripts/docker/run index e457aa1..98495c1 100755 --- a/config/scripts/docker/run +++ b/config/scripts/docker/run @@ -13,22 +13,22 @@ run_container() { echo "Starting Config service ..." docker run -it -p 9005:9005 \ - -e PCS_AUTH_WEBSERVICE_URL \ - -e PCS_STORAGEADAPTER_WEBSERVICE_URL \ - -e PCS_DEVICESIMULATION_WEBSERVICE_URL \ - -e PCS_TELEMETRY_WEBSERVICE_URL \ - -e PCS_SOLUTION_TYPE \ - -e PCS_AZUREMAPS_KEY \ - -e PCS_AUTH_ISSUER \ - -e PCS_AUTH_AUDIENCE \ - -e PCS_AUTH_REQUIRED \ - -e PCS_CORS_WHITELIST \ - -e PCS_APPLICATION_SECRET \ - -e PCS_OFFICE365_CONNECTION_URL \ - -e PCS_SOLUTION_NAME \ - -e PCS_SUBSCRIPTION_ID \ - -e PCS_ARM_ENDPOINT_URL \ - -e PCS_SEED_TEMPLATE \ + -e PCS_AUTH_WEBSERVICE_URL \ + -e PCS_STORAGEADAPTER_WEBSERVICE_URL \ + -e PCS_DEVICESIMULATION_WEBSERVICE_URL \ + -e PCS_TELEMETRY_WEBSERVICE_URL \ + -e PCS_SOLUTION_TYPE \ + -e PCS_AZUREMAPS_KEY \ + -e PCS_AUTH_ISSUER \ + -e PCS_AUTH_AUDIENCE \ + -e PCS_AUTH_REQUIRED \ + -e PCS_CORS_WHITELIST \ + -e PCS_APPLICATION_SECRET \ + -e PCS_OFFICE365_CONNECTION_URL \ + -e PCS_SOLUTION_NAME \ + -e PCS_SUBSCRIPTION_ID \ + -e PCS_ARM_ENDPOINT_URL \ + -e PCS_SEED_TEMPLATE \ "$DOCKER_IMAGE:testing" } diff --git a/device-telemetry/scripts/docker/run b/device-telemetry/scripts/docker/run index aa92c63..44432e9 100755 --- a/device-telemetry/scripts/docker/run +++ b/device-telemetry/scripts/docker/run @@ -16,25 +16,25 @@ run_container() { echo "Starting Telemetry service..." docker run -it -p 9004:9004 \ - -e PCS_AUTH_WEBSERVICE_URL \ - -e PCS_STORAGEADAPTER_WEBSERVICE_URL \ - -e PCS_DIAGNOSTICS_WEBSERVICE_URL \ - -e PCS_TELEMETRY_DOCUMENTDB_CONNSTRING \ - -e PCS_AUTH_ISSUER \ - -e PCS_AUTH_AUDIENCE \ - -e PCS_AUTH_REQUIRED \ - -e PCS_CORS_WHITELIST \ - -e PCS_APPLICATION_SECRET \ - -e PCS_AAD_TENANT \ - -e PCS_AAD_APPID \ - -e PCS_AAD_APPSECRET \ - -e PCS_TELEMETRY_STORAGE_TYPE \ - -e PCS_TSI_FQDN \ - -e PCS_AZUREBLOB_CONNSTRING \ - -e PCS_ACTION_EVENTHUB_CONNSTRING \ - -e PCS_ACTION_EVENTHUB_NAME \ - -e PCS_LOGICAPP_ENDPOINT_URL \ - -e PCS_SOLUTION_WEBSITE_URL \ + -e PCS_AUTH_WEBSERVICE_URL \ + -e PCS_STORAGEADAPTER_WEBSERVICE_URL \ + -e PCS_DIAGNOSTICS_WEBSERVICE_URL \ + -e PCS_TELEMETRY_DOCUMENTDB_CONNSTRING \ + -e PCS_AUTH_ISSUER \ + -e PCS_AUTH_AUDIENCE \ + -e PCS_AUTH_REQUIRED \ + -e PCS_CORS_WHITELIST \ + -e PCS_APPLICATION_SECRET \ + -e PCS_AAD_TENANT \ + -e PCS_AAD_APPID \ + -e PCS_AAD_APPSECRET \ + -e PCS_TELEMETRY_STORAGE_TYPE \ + -e PCS_TSI_FQDN \ + -e PCS_AZUREBLOB_CONNSTRING \ + -e PCS_ACTION_EVENTHUB_CONNSTRING \ + -e PCS_ACTION_EVENTHUB_NAME \ + -e PCS_LOGICAPP_ENDPOINT_URL \ + -e PCS_SOLUTION_WEBSITE_URL \ "$DOCKER_IMAGE:testing" } From fb4faba64b2b03faed074dbe9242e553b1b48c3b Mon Sep 17 00:00:00 2001 From: Jill Bender Date: Wed, 16 Jan 2019 17:15:50 -0800 Subject: [PATCH 15/19] remove ETag from validation --- device-telemetry/Services/Models/Rule.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/device-telemetry/Services/Models/Rule.cs b/device-telemetry/Services/Models/Rule.cs index c697ddf..2b286f4 100644 --- a/device-telemetry/Services/Models/Rule.cs +++ b/device-telemetry/Services/Models/Rule.cs @@ -46,7 +46,6 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Models public void Validate() { - InputValidator.Validate(this.ETag); InputValidator.Validate(this.Id); InputValidator.Validate(this.Name); InputValidator.Validate(this.Description); From 5f0f68d5e48dda05a42a5b6aaacf5e5952ddf8a5 Mon Sep 17 00:00:00 2001 From: Jill Bender Date: Wed, 16 Jan 2019 17:39:53 -0800 Subject: [PATCH 16/19] include * in allowable characters --- device-telemetry/Services/Helpers/InputValidator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/device-telemetry/Services/Helpers/InputValidator.cs b/device-telemetry/Services/Helpers/InputValidator.cs index e349097..13a430e 100644 --- a/device-telemetry/Services/Helpers/InputValidator.cs +++ b/device-telemetry/Services/Helpers/InputValidator.cs @@ -5,7 +5,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Helpers { public class InputValidator { - private const string INVALID_CHARACTER = @"[^A-Za-z0-9:;.,_\-]"; + private const string INVALID_CHARACTER = @"[^A-Za-z0-9:;.,_\-*]"; // Check illegal characters in input public static void Validate(string input) From dbf7f0955330ec98d1bf34f1334858b048952d64 Mon Sep 17 00:00:00 2001 From: Jill Bender Date: Thu, 17 Jan 2019 11:32:37 -0800 Subject: [PATCH 17/19] Add test to make sure validation works in regular case. Add whitespace and * to validation regex --- device-telemetry/Services.Test/RulesTest.cs | 38 ++++++++++++++++++- .../Services/Helpers/InputValidator.cs | 6 +-- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/device-telemetry/Services.Test/RulesTest.cs b/device-telemetry/Services.Test/RulesTest.cs index 9adfc45..19fb050 100644 --- a/device-telemetry/Services.Test/RulesTest.cs +++ b/device-telemetry/Services.Test/RulesTest.cs @@ -457,6 +457,21 @@ namespace Services.Test await Assert.ThrowsAsync(async () => await this.rules.UpsertIfNotDeletedAsync(rule)); } + [Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)] + public void InputValidationPassesWithValidRule() + { + // Arrange + this.ThereAreSomeRulesInStorage(); + + List rulesList = this.GetSampleRulesList(); + + // Act & Assert + foreach (var rule in rulesList) + { + rule.Validate(); + } + } + private void ThereAreNoRulessInStorage() { this.rulesMock.Setup(x => x.GetListAsync(null, 0, LIMIT, null, false)) @@ -464,6 +479,14 @@ namespace Services.Test } private void ThereAreSomeRulesInStorage() + { + var sampleRules = this.GetSampleRulesList(); + + this.rulesMock.Setup(x => x.GetListAsync(null, 0, LIMIT, null, false)) + .ReturnsAsync(sampleRules); + } + + private List GetSampleRulesList() { var sampleConditions = new List { @@ -507,11 +530,22 @@ namespace Services.Test Severity = SeverityType.Warning, Conditions = sampleConditions, Actions = sampleActions + }, + new Rule() + { + ETag = "*", + Name = "Sample 3", + Enabled = true, + Calculation = CalculationType.Instant, + Description = "Sample description 2.", + GroupId = "Chillers", + Severity = SeverityType.Warning, + Conditions = sampleConditions, + Actions = sampleActions } }; - this.rulesMock.Setup(x => x.GetListAsync(null, 0, LIMIT, null, false)) - .ReturnsAsync(sampleRules); + return sampleRules; } /** diff --git a/device-telemetry/Services/Helpers/InputValidator.cs b/device-telemetry/Services/Helpers/InputValidator.cs index 13a430e..ee0c9ff 100644 --- a/device-telemetry/Services/Helpers/InputValidator.cs +++ b/device-telemetry/Services/Helpers/InputValidator.cs @@ -5,14 +5,12 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Helpers { public class InputValidator { - private const string INVALID_CHARACTER = @"[^A-Za-z0-9:;.,_\-*]"; + private const string INVALID_CHARACTER = @"[^A-Za-z0-9:;.!,_\-* ]"; // Check illegal characters in input public static void Validate(string input) { - input = input.Trim(); - - if (Regex.IsMatch(input, INVALID_CHARACTER)) + if (Regex.IsMatch(input.Trim(), INVALID_CHARACTER)) { throw new InvalidInputException($"Input '{input}' contains invalid characters."); } From fea4e0b79e5b09eb1f62aaf5bf00ad9571980075 Mon Sep 17 00:00:00 2001 From: Jill Bender Date: Thu, 17 Jan 2019 13:03:15 -0800 Subject: [PATCH 18/19] Add test with characters that are in default rules. Remove checks for description --- device-telemetry/Services.Test/RulesTest.cs | 2 +- device-telemetry/Services/Models/Rule.cs | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/device-telemetry/Services.Test/RulesTest.cs b/device-telemetry/Services.Test/RulesTest.cs index 19fb050..9ac8f9a 100644 --- a/device-telemetry/Services.Test/RulesTest.cs +++ b/device-telemetry/Services.Test/RulesTest.cs @@ -515,7 +515,7 @@ namespace Services.Test { Name = "Sample 1", Enabled = true, - Description = "Sample description 1", + Description = "Sample description 1 -- Pressure >= 298", GroupId = "Prototyping devices", Severity = SeverityType.Critical, Conditions = sampleConditions, diff --git a/device-telemetry/Services/Models/Rule.cs b/device-telemetry/Services/Models/Rule.cs index 2b286f4..916c4c1 100644 --- a/device-telemetry/Services/Models/Rule.cs +++ b/device-telemetry/Services/Models/Rule.cs @@ -47,9 +47,6 @@ namespace Microsoft.Azure.IoTSolutions.DeviceTelemetry.Services.Models public void Validate() { InputValidator.Validate(this.Id); - InputValidator.Validate(this.Name); - InputValidator.Validate(this.Description); - InputValidator.Validate(this.GroupId); } } From a95a3f0b21731a92bc4cdb3f6182c73ef2f1720a Mon Sep 17 00:00:00 2001 From: Jill Bender Date: Thu, 17 Jan 2019 14:38:28 -0800 Subject: [PATCH 19/19] Upgrade to DS-1.0.3 for local deployments --- scripts/local/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/local/docker-compose.yml b/scripts/local/docker-compose.yml index 5bc95e5..4701f46 100644 --- a/scripts/local/docker-compose.yml +++ b/scripts/local/docker-compose.yml @@ -72,7 +72,7 @@ services: - PCS_APPLICATION_SECRET devicesimulation: - image: azureiotpcs/device-simulation-dotnet:DS-1.0.2 + image: azureiotpcs/device-simulation-dotnet:DS-1.0.3 depends_on: - storageadapter environment: