├── .dockerignore ├── .gitignore ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── gitops ├── app │ ├── core │ │ ├── conexp │ │ │ ├── kustomization.yaml │ │ │ └── namespace.yaml │ │ ├── functions │ │ │ ├── kustomization.yaml │ │ │ └── namespace.yaml │ │ └── kustomization.yaml │ └── devops │ │ ├── kustomization.yaml │ │ └── namespace.yaml ├── clusters │ ├── bootstrap │ │ ├── flux-system │ │ │ ├── gotk-components.yaml │ │ │ ├── gotk-sync.yaml │ │ │ └── kustomization.yaml │ │ ├── kustomization.yaml │ │ ├── release.yaml │ │ └── repo.yaml │ └── production │ │ ├── app-core.yaml │ │ ├── app-devops.yaml │ │ ├── infrastructure-certmanager-issuer.yaml │ │ ├── infrastructure-certmanager.yaml │ │ ├── infrastructure-fluxui.yaml │ │ ├── infrastructure-harbor-nginx.yaml │ │ ├── infrastructure-harbor.yaml │ │ ├── infrastructure-jaeger.yaml │ │ ├── infrastructure-knative-channel.yaml │ │ ├── infrastructure-knative.yaml │ │ ├── infrastructure-linkerd.yaml │ │ ├── infrastructure-mysql.yaml │ │ ├── infrastructure-nginx.yaml │ │ ├── infrastructure-prometheus.yaml │ │ ├── infrastructure-rook-cluster.yaml │ │ ├── infrastructure-rook.yaml │ │ ├── infrastructure-seed.yaml │ │ ├── infrastructure-sources.yaml │ │ └── infrastructure-tekton.yaml ├── infrastructure │ ├── cert-manager-issuer │ │ ├── cluster-issuer.yaml │ │ └── kustomization.yaml │ ├── cert-manager │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ └── release.yaml │ ├── flux-ui │ │ ├── kustomization.yaml │ │ └── release.yaml │ ├── harbor-nginx │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ └── release.yaml │ ├── harbor │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ └── release.yaml │ ├── jaeger │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ └── release.yaml │ ├── knative-channel │ │ └── kustomization.yaml │ ├── knative │ │ └── kustomization.yaml │ ├── linkerd │ │ ├── crds.yaml │ │ ├── dashboard.yaml │ │ ├── jaeger.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ └── release.yaml │ ├── mysql │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ └── release.yaml │ ├── nginx │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ └── release.yaml │ ├── prometheus │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ └── release.yaml │ ├── rook-cluster │ │ └── kustomization.yaml │ ├── rook │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ └── release.yaml │ ├── seed │ │ ├── harbor.yaml │ │ ├── kustomization.yaml │ │ └── mysql.yaml │ ├── sources │ │ ├── bitnami.yaml │ │ ├── harbor.yaml │ │ ├── ingress-nginx.yaml │ │ ├── jaegertracing.yaml │ │ ├── jetstack.yaml │ │ ├── kustomization.yaml │ │ ├── linkerd.yaml │ │ ├── prometheus-community.yaml │ │ ├── rook.yaml │ │ └── weaveworks.yaml │ └── tekton │ │ └── kustomization.yaml ├── readme.md └── setup.sh ├── images └── cncf-projects-app-arc.png ├── json ├── harbor-project-member.json ├── harbor-project-user.json └── harbor-project.json ├── notes.md ├── src ├── .dockerignore ├── .gitattributes ├── .gitignore ├── Contoso.Common │ ├── .dockerignore │ ├── Contoso.Expenses.Common.csproj │ └── Models │ │ ├── ContosoExpensesWebContext.cs │ │ └── Expense.cs ├── Contoso.Expenses.API │ ├── .dockerignore │ ├── Contoso.Expenses.API.csproj │ ├── Controllers │ │ └── CostCenterController.cs │ ├── Database │ │ └── DatabaseContext.cs │ ├── Dockerfile │ ├── Models │ │ ├── ConfigValues.cs │ │ └── CostCenter.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── Contoso.Expenses.Function │ ├── .dockerignore │ ├── Contoso.Expenses.Function.csproj │ ├── Dockerfile │ ├── EmailHandler.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── Contoso.Expenses.Web │ ├── .dockerignore │ ├── AppMetrics │ │ └── MetricsRegistry.cs │ ├── Contoso.Expenses.Web.csproj │ ├── Dockerfile │ ├── Models │ │ ├── ConfigValues.cs │ │ ├── CostCenter.cs │ │ └── QueueInfo.cs │ ├── Pages │ │ ├── About.cshtml │ │ ├── About.cshtml.cs │ │ ├── Contact.cshtml │ │ ├── Contact.cshtml.cs │ │ ├── Error.cshtml │ │ ├── Error.cshtml.cs │ │ ├── Expenses │ │ │ ├── Create.cshtml │ │ │ ├── Create.cshtml.cs │ │ │ ├── Delete.cshtml │ │ │ ├── Delete.cshtml.cs │ │ │ ├── Details.cshtml │ │ │ ├── Details.cshtml.cs │ │ │ ├── Edit.cshtml │ │ │ ├── Edit.cshtml.cs │ │ │ ├── Index.cshtml │ │ │ └── Index.cshtml.cs │ │ ├── Index.cshtml │ │ ├── Index.cshtml.cs │ │ ├── Privacy.cshtml │ │ ├── Privacy.cshtml.cs │ │ ├── Shared │ │ │ ├── _CookieConsentPartial.cshtml │ │ │ ├── _Layout.cshtml │ │ │ └── _ValidationScriptsPartial.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Startup.cs │ ├── Tracing │ │ ├── JaegerTracingOptions.cs │ │ └── JaegerTracingServiceCollectionExtensions.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ │ ├── css │ │ ├── site.css │ │ └── site.min.css │ │ ├── favicon.ico │ │ ├── images │ │ └── Expenses.jpg │ │ ├── js │ │ ├── site.js │ │ └── site.min.js │ │ └── lib │ │ ├── bootstrap │ │ ├── .bower.json │ │ ├── LICENSE │ │ └── dist │ │ │ ├── css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap-theme.css.map │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap-theme.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ │ └── js │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.min.js │ │ │ └── npm.js │ │ ├── jquery-validation-unobtrusive │ │ ├── .bower.json │ │ ├── LICENSE.txt │ │ ├── jquery.validate.unobtrusive.js │ │ └── jquery.validate.unobtrusive.min.js │ │ ├── jquery-validation │ │ ├── .bower.json │ │ ├── LICENSE.md │ │ └── dist │ │ │ ├── additional-methods.js │ │ │ ├── additional-methods.min.js │ │ │ ├── jquery.validate.js │ │ │ └── jquery.validate.min.js │ │ └── jquery │ │ ├── .bower.json │ │ ├── LICENSE.txt │ │ └── dist │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map ├── ContosoCore.sln ├── README.md ├── backend.yaml ├── frontend.yaml └── function.yaml └── yml ├── README.md ├── app-admin-role.yaml ├── app-deploy-rolebinding.yaml ├── app-pipeline.yaml ├── app-triggers.yaml ├── git-clone.yaml ├── jaeger-values.yaml ├── knative-broker.yaml ├── linkerd-ingress.yaml ├── linkerd-opencesus-collector.yaml ├── linkerd-prometheus-additional.yaml ├── prometheus-values.yaml ├── rook-cluster.yaml ├── rook-storageclass.yaml ├── rook-values.yaml ├── tekton-default-configmap.yaml ├── tekton-el-ingress.yaml ├── tekton-feature-flags-configmap.yaml └── tekton-limit-range.yaml /.dockerignore: -------------------------------------------------------------------------------- 1 | # See https://docs.docker.com/engine/reference/builder/#dockerignore-file 2 | # Put files here that you don't want copied into your bundle's invocation image 3 | Dockerfile.tmpl 4 | 5 | # Ignore git directories 6 | .git 7 | .gitignore 8 | 9 | vitess/** 10 | 11 | !vitess/examples/operator/operator.yaml 12 | !vitess/examples/operator/101_initial_cluster.yaml 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cnab/ 2 | .vscode/ 3 | gitops/cluster/production/flux-system 4 | gitops/infrastructure/linkerd/*.crt 5 | gitops/infrastructure/linkerd/*.key 6 | *.deb 7 | *.tar.gz 8 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # ref: https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 2 | # CODEOWNERS are currently members of the Azure Upstream and Azure FastTrack team 3 | # We welcome all contributors and those wishing to become maintainers to this project 4 | * @phillipgibson @fmustaf @garvinmsft @seenu433 @squillace @ssarwa @lachie83 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Microsoft Azure 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CNCF Projects App 2 | 3 | ## Overview 4 | 5 | Have you ever wondered what an application architecture would look like if you committed to using mostly all graduated or incubating projects from the [Cloud Native Computing Foundation](https://www.cncf.io/projects/)? This repo, the **[CNCF](https://www.cncf.io/) Projects App**, attempts to answer that question with an example expense application that is made up almost exclusively of CNCF projects. 6 | 7 | ## CNCF Projects Application 8 | 9 | The CNCF Projects App is a sample expense application simulating a user submitting an expense report. The application consists of the following components: 10 | 11 | * [Kubernetes](https://kubernetes.io/) - Container Orchestration Cluster (CNCF) 12 | * [Flux](https://fluxcd.io/). CNCF. GitOps provider for infrastructure delivery (CNCF). 13 | * [Rook](https://rook.io/) - Storage Management (CNCF) 14 | * [Harbor](https://goharbor.io/) - Container Registry (CNCF) 15 | * [Linkerd](https://linkerd.io/) - Service Mesh (CNCF) 16 | * [Prometheus](https://prometheus.io/) - Monitoring (CNCF) 17 | * [Jaeger](https://www.jaegertracing.io/) - Observability/Tracing (CNCF) 18 | * [Knative](https://knative.dev/) - Serverless (CNCF) 19 | * [MySQL](https://www.mysql.com/) - Database 20 | * [Nginx](https://www.nginx.com/) - Kubernetes Ingress Controller 21 | * [Tekton](https://tekton.dev/) CI/CD (CD Foundation) 22 | * [Grafana](https://grafana.com/) - Dashboard 23 | * [SendGrid](https://sendgrid.com/) - Email Service 24 | * [GitHub](https://github.com/) - Code Repository 25 | * Web Front-End & Web API - [.NET Core](https://docs.microsoft.com/en-us/dotnet/core/about) 26 | 27 | ## Architecture 28 | 29 | Below is the documented CNCF Projects App architecture for reference. 30 | 31 | ![Alt text](/images/cncf-projects-app-arc.png) 32 | 33 | ### Workflow 34 | 35 | #### Application flow 36 | 37 | 1. The employee accesses a web app via NGINX Ingress to submit expenses. 38 | 39 | 2. The web app calls an API app to retrieve the employee's manager. 40 | 41 | 3. The web app pushes a message that's generated for the creation of the expense report to a Knative broker. 42 | 43 | 4. The expense report is saved in MySQL. 44 | 45 | 5. Knative triggers the Email Dispatcher function with the expense message as the payload. 46 | 47 | 6. Email Dispatcher creates a SendGrid message. 48 | 49 | 7. SendGrid sends an email to the retrieved manager for review. 50 | 51 | #### DevOps flow 52 | 53 | a. Developers write or update the code in Visual Studio Code. 54 | 55 | b. Developers push the code to GitHub from their local workspace in Visual Studio Code. 56 | 57 | c. Github Webhook triggers Tekton pipelines which clones the GitHub code. 58 | 59 | d. Pipelines build and push and the container images to a Harbor registry. 60 | 61 | e. Tekton deploys the web app, API app, and Email Dispatcher applications. 62 | 63 | f. Prometheus captures application metrics. 64 | 65 | g. Engineers monitor metrics on a Grafana Dashboard. 66 | 67 | h. DevOps engineers monitor the Grafana Dashboard. 68 | 69 | #### Infrastructure 70 | 71 | i. AKS cluster based on the infrastructure presented in the AKS baseline. 72 | 73 | ii. Rook Ceph used for cluster storage. 74 | 75 | iii. Linkerd service mesh. 76 | 77 | iv. Jaeger for overall application tracing on the Kubernetes cluster. 78 | 79 | ## Install 80 | 81 | Please follow the instructions [here](notes.md) in sequence to deploy the CNCF Projects App in your environment. 82 | 83 | Instructions for automated deployment through gitops are [here](/gitops/readme.md). 84 | 85 | ## Contributing 86 | 87 | This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com. 88 | 89 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. 90 | 91 | This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments. 92 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | - Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | - Full paths of source file(s) related to the manifestation of the issue 23 | - The location of the affected source code (tag/branch/commit or direct URL) 24 | - Any special configuration required to reproduce the issue 25 | - Step-by-step instructions to reproduce the issue 26 | - Proof-of-concept or exploit code (if possible) 27 | - Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 6 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 7 | feature request as a new Issue. 8 | 9 | For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE 10 | FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER 11 | CHANNEL. WHERE WILL YOU HELP PEOPLE?**. 12 | 13 | ## Microsoft Support Policy 14 | 15 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above. 16 | -------------------------------------------------------------------------------- /gitops/app/core/conexp/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: conexp-mvp 4 | resources: 5 | - namespace.yaml 6 | - ../regcred-conexp-sealed.yaml 7 | - ../../../../yml/app-deploy-rolebinding.yaml 8 | -------------------------------------------------------------------------------- /gitops/app/core/conexp/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: conexp-mvp 5 | annotations: 6 | linkerd.io/inject: enabled 7 | config.linkerd.io/trace-collector: "collector.linkerd-jaeger:55678" 8 | config.alpha.linkerd.io/trace-collector-service-account: "collector" 9 | -------------------------------------------------------------------------------- /gitops/app/core/functions/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: conexp-mvp-fn 4 | resources: 5 | - namespace.yaml 6 | - ../regcred-fn-sealed.yaml 7 | - ../../../../yml/knative-broker.yaml 8 | - ../../../../yml/app-deploy-rolebinding.yaml 9 | -------------------------------------------------------------------------------- /gitops/app/core/functions/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: conexp-mvp-fn 5 | -------------------------------------------------------------------------------- /gitops/app/core/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - conexp 5 | - functions 6 | -------------------------------------------------------------------------------- /gitops/app/devops/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: conexp-mvp-devops 4 | resources: 5 | - namespace.yaml 6 | - ../../../yml/tekton-limit-range.yaml 7 | - ../../../yml/app-admin-role.yaml 8 | - ../../../yml/app-triggers.yaml 9 | - ../../../yml/app-pipeline.yaml 10 | - ../../../yml/tekton-el-ingress.yaml 11 | - ../../../yml/git-clone.yaml 12 | - https://raw.githubusercontent.com/tektoncd/catalog/main/task/kaniko/0.6/kaniko.yaml 13 | - regcred-devops-sealed.yaml 14 | patches: 15 | - patch: |- 16 | - op: replace 17 | path: /spec/rules/0/host 18 | value: ${cicdWebhookHost} 19 | - op: replace 20 | path: /spec/tls/0/hosts/0 21 | value: ${cicdWebhookHost} 22 | target: 23 | group: networking.k8s.io 24 | version: v1 25 | kind: Ingress 26 | name: ingress-resource 27 | - patch: |- 28 | - op: replace 29 | path: /spec/steps/0/args 30 | value: 31 | - "-i" 32 | - "-e" 33 | - "s|__IMAGE__|$(params.image-source)|g;s|__SENDGRIDAPIKEY__|${sendGridApiKey}|g;s|__APPHOSTNAME__|${appHostName}|g;s|__APIDBCONSTR__|server=mysql.mysql.svc.cluster.local;Port=3306;database=conexpapi;user=ftacncf;password=FTA@CNCF0n@zure3;|g;s|__WEBDBCONSTR__|server=mysql.mysql.svc.cluster.local;Port=3306;database=conexpweb;user=ftacncf;password=FTA@CNCF0n@zure3;|g;s|__APIURL__|http://backend-svc.conexp-mvp.svc.cluster.local:80|g;" 34 | - "$(workspaces.source.path)/src/$(params.pathToYamlFile)" 35 | target: 36 | group: tekton.dev 37 | version: v1beta1 38 | kind: Task 39 | name: deploy-using-kubectl 40 | - patch: |- 41 | - op: replace 42 | path: /spec/params/5 43 | value: 44 | name: web-imageUrl 45 | value: "${registryHost}/conexp/web:$(body.head_commit.id)" 46 | - op: replace 47 | path: /spec/params/6 48 | value: 49 | name: api-imageUrl 50 | value: "${registryHost}/conexp/api:$(body.head_commit.id)" 51 | - op: replace 52 | path: /spec/params/7 53 | value: 54 | name: fn-imageUrl 55 | value: "${registryHost}/conexp/emaildispatcher:$(body.head_commit.id)" 56 | target: 57 | group: triggers.tekton.dev 58 | version: v1alpha1 59 | kind: TriggerBinding 60 | name: conexp-pipelinebinding -------------------------------------------------------------------------------- /gitops/app/devops/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: conexp-mvp-devops 5 | 6 | -------------------------------------------------------------------------------- /gitops/clusters/bootstrap/flux-system/gotk-sync.yaml: -------------------------------------------------------------------------------- 1 | # This manifest was generated by flux. DO NOT EDIT. 2 | --- 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: GitRepository 5 | metadata: 6 | name: flux-system 7 | namespace: flux-system 8 | spec: 9 | interval: 1m0s 10 | ref: 11 | branch: main 12 | secretRef: 13 | name: flux-system 14 | url: ssh://git@github.com/seenu433/cloud-native-app 15 | --- 16 | apiVersion: kustomize.toolkit.fluxcd.io/v1 17 | kind: Kustomization 18 | metadata: 19 | name: flux-system 20 | namespace: flux-system 21 | spec: 22 | interval: 10m0s 23 | path: ./gitops/clusters/bootstrap 24 | prune: true 25 | sourceRef: 26 | kind: GitRepository 27 | name: flux-system 28 | -------------------------------------------------------------------------------- /gitops/clusters/bootstrap/flux-system/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - gotk-components.yaml 5 | - gotk-sync.yaml 6 | -------------------------------------------------------------------------------- /gitops/clusters/bootstrap/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - repo.yaml 5 | - release.yaml 6 | -------------------------------------------------------------------------------- /gitops/clusters/bootstrap/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 2 | kind: HelmRelease 3 | metadata: 4 | name: sealed-secrets 5 | namespace: flux-system 6 | spec: 7 | chart: 8 | spec: 9 | chart: sealed-secrets 10 | sourceRef: 11 | kind: HelmRepository 12 | name: sealed-secrets 13 | version: ">=2.10.0-0" 14 | interval: 1h0m0s 15 | releaseName: sealed-secrets-controller 16 | targetNamespace: flux-system 17 | install: 18 | crds: Create 19 | upgrade: 20 | crds: CreateReplace 21 | -------------------------------------------------------------------------------- /gitops/clusters/bootstrap/repo.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1beta2 2 | kind: HelmRepository 3 | metadata: 4 | name: sealed-secrets 5 | namespace: flux-system 6 | spec: 7 | interval: 1h0m0s 8 | url: https://bitnami-labs.github.io/sealed-secrets 9 | -------------------------------------------------------------------------------- /gitops/clusters/production/app-core.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: app-core 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-nginx 10 | - name: infrastructure-harbor 11 | - name: infrastructure-mysql 12 | - name: infrastructure-linkerd 13 | - name: infrastructure-knative-channel 14 | - name: infrastructure-seed 15 | sourceRef: 16 | kind: GitRepository 17 | name: flux-system 18 | path: ./gitops/app/core 19 | prune: true -------------------------------------------------------------------------------- /gitops/clusters/production/app-devops.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: app-devops 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: app-core 10 | - name: infrastructure-tekton 11 | sourceRef: 12 | kind: GitRepository 13 | name: flux-system 14 | path: ./gitops/app/devops 15 | postBuild: 16 | substituteFrom: 17 | - kind: Secret 18 | name: gitops-variables 19 | prune: true 20 | -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-certmanager-issuer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-certmanager-issuer 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-certmanager 10 | sourceRef: 11 | kind: GitRepository 12 | name: flux-system 13 | path: ./gitops/infrastructure/cert-manager-issuer 14 | postBuild: 15 | substituteFrom: 16 | - kind: Secret 17 | name: gitops-variables 18 | prune: true 19 | healthChecks: 20 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 21 | kind: HelmRelease 22 | name: cert-manager 23 | namespace: cert-manager 24 | -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-certmanager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-certmanager 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-sources 10 | sourceRef: 11 | kind: GitRepository 12 | name: flux-system 13 | path: ./gitops/infrastructure/cert-manager 14 | postBuild: 15 | substituteFrom: 16 | - kind: Secret 17 | name: gitops-variables 18 | prune: true 19 | healthChecks: 20 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 21 | kind: HelmRelease 22 | name: cert-manager 23 | namespace: cert-manager 24 | -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-fluxui.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-flux-ui 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-sources 10 | sourceRef: 11 | kind: GitRepository 12 | name: flux-system 13 | path: ./gitops/infrastructure/flux-ui 14 | prune: true 15 | postBuild: 16 | substituteFrom: 17 | - kind: Secret 18 | name: gitops-variables 19 | healthChecks: 20 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 21 | kind: HelmRelease 22 | name: weave-gitops 23 | namespace: flux-system 24 | -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-harbor-nginx.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-harbor-nginx 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-sources 10 | sourceRef: 11 | kind: GitRepository 12 | name: flux-system 13 | path: ./gitops/infrastructure/harbor-nginx 14 | prune: true 15 | postBuild: 16 | substituteFrom: 17 | - kind: Secret 18 | name: gitops-variables 19 | healthChecks: 20 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 21 | kind: HelmRelease 22 | name: harbor-nginx-ingress 23 | namespace: harbor-ingress-system 24 | -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-harbor.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-harbor 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-harbor-nginx 10 | - name: infrastructure-rook-cluster 11 | - name: infrastructure-linkerd 12 | sourceRef: 13 | kind: GitRepository 14 | name: flux-system 15 | path: ./gitops/infrastructure/harbor 16 | postBuild: 17 | substituteFrom: 18 | - kind: Secret 19 | name: gitops-variables 20 | prune: true 21 | healthChecks: 22 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 23 | kind: HelmRelease 24 | name: harbor 25 | namespace: harbor-system 26 | -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-jaeger.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-jaeger 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-sources 10 | - name: infrastructure-rook-cluster 11 | sourceRef: 12 | kind: GitRepository 13 | name: flux-system 14 | path: ./gitops/infrastructure/jaeger 15 | prune: true 16 | healthChecks: 17 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 18 | kind: HelmRelease 19 | name: jaeger 20 | namespace: tracing -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-knative-channel.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-knative-channel 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-knative 10 | sourceRef: 11 | kind: GitRepository 12 | name: flux-system 13 | path: ./gitops/infrastructure/knative-channel 14 | postBuild: 15 | substituteFrom: 16 | - kind: Secret 17 | name: gitops-variables 18 | prune: true -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-knative.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-knative 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-sources 10 | sourceRef: 11 | kind: GitRepository 12 | name: flux-system 13 | path: ./gitops/infrastructure/knative 14 | postBuild: 15 | substituteFrom: 16 | - kind: Secret 17 | name: gitops-variables 18 | prune: true -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-linkerd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-linkerd 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-jaeger 10 | - name: infrastructure-prometheus 11 | - name: infrastructure-knative-channel 12 | - name: infrastructure-mysql 13 | sourceRef: 14 | kind: GitRepository 15 | name: flux-system 16 | path: ./gitops/infrastructure/linkerd 17 | postBuild: 18 | substituteFrom: 19 | - kind: Secret 20 | name: gitops-variables 21 | prune: true 22 | healthChecks: 23 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 24 | kind: HelmRelease 25 | name: linkerd 26 | namespace: linkerd 27 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 28 | kind: HelmRelease 29 | name: linkerd-jaeger 30 | namespace: linkerd 31 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 32 | kind: HelmRelease 33 | name: linkerd-viz 34 | namespace: linkerd-viz 35 | -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-mysql.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-mysql 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-sources 10 | - name: infrastructure-rook-cluster 11 | sourceRef: 12 | kind: GitRepository 13 | name: flux-system 14 | path: ./gitops/infrastructure/mysql 15 | prune: true 16 | healthChecks: 17 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 18 | kind: HelmRelease 19 | name: mysql 20 | namespace: mysql -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-nginx.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-nginx 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-sources 10 | sourceRef: 11 | kind: GitRepository 12 | name: flux-system 13 | path: ./gitops/infrastructure/nginx 14 | prune: true 15 | postBuild: 16 | substituteFrom: 17 | - kind: Secret 18 | name: gitops-variables 19 | healthChecks: 20 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 21 | kind: HelmRelease 22 | name: nginx-ingress 23 | namespace: ingress-basic 24 | -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-prometheus.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-prometheus 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-sources 10 | - name: infrastructure-rook-cluster 11 | sourceRef: 12 | kind: GitRepository 13 | name: flux-system 14 | path: ./gitops/infrastructure/prometheus 15 | prune: true 16 | healthChecks: 17 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 18 | kind: HelmRelease 19 | name: prometheus 20 | namespace: monitoring -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-rook-cluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-rook-cluster 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-rook 10 | sourceRef: 11 | kind: GitRepository 12 | name: flux-system 13 | path: ./gitops/infrastructure/rook-cluster 14 | postBuild: 15 | substituteFrom: 16 | - kind: Secret 17 | name: gitops-variables 18 | prune: true 19 | healthChecks: 20 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 21 | kind: HelmRelease 22 | name: rook-ceph 23 | namespace: rook-ceph 24 | - apiVersion: apps/v1 25 | kind: Deployment 26 | name: rook-ceph-osd-0 27 | namespace: rook-ceph 28 | - apiVersion: apps/v1 29 | kind: Deployment 30 | name: rook-ceph-osd-1 31 | namespace: rook-ceph 32 | - apiVersion: apps/v1 33 | kind: Deployment 34 | name: rook-ceph-osd-2 35 | namespace: rook-ceph -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-rook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-rook 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-sources 10 | sourceRef: 11 | kind: GitRepository 12 | name: flux-system 13 | path: ./gitops/infrastructure/rook 14 | prune: true 15 | postBuild: 16 | substituteFrom: 17 | - kind: Secret 18 | name: gitops-variables 19 | healthChecks: 20 | - apiVersion: helm.toolkit.fluxcd.io/v1beta1 21 | kind: HelmRelease 22 | name: rook-ceph 23 | namespace: rook-ceph -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-seed.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-seed 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-harbor 10 | - name: infrastructure-mysql 11 | sourceRef: 12 | kind: GitRepository 13 | name: flux-system 14 | path: ./gitops/infrastructure/seed 15 | postBuild: 16 | substituteFrom: 17 | - kind: Secret 18 | name: gitops-variables 19 | prune: true 20 | -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-sources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-sources 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | sourceRef: 9 | kind: GitRepository 10 | name: flux-system 11 | path: ./gitops/infrastructure/sources 12 | prune: true -------------------------------------------------------------------------------- /gitops/clusters/production/infrastructure-tekton.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: infrastructure-tekton 5 | namespace: flux-system 6 | spec: 7 | interval: 10m0s 8 | dependsOn: 9 | - name: infrastructure-rook-cluster 10 | - name: infrastructure-nginx 11 | sourceRef: 12 | kind: GitRepository 13 | name: flux-system 14 | path: ./gitops/infrastructure/tekton 15 | prune: true 16 | -------------------------------------------------------------------------------- /gitops/infrastructure/cert-manager-issuer/cluster-issuer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: ClusterIssuer 3 | metadata: 4 | name: letsencrypt 5 | namespace: cert-manager 6 | spec: 7 | acme: 8 | server: https://acme-v02.api.letsencrypt.org/directory 9 | email: "${cluster_issuer_email}" 10 | privateKeySecretRef: 11 | name: letsencrypt 12 | solvers: 13 | - http01: 14 | ingress: 15 | class: nginx 16 | -------------------------------------------------------------------------------- /gitops/infrastructure/cert-manager-issuer/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: cert-manager 4 | resources: 5 | - cluster-issuer.yaml 6 | -------------------------------------------------------------------------------- /gitops/infrastructure/cert-manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: cert-manager 4 | resources: 5 | - namespace.yaml 6 | - release.yaml 7 | -------------------------------------------------------------------------------- /gitops/infrastructure/cert-manager/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: cert-manager 5 | labels: 6 | cert-manager.io/disable-validation: "true" -------------------------------------------------------------------------------- /gitops/infrastructure/cert-manager/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 2 | kind: HelmRelease 3 | metadata: 4 | name: cert-manager 5 | namespace: cert-manager 6 | spec: 7 | releaseName: cert-manager 8 | chart: 9 | spec: 10 | chart: cert-manager 11 | version: 'v1.12.1' 12 | sourceRef: 13 | kind: HelmRepository 14 | name: jetstack 15 | namespace: flux-system 16 | interval: 1h0m0s 17 | install: 18 | remediation: 19 | retries: 3 20 | # Default values 21 | # https://github.com/bitnami/charts/blob/master/bitnami/nginx-ingress-controller/values.yaml 22 | values: 23 | installCRDs: true 24 | nodeSelector: 25 | beta.kubernetes.io/os: linux 26 | -------------------------------------------------------------------------------- /gitops/infrastructure/flux-ui/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: flux-system 4 | resources: 5 | - release.yaml 6 | -------------------------------------------------------------------------------- /gitops/infrastructure/flux-ui/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 2 | kind: HelmRelease 3 | metadata: 4 | name: weave-gitops 5 | namespace: flux-system 6 | spec: 7 | interval: 60m 8 | chart: 9 | spec: 10 | chart: weave-gitops 11 | version: "*" 12 | sourceRef: 13 | kind: HelmRepository 14 | name: weave-gitops 15 | interval: 12h 16 | # https://github.com/weaveworks/weave-gitops/blob/main/charts/gitops-server/values.yaml 17 | values: 18 | resources: 19 | requests: 20 | cpu: 100m 21 | memory: 64Mi 22 | limits: 23 | cpu: 1 24 | memory: 512Mi 25 | securityContext: 26 | capabilities: 27 | drop: 28 | - ALL 29 | readOnlyRootFilesystem: true 30 | runAsNonRoot: true 31 | runAsUser: 1000 32 | adminUser: 33 | create: true 34 | username: admin 35 | # Change password by generating a new hash with: 36 | # https://docs.gitops.weave.works/docs/configuration/securing-access-to-the-dashboard/#login-via-a-cluster-user-account 37 | # bcrypt hash for password "flux" 38 | passwordHash: "$2a$10$P/tHQ1DNFXdvX0zRGA8LPeSOyb0JXq9rP3fZ4W8HGTpLV7qHDlWhe" -------------------------------------------------------------------------------- /gitops/infrastructure/harbor-nginx/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: harbor-ingress-system 4 | resources: 5 | - namespace.yaml 6 | - release.yaml 7 | -------------------------------------------------------------------------------- /gitops/infrastructure/harbor-nginx/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: harbor-ingress-system 5 | labels: 6 | cert-manager.io/disable-validation: "true" 7 | -------------------------------------------------------------------------------- /gitops/infrastructure/harbor-nginx/release.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: harbor-nginx-ingress 6 | namespace: harbor-ingress-system 7 | spec: 8 | releaseName: harbor-nginx-ingress 9 | chart: 10 | spec: 11 | chart: ingress-nginx 12 | version: '4.7.1' 13 | sourceRef: 14 | kind: HelmRepository 15 | name: ingress-nginx 16 | namespace: flux-system 17 | interval: 1h0m0s 18 | install: 19 | remediation: 20 | retries: 3 21 | # Default values 22 | # https://github.com/bitnami/charts/blob/master/bitnami/nginx-ingress-controller/values.yaml 23 | values: 24 | controller: 25 | electionID: harbor-ingress-controller-leader 26 | ingressClassResource: 27 | name: harbor 28 | enabled: true 29 | default: true 30 | controllerValue: k8s.io/harbor 31 | ingressClass: harbor 32 | replicaCount: 2 33 | pod: 34 | annotations: 35 | linkerd.io/inject: enabled 36 | config.linkerd.io/trace-collector: collector.linkerd-jaeger:55678 37 | service: 38 | annotations: 39 | service.beta.kubernetes.io/azure-dns-label-name: "${registryHostDnsLabel}" 40 | service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path: "/healthz" 41 | nodeSelector: 42 | beta.kubernetes.io/os: linux 43 | defaultBackend: 44 | nodeSelector: 45 | beta.kubernetes.io/os: linux 46 | -------------------------------------------------------------------------------- /gitops/infrastructure/harbor/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: harbor-system 4 | resources: 5 | - namespace.yaml 6 | - release.yaml 7 | -------------------------------------------------------------------------------- /gitops/infrastructure/harbor/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: harbor-system 5 | -------------------------------------------------------------------------------- /gitops/infrastructure/harbor/release.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: harbor 6 | namespace: harbor-system 7 | spec: 8 | releaseName: harbor 9 | chart: 10 | spec: 11 | chart: harbor 12 | version: '1.12.2' 13 | sourceRef: 14 | kind: HelmRepository 15 | name: harbor 16 | namespace: flux-system 17 | interval: 1h0m0s 18 | install: 19 | remediation: 20 | retries: 3 21 | values: 22 | expose: 23 | tls: 24 | certSource: secret 25 | secret: 26 | secretName: ingress-cert-harbor 27 | ingress: 28 | annotations: 29 | kubernetes.io/ingress.class: harbor 30 | ingress.kubernetes.io/ssl-redirect: "true" 31 | cert-manager.io/cluster-issuer: letsencrypt 32 | acme.cert-manager.io/http01-ingress-class: harbor 33 | className: harbor 34 | hosts: 35 | core: "${registryHost}" 36 | notary: 37 | enabled: false 38 | trivy: 39 | enabled: false 40 | persistence: 41 | enabled: true 42 | persistentVolumeClaim: 43 | chartmuseum: 44 | storageClass: rook-ceph-block 45 | jobservice: 46 | storageClass: rook-ceph-block 47 | database: 48 | storageClass: rook-ceph-block 49 | redis: 50 | storageClass: rook-ceph-block 51 | registry: 52 | storageClass: rook-ceph-block 53 | externalURL: "${externalUrl}" 54 | harborAdminPassword: admin 55 | -------------------------------------------------------------------------------- /gitops/infrastructure/jaeger/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: tracing 4 | resources: 5 | - namespace.yaml 6 | - release.yaml 7 | configMapGenerator: 8 | - name: helm-values 9 | files: 10 | - ../../../yml/jaeger-values.yaml 11 | generatorOptions: 12 | disableNameSuffixHash: true -------------------------------------------------------------------------------- /gitops/infrastructure/jaeger/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: tracing -------------------------------------------------------------------------------- /gitops/infrastructure/jaeger/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 2 | kind: HelmRelease 3 | metadata: 4 | name: jaeger 5 | namespace: tracing 6 | spec: 7 | releaseName: jaeger 8 | chart: 9 | spec: 10 | chart: jaeger 11 | version: '0.71.8' 12 | sourceRef: 13 | kind: HelmRepository 14 | name: jaegertracing 15 | namespace: flux-system 16 | interval: 1h0m0s 17 | install: 18 | remediation: 19 | retries: 3 20 | valuesFrom: 21 | - kind: ConfigMap 22 | name: helm-values 23 | valuesKey: jaeger-values.yaml -------------------------------------------------------------------------------- /gitops/infrastructure/knative-channel/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - https://github.com/knative/eventing/releases/download/knative-v1.10.1/in-memory-channel.yaml 5 | - https://github.com/knative/eventing/releases/download/knative-v1.10.1/mt-channel-broker.yaml -------------------------------------------------------------------------------- /gitops/infrastructure/knative/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - https://github.com/knative/serving/releases/download/knative-v1.10.2/serving-core.yaml 5 | - https://github.com/knative/net-kourier/releases/download/knative-v1.10.0/kourier.yaml 6 | - https://github.com/knative/eventing/releases/download/knative-v1.10.1/eventing-core.yaml 7 | patches: 8 | - patch: |- 9 | - op: replace 10 | path: /data 11 | value: {"ingress-class":"kourier.ingress.networking.knative.dev"} 12 | target: 13 | version: v1 14 | kind: ConfigMap 15 | name: config-network 16 | - patch: |- 17 | - op: replace 18 | path: /data 19 | value: {"example.com":""} 20 | target: 21 | version: v1 22 | kind: ConfigMap 23 | name: config-domain 24 | - patch: |- 25 | - op: replace 26 | path: /data 27 | value: {"enable-scale-to-zero": "true"} 28 | target: 29 | version: v1 30 | kind: ConfigMap 31 | name: config-autoscaler 32 | - patch: |- 33 | - op: replace 34 | path: /data 35 | value: {} 36 | target: 37 | version: v1 38 | kind: ConfigMap 39 | name: config-observability 40 | - patch: |- 41 | - op: replace 42 | path: /data 43 | value: {} 44 | target: 45 | version: v1 46 | kind: ConfigMap 47 | name: config-tracing 48 | - patch: |- 49 | - op: replace 50 | path: /data 51 | value: {} 52 | target: 53 | version: v1 54 | kind: ConfigMap 55 | name: config-logging 56 | - patch: |- 57 | - op: replace 58 | path: /data 59 | value: {} 60 | target: 61 | version: v1 62 | kind: ConfigMap 63 | name: config-gc 64 | - patch: |- 65 | - op: replace 66 | path: /data 67 | value: {} 68 | target: 69 | version: v1 70 | kind: ConfigMap 71 | name: config-leader-election -------------------------------------------------------------------------------- /gitops/infrastructure/linkerd/crds.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: linkerd-crds 6 | namespace: linkerd 7 | spec: 8 | releaseName: linkerd-crds 9 | chart: 10 | spec: 11 | chart: linkerd-crds 12 | version: '1.6.1' 13 | sourceRef: 14 | kind: HelmRepository 15 | name: linkerd 16 | namespace: flux-system 17 | interval: 1h0m0s 18 | install: 19 | remediation: 20 | retries: 3 21 | -------------------------------------------------------------------------------- /gitops/infrastructure/linkerd/dashboard.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: linkerd-viz 6 | namespace: linkerd-viz 7 | spec: 8 | releaseName: linkerd-viz 9 | chart: 10 | spec: 11 | chart: linkerd-viz 12 | version: '30.8.5' 13 | sourceRef: 14 | kind: HelmRepository 15 | name: linkerd 16 | namespace: flux-system 17 | interval: 1h0m0s 18 | dependsOn: 19 | - name: linkerd-jaeger 20 | namespace: linkerd 21 | install: 22 | remediation: 23 | retries: 3 24 | values: 25 | jaegerUrl: jaeger-query.tracing:16687 26 | -------------------------------------------------------------------------------- /gitops/infrastructure/linkerd/jaeger.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: linkerd-jaeger 6 | namespace: linkerd 7 | spec: 8 | releaseName: linkerd-jaeger 9 | chart: 10 | spec: 11 | chart: linkerd-jaeger 12 | version: '30.8.5' 13 | sourceRef: 14 | kind: HelmRepository 15 | name: linkerd 16 | namespace: flux-system 17 | interval: 1h0m0s 18 | dependsOn: 19 | - name: linkerd 20 | install: 21 | remediation: 22 | retries: 3 23 | values: 24 | jaeger: 25 | enabled: false 26 | collector: 27 | jaegerAddr: http://jaeger-collector.tacing:14268/api/traces 28 | -------------------------------------------------------------------------------- /gitops/infrastructure/linkerd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - namespace.yaml 5 | - certs-sealed.yaml 6 | - crds.yaml 7 | - release.yaml 8 | - jaeger.yaml 9 | - dashboard.yaml 10 | -------------------------------------------------------------------------------- /gitops/infrastructure/linkerd/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: linkerd 5 | labels: 6 | linkerd.io/admission-webhooks: disabled 7 | --- 8 | apiVersion: v1 9 | kind: Namespace 10 | metadata: 11 | name: linkerd-viz -------------------------------------------------------------------------------- /gitops/infrastructure/linkerd/release.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: linkerd 6 | namespace: linkerd 7 | spec: 8 | releaseName: linkerd 9 | chart: 10 | spec: 11 | chart: linkerd-control-plane 12 | version: '1.12.5' 13 | sourceRef: 14 | kind: HelmRepository 15 | name: linkerd 16 | namespace: flux-system 17 | interval: 1h0m0s 18 | install: 19 | remediation: 20 | retries: 3 21 | valuesFrom: 22 | - kind: Secret 23 | name: certs 24 | valuesKey: ca.crt 25 | targetPath: identityTrustAnchorsPEM 26 | - kind: Secret 27 | name: certs 28 | valuesKey: issuer.crt 29 | targetPath: identity.issuer.tls.crtPEM 30 | - kind: Secret 31 | name: certs 32 | valuesKey: issuer.key 33 | targetPath: identity.issuer.tls.keyPEM 34 | values: 35 | installNamespace: false 36 | identity: 37 | issuer: 38 | crtExpiry: ${cert_expiry} 39 | -------------------------------------------------------------------------------- /gitops/infrastructure/mysql/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: mysql 4 | resources: 5 | - namespace.yaml 6 | - release.yaml 7 | -------------------------------------------------------------------------------- /gitops/infrastructure/mysql/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: mysql 5 | -------------------------------------------------------------------------------- /gitops/infrastructure/mysql/release.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: mysql 6 | namespace: mysql 7 | spec: 8 | releaseName: mysql 9 | chart: 10 | spec: 11 | chart: mysql 12 | version: '9.10.5' 13 | sourceRef: 14 | kind: HelmRepository 15 | name: bitnami 16 | namespace: flux-system 17 | interval: 1h0m0s 18 | install: 19 | remediation: 20 | retries: 3 21 | values: 22 | auth: 23 | rootPassword: FTA@CNCF0n@zure3 24 | username: ftacncf 25 | password: FTA@CNCF0n@zure3 26 | global: 27 | storageClass: rook-ceph-block 28 | -------------------------------------------------------------------------------- /gitops/infrastructure/nginx/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: ingress-basic 4 | resources: 5 | - namespace.yaml 6 | - release.yaml 7 | -------------------------------------------------------------------------------- /gitops/infrastructure/nginx/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: ingress-basic 5 | labels: 6 | cert-manager.io/disable-validation: "true" -------------------------------------------------------------------------------- /gitops/infrastructure/nginx/release.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 3 | kind: HelmRelease 4 | metadata: 5 | name: nginx-ingress 6 | namespace: ingress-basic 7 | spec: 8 | releaseName: nginx-ingress 9 | chart: 10 | spec: 11 | chart: ingress-nginx 12 | version: '4.7.1' 13 | sourceRef: 14 | kind: HelmRepository 15 | name: ingress-nginx 16 | namespace: flux-system 17 | interval: 1h0m0s 18 | install: 19 | remediation: 20 | retries: 3 21 | # Default values 22 | # https://github.com/bitnami/charts/blob/master/bitnami/nginx-ingress-controller/values.yaml 23 | values: 24 | controller: 25 | replicaCount: 2 26 | electionID: ingress-controller-leader 27 | ingressClassResource: 28 | name: nginx 29 | enabled: true 30 | default: true 31 | controllerValue: k8s.io/nginx 32 | ingressClass: nginx 33 | pod: 34 | annotations: 35 | linkerd.io/inject: enabled 36 | config.linkerd.io/trace-collector: collector.linkerd-jaeger:55678 37 | service: 38 | annotations: 39 | service.beta.kubernetes.io/azure-dns-label-name: "${appHostDnsLabel}" 40 | service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path: "/healthz" 41 | nodeSelector: 42 | beta.kubernetes.io/os: linux 43 | defaultBackend: 44 | nodeSelector: 45 | beta.kubernetes.io/os: linux 46 | postRenderers: 47 | - kustomize: 48 | patchesStrategicMerge: 49 | - kind: Deployment 50 | apiVersion: apps/v1 51 | metadata: 52 | name: nginx-ingress-ingress-nginx-controller 53 | namespace: ingress-basic 54 | annotations: 55 | linkerd.io/inject: enabled 56 | -------------------------------------------------------------------------------- /gitops/infrastructure/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: monitoring 4 | resources: 5 | - namespace.yaml 6 | - release.yaml 7 | configMapGenerator: 8 | - name: helm-values 9 | files: 10 | - ../../../yml/prometheus-values.yaml 11 | generatorOptions: 12 | disableNameSuffixHash: true 13 | secretGenerator: 14 | - name: prometheus-kube-prometheus-prometheus-scrape-confg-linkerd 15 | files: 16 | - additional-scrape-configs.yaml=../../../yml/linkerd-prometheus-additional.yaml -------------------------------------------------------------------------------- /gitops/infrastructure/prometheus/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: monitoring -------------------------------------------------------------------------------- /gitops/infrastructure/prometheus/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 2 | kind: HelmRelease 3 | metadata: 4 | name: prometheus 5 | namespace: monitoring 6 | spec: 7 | releaseName: prometheus 8 | chart: 9 | spec: 10 | chart: kube-prometheus-stack 11 | version: '47.3.0' 12 | sourceRef: 13 | kind: HelmRepository 14 | name: prometheus-community 15 | namespace: flux-system 16 | interval: 1h0m0s 17 | postRenderers: 18 | - kustomize: 19 | patchesJson6902: 20 | - target: 21 | version: v1 22 | kind: Prometheus 23 | name: prometheus-kube-prometheus-prometheus 24 | patch: 25 | - op: replace 26 | path: /spec/additionalScrapeConfigs 27 | value: 28 | key: additional-scrape-configs.yaml 29 | name: prometheus-kube-prometheus-prometheus-scrape-confg-linkerd 30 | - target: 31 | version: v1 32 | kind: ServiceMonitor 33 | name: prometheus-kube-prometheus-kubelet 34 | patch: 35 | - op: replace 36 | path: /spec/endpoints 37 | value: 38 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 39 | interval: 10s 40 | port: https-metrics 41 | scheme: https 42 | tlsConfig: 43 | insecureSkipVerify: true 44 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 45 | honorLabels: true 46 | interval: 10s 47 | path: /metrics/cadvisor 48 | port: https-metrics 49 | scheme: https 50 | tlsConfig: 51 | insecureSkipVerify: true 52 | install: 53 | remediation: 54 | retries: 3 55 | valuesFrom: 56 | - kind: ConfigMap 57 | name: helm-values 58 | valuesKey: prometheus-values.yaml 59 | -------------------------------------------------------------------------------- /gitops/infrastructure/rook-cluster/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: rook-ceph 4 | resources: 5 | - ../../../yml/rook-cluster.yaml 6 | - ../../../yml/rook-storageclass.yaml -------------------------------------------------------------------------------- /gitops/infrastructure/rook/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: rook-ceph 4 | resources: 5 | - namespace.yaml 6 | - release.yaml 7 | configMapGenerator: 8 | - name: helm-values 9 | files: 10 | - ../../../yml/rook-values.yaml 11 | generatorOptions: 12 | disableNameSuffixHash: true -------------------------------------------------------------------------------- /gitops/infrastructure/rook/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: rook-ceph -------------------------------------------------------------------------------- /gitops/infrastructure/rook/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 2 | kind: HelmRelease 3 | metadata: 4 | name: rook-ceph 5 | namespace: rook-ceph 6 | spec: 7 | releaseName: rook-ceph 8 | chart: 9 | spec: 10 | chart: rook-ceph 11 | version: '1.11.9' 12 | sourceRef: 13 | kind: HelmRepository 14 | name: rook-release 15 | namespace: flux-system 16 | interval: 1h0m0s 17 | install: 18 | remediation: 19 | retries: 3 20 | valuesFrom: 21 | - kind: ConfigMap 22 | name: helm-values 23 | valuesKey: rook-values.yaml 24 | -------------------------------------------------------------------------------- /gitops/infrastructure/seed/harbor.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: harbor-seed 5 | namespace: harbor-system 6 | spec: 7 | parallelism: 1 8 | completions: 1 9 | template: 10 | metadata: 11 | name: harbor-seed 12 | spec: 13 | volumes: 14 | - name: harbor-seed-scripts-volume 15 | configMap: 16 | name: harbor-seed-scripts 17 | containers: 18 | - name: harbor-seed-job 19 | image: ubuntu:18.04 20 | volumeMounts: 21 | - mountPath: /harbor-seed-scripts 22 | name: harbor-seed-scripts-volume 23 | env: 24 | - name: HOME 25 | value: /tmp 26 | command: 27 | - /bin/sh 28 | - -c 29 | - | 30 | echo "scripts in /harbor-seed-scripts" 31 | ls -lh /harbor-seed-scripts 32 | echo "copy scripts to /tmp" 33 | cp /harbor-seed-scripts/*.json /tmp 34 | cp /harbor-seed-scripts/*.sh /tmp 35 | echo "apply 'chmod +x' to /tmp/*.sh" 36 | chmod +x /tmp/*.sh 37 | echo "execute harbor-seed.sh now" 38 | /tmp/harbor-seed.sh 39 | restartPolicy: Never 40 | --- 41 | apiVersion: v1 42 | items: 43 | - apiVersion: v1 44 | data: 45 | harbor-seed.sh: | 46 | echo "script-one.sh" 47 | apt update && apt -y install curl && apt -y install jq 48 | curl -u admin:admin -i -k -X POST "${externalUrl}/api/v2.0/projects" -d "@/tmp/harbor-project.json" -H "Content-Type: application/json" 49 | curl -u admin:admin -i -k -X POST "${externalUrl}/api/v2.0/users" -d "@/tmp/harbor-project-user.json" -H "Content-Type: application/json" 50 | conexpid=$(curl -u admin:admin -k -s -X GET "${externalUrl}/api/v2.0/projects?name=conexp" | jq '.[0].project_id') 51 | echo "project_id: $conexpid" 52 | curl -u admin:admin -i -k -X POST "${externalUrl}/api/v2.0/projects/$conexpid/members" -d "@/tmp/harbor-project-member.json" -H "Content-Type: application/json" 53 | harbor-project.json: | 54 | { 55 | "metadata": { 56 | "public": "false" 57 | }, 58 | "project_name": "conexp", 59 | "owner_id": 1, 60 | "owner_name": "admin" 61 | } 62 | harbor-project-user.json: | 63 | { 64 | "username": "conexp", 65 | "email": "conexp@host.local", 66 | "realname": "conexp", 67 | "password": "FTA@CNCF0n@zure3" 68 | } 69 | harbor-project-member.json: | 70 | {"role_id":2,"member_user":{"username":"conexp"}} 71 | kind: ConfigMap 72 | metadata: 73 | name: harbor-seed-scripts 74 | namespace: harbor-system 75 | kind: List 76 | metadata: {} -------------------------------------------------------------------------------- /gitops/infrastructure/seed/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - harbor.yaml 5 | - mysql.yaml 6 | -------------------------------------------------------------------------------- /gitops/infrastructure/seed/mysql.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: mysql-seed 5 | namespace: mysql 6 | spec: 7 | parallelism: 1 8 | completions: 1 9 | template: 10 | metadata: 11 | name: mysql-seed 12 | spec: 13 | volumes: 14 | - name: mysql-seed-scripts-volume 15 | configMap: 16 | name: mysql-seed-scripts 17 | containers: 18 | - name: mysql-seed-job 19 | image: ubuntu:18.04 20 | volumeMounts: 21 | - mountPath: /mysql-seed-scripts 22 | name: mysql-seed-scripts-volume 23 | env: 24 | - name: HOME 25 | value: /tmp 26 | command: 27 | - /bin/sh 28 | - -c 29 | - | 30 | echo "scripts in /mysql-seed-scripts" 31 | ls -lh /mysql-seed-scripts 32 | echo "copy scripts to /tmp" 33 | cp /mysql-seed-scripts/*.sh /tmp 34 | echo "apply 'chmod +x' to /tmp/*.sh" 35 | chmod +x /tmp/*.sh 36 | echo "execute mysql-seed.sh now" 37 | /tmp/mysql-seed.sh 38 | restartPolicy: Never 39 | --- 40 | apiVersion: v1 41 | items: 42 | - apiVersion: v1 43 | data: 44 | mysql-seed.sh: | 45 | #!/bin/bash 46 | echo "create-db.sh" 47 | apt-get update && apt-get install mysql-client -y 48 | mysql -h mysql --password=FTA@CNCF0n@zure3 < certs.yaml" 38 | 39 | echo "## Checking kube seal roolout status" 40 | kubectl rollout status deployment sealed-secrets-controller -n flux-system 41 | 42 | echo "## Download kube seal publick key" 43 | sudo sh -c "kubeseal --fetch-cert \ 44 | --controller-name=sealed-secrets-controller \ 45 | --controller-namespace=flux-system \ 46 | > ../../../pub-sealed-secrets.pem" 47 | 48 | echo "# Seal Certs Secrets" 49 | sudo sh -c "kubeseal --format=yaml --cert=../../../pub-sealed-secrets.pem \ 50 | < certs.yaml > certs-sealed.yaml" 51 | sudo rm certs.yaml ca.crt issuer.crt issuer.key ca.key 52 | 53 | cd ../../.. 54 | 55 | registryUrl=https://$registryHost 56 | appHostDnsLabel=`echo $appHostName | cut -d '.' -f 1` 57 | registryHostDnsLabel=`echo $registryHost | cut -d '.' -f 1` 58 | 59 | echo "## Replace tokens in yamls" 60 | exp=$(date -d '+8760 hour' +"%Y-%m-%dT%H:%M:%SZ") 61 | 62 | echo "## Generate secret for variable substitution" 63 | sudo sh -c "kubectl create secret generic gitops-variables --from-literal=registryHost=$registryHost \ 64 | --from-literal=registryUrl=$registryUrl \ 65 | --from-literal=externalUrl=$registryUrl \ 66 | --from-literal=cluster_issuer_email=$cluster_issuer_email \ 67 | --from-literal=cicdWebhookHost=$appHostName \ 68 | --from-literal=appHostName=$appHostName \ 69 | --from-literal=sendGridApiKey=$sendGridApiKey \ 70 | --from-literal=registryHostDnsLabel=$registryHostDnsLabel \ 71 | --from-literal=appHostDnsLabel=$appHostDnsLabel \ 72 | --from-literal=cert_expiry=$exp \ 73 | -n flux-system -oyaml --dry-run=client \ 74 | > gitops-variables.yaml" 75 | 76 | echo "## Seal secret for variable substitution" 77 | sudo sh -c "kubeseal --format=yaml --cert=pub-sealed-secrets.pem \ 78 | < gitops-variables.yaml > gitops-variables-sealed.yaml" 79 | sudo rm gitops-variables.yaml 80 | 81 | echo "## Apply secret for variable substitution" 82 | kubectl apply -f gitops-variables-sealed.yaml 83 | 84 | sudo rm gitops-variables-sealed.yaml 85 | 86 | cd gitops/app/core 87 | 88 | echo "## Generate registry secrets for conexp namespace" 89 | sudo sh -c "kubectl create secret docker-registry regcred \ 90 | --docker-server="https://$registryHost" --docker-username=conexp --docker-password=FTA@CNCF0n@zure3 --docker-email=user@mycompany.com -n conexp-mvp -oyaml --dry-run=client \ 91 | > regcred-conexp.yaml" 92 | 93 | echo "## Seal the secret" 94 | sudo sh -c "kubeseal --format=yaml --cert=../../../pub-sealed-secrets.pem \ 95 | < regcred-conexp.yaml > regcred-conexp-sealed.yaml" 96 | sudo rm regcred-conexp.yaml 97 | 98 | echo "## Generate registry secrets for conexp-mvp-fn namespace" 99 | sudo sh -c "kubectl create secret docker-registry regcred \ 100 | --docker-server="https://$registryHost" --docker-username=conexp --docker-password=FTA@CNCF0n@zure3 --docker-email=user@mycompany.com -n conecp-mvp-fn -oyaml --dry-run=client \ 101 | > regcred-fn.yaml" 102 | 103 | echo "## Seal the secret" 104 | sudo sh -c "kubeseal --format=yaml --cert=../../../pub-sealed-secrets.pem \ 105 | < regcred-fn.yaml > regcred-fn-sealed.yaml" 106 | sudo rm regcred-fn.yaml 107 | 108 | cd ../../.. 109 | 110 | cd gitops/app/devops 111 | 112 | echo "## Create dockerconfig for tekton pipelines" 113 | CONFIG="\ 114 | {\n 115 | \"auths\": {\n 116 | \"${registryHost}\": {\n 117 | \"username\": \"conexp\",\n 118 | \"password\": \"FTA@CNCF0n@zure3\",\n 119 | \"email\": \"user@mycompany.com\",\n 120 | \"auth\": \"Y29uZXhwOkZUQUBDTkNGMG5AenVyZTM=\"\n 121 | }\n 122 | }\n 123 | }\n" 124 | 125 | printf "${CONFIG}" > config.json 126 | 127 | echo "## Generate registry secrets for tekton pipelines" 128 | sudo sh -c "kubectl create secret generic regcred --from-file=config.json=config.json -n conexp-mvp-devops -oyaml --dry-run=client > regcred-devops.yaml" 129 | rm config.json 130 | 131 | echo "## Seal the secret" 132 | sudo sh -c "kubeseal --format=yaml --cert=../../../pub-sealed-secrets.pem \ 133 | < regcred-devops.yaml > regcred-devops-sealed.yaml" 134 | rm regcred-devops.yaml 135 | 136 | cd ../../.. 137 | 138 | rm step-cli_0.23.4_amd64.deb pub-sealed-secrets.pem 139 | 140 | echo "## Commit the changes to the git repo" 141 | sudo git remote set-url origin "https://$owner:$GITHUB_TOKEN@github.com/$owner/cloud-native-app.git" 142 | sudo git config user.email "$cluster_issuer_email" 143 | sudo git config user.name "Auto" 144 | sudo git pull 145 | sudo git add * 146 | sudo git commit -m "Auto commit prep files" 147 | sudo git push -u origin main 148 | 149 | echo "## Add Kustomization to reconcile the app" 150 | flux create kustomization cloud-native-app \ 151 | --depends-on=flux-system \ 152 | --source=flux-system \ 153 | --path="gitops/clusters/production" \ 154 | --prune=true \ 155 | --interval=5m 156 | 157 | echo "## Create the webhook for continous integration" 158 | curl -H "Authorization: token $GITHUB_TOKEN" \ 159 | -X POST \ 160 | -H "Accept: application/vnd.github.v3+json" \ 161 | https://api.github.com/repos/$owner/cloud-native-app/hooks \ 162 | -d "{\"config\":{\"url\":\"https://$appHostName/cd\",\"content_type\":\"json\"}}" 163 | 164 | -------------------------------------------------------------------------------- /images/cncf-projects-app-arc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/cloud-native-app/d2f68dec4290621e1f526153cb8eb0fbc34fd71c/images/cncf-projects-app-arc.png -------------------------------------------------------------------------------- /json/harbor-project-member.json: -------------------------------------------------------------------------------- 1 | {"role_id":2,"member_user":{"username":"conexp"}} 2 | -------------------------------------------------------------------------------- /json/harbor-project-user.json: -------------------------------------------------------------------------------- 1 | { 2 | "username": "conexp", 3 | "email": "conexp@host.local", 4 | "realname": "co nexp", 5 | "password": "" 6 | } 7 | -------------------------------------------------------------------------------- /json/harbor-project.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "public": "false" 4 | }, 5 | "project_name": "conexp", 6 | "owner_id": 1, 7 | "owner_name": "admin" 8 | } 9 | -------------------------------------------------------------------------------- /src/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | [b|B]in 3 | [O|o]bj 4 | Contoso.Common/bin 5 | Contoso.Common/obj 6 | Contoso.Expenses.Web/bin 7 | Contoso.Expenses.Web/obj -------------------------------------------------------------------------------- /src/.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /src/Contoso.Common/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | [b|B]in 3 | [O|o]bj -------------------------------------------------------------------------------- /src/Contoso.Common/Contoso.Expenses.Common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net7.0 4 | false 5 | false 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Contoso.Common/Models/ContosoExpensesWebContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | namespace Contoso.Expenses.Common.Models 8 | { 9 | // see https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/new-db?tabs=visual-studio 10 | public class ContosoExpensesWebContext : DbContext 11 | { 12 | public ContosoExpensesWebContext (DbContextOptions options) 13 | : base(options) 14 | { 15 | } 16 | 17 | public DbSet Expense { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Contoso.Common/Models/Expense.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | 8 | namespace Contoso.Expenses.Common.Models 9 | { 10 | public class Expense 11 | { 12 | public int ExpenseId { get; set; } 13 | 14 | public string Purpose { get; set; } 15 | 16 | [DataType(DataType.Date)] 17 | public DateTime Date { get; set; } 18 | 19 | [Display(Name = "Cost Center")] 20 | public string CostCenter { get; set; } 21 | 22 | [Column(TypeName = "decimal(18, 2)")] 23 | public decimal Amount { get; set; } 24 | 25 | [Display(Name = "Approver Email")] 26 | [DataType(DataType.EmailAddress)] 27 | public string ApproverEmail { get; set; } 28 | 29 | [Display(Name = "Submitter Email")] 30 | [DataType(DataType.EmailAddress)] 31 | public string SubmitterEmail { get; set; } 32 | 33 | [Display(Name = "Receipt Provided, if not please explain")] 34 | public string Receipt { get; set;} 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.API/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | [b|B]in 3 | [O|o]bj -------------------------------------------------------------------------------- /src/Contoso.Expenses.API/Contoso.Expenses.API.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net7.0 4 | 6577435a-bf95-418a-b180-3f26ed5c0076 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.API/Controllers/CostCenterController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Contoso.Expenses.API.Database; 5 | using Contoso.Expenses.API.Models; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.AspNetCore.Mvc; 8 | 9 | namespace Contoso.Expenses.API.Controllers 10 | { 11 | [Route("api/[controller]")] 12 | [ApiController] 13 | public class CostCenterController : ControllerBase 14 | { 15 | private readonly DatabaseContext _dbctx; 16 | 17 | public CostCenterController(DatabaseContext databaseContext) 18 | { 19 | _dbctx = databaseContext; 20 | } 21 | 22 | // GET: api/CostCenter 23 | [HttpGet] 24 | public IEnumerable Get() 25 | { 26 | return _dbctx.CostCenters.ToList(); 27 | } 28 | 29 | // GET: api/CostCenter/umar@hotmail.com 30 | [HttpGet("{email}", Name = "Get")] 31 | public CostCenter Get(string email) 32 | { 33 | var costCenter = _dbctx.CostCenters.FirstOrDefault(x => x.SubmitterEmail == email); 34 | 35 | if (costCenter != null) 36 | { 37 | Console.WriteLine("Cost Center with email {0} found.", email); 38 | return costCenter; 39 | } 40 | else 41 | { 42 | Console.WriteLine("Cost Center with email {0} not found.", email); 43 | return null; 44 | } 45 | } 46 | 47 | // POST: api/CostCenter 48 | [HttpPost] 49 | public IActionResult Post([FromBody] CostCenter model) 50 | { 51 | try 52 | { 53 | _dbctx.CostCenters.Add(model); 54 | _dbctx.SaveChanges(); 55 | return StatusCode(StatusCodes.Status201Created, model); 56 | } 57 | catch (Exception ex) 58 | { 59 | return StatusCode(StatusCodes.Status500InternalServerError, ex); 60 | } 61 | } 62 | 63 | // DELETE: api/CostCenter/umar@hotmail.com 64 | [HttpDelete("{email}")] 65 | public IActionResult Delete(string email) 66 | { 67 | var costCenter = Get(email); 68 | 69 | if (costCenter != null) 70 | { 71 | _dbctx.CostCenters.Remove(Get(email)); 72 | _dbctx.SaveChanges(); 73 | Console.WriteLine("Cost Center with email {0} deleted successfully." + email); 74 | return StatusCode(StatusCodes.Status200OK, costCenter); 75 | } 76 | else 77 | { 78 | Console.WriteLine("Cost Center with email {0} not found. No action taken." + email); 79 | return StatusCode(StatusCodes.Status500InternalServerError, string.Format("Cost Center with email {0} not found. No action taken.", email)); 80 | } 81 | 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.API/Database/DatabaseContext.cs: -------------------------------------------------------------------------------- 1 | using Contoso.Expenses.API.Models; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace Contoso.Expenses.API.Database 5 | { 6 | public class DatabaseContext : DbContext 7 | { 8 | public DatabaseContext(DbContextOptions options) : base(options) 9 | { 10 | } 11 | 12 | public DbSet CostCenters { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.API/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build 2 | WORKDIR /app 3 | 4 | COPY *.csproj ./ 5 | RUN dotnet restore 6 | 7 | COPY . ./ 8 | RUN dotnet publish -c Release -o out 9 | 10 | FROM mcr.microsoft.com/dotnet/aspnet:7.0 11 | WORKDIR /app 12 | COPY --from=build /app/out . 13 | ENTRYPOINT ["dotnet", "Contoso.Expenses.API.dll"] -------------------------------------------------------------------------------- /src/Contoso.Expenses.API/Models/ConfigValues.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Contoso.Expenses.API.Models 7 | { 8 | public class ConfigValues 9 | { 10 | public string SQLConnectionString { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.API/Models/CostCenter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Contoso.Expenses.API.Models 8 | { 9 | public class CostCenter 10 | { 11 | public int CostCenterId { get; set; } 12 | public string SubmitterEmail { get; set; } 13 | public string ApproverEmail { get; set; } 14 | public string CostCenterName { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.API/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Contoso.Expenses.API 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.API/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:59282", 8 | "sslPort": 44375 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "api/values", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "Contoso.Expenses.API": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "api/values", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Contoso.Expenses.API/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Contoso.Expenses.API.Database; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | using Microsoft.OpenApi.Models; 10 | 11 | namespace Contoso.Expenses.API 12 | { 13 | public class Startup 14 | { 15 | public IConfiguration Configuration { get; } 16 | 17 | public Startup(IWebHostEnvironment env) 18 | { 19 | var builder = new ConfigurationBuilder() 20 | .SetBasePath(env.ContentRootPath) 21 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 22 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) 23 | .AddEnvironmentVariables(); 24 | 25 | if (env.IsDevelopment()) 26 | { 27 | builder.AddUserSecrets(); 28 | } 29 | 30 | Configuration = builder.Build(); 31 | 32 | //Configuration = configuration; 33 | } 34 | 35 | // This method gets called by the runtime. Use this method to add services to the container. 36 | public void ConfigureServices(IServiceCollection services) 37 | { 38 | services.AddControllers(); 39 | 40 | string connectionString = Configuration["ConnectionStrings:DBConnectionString"]; 41 | ServerVersion version = ServerVersion.AutoDetect(connectionString); 42 | services.AddDbContextPool(options => options.UseMySql(connectionString, version)); 43 | 44 | // Register the Swagger generator, defining 1 or more Swagger documents 45 | services.AddSwaggerGen(c => 46 | { 47 | c.SwaggerDoc("v1", new OpenApiInfo 48 | { 49 | Title = "Contoso Expenses API", 50 | Version = "1.0", 51 | 52 | Description = "Contoso Expenses API exposes several APIs to get Cost Center details, Create New Expense(ToDo), etc.", 53 | TermsOfService = new Uri("http://www.azurefasttrack.com") 54 | }); 55 | }); 56 | services.AddApplicationInsightsTelemetry(); 57 | } 58 | 59 | 60 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 61 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DatabaseContext context) 62 | { 63 | // Enable middleware to serve generated Swagger as a JSON endpoint. 64 | app.UseSwagger(); 65 | 66 | // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), 67 | // specifying the Swagger JSON endpoint. 68 | app.UseSwaggerUI(c => 69 | { 70 | c.SwaggerEndpoint("/swagger/v1/swagger.json", "Contoso Expenses API v1"); 71 | }); 72 | 73 | 74 | if (env.IsDevelopment()) 75 | { 76 | app.UseDeveloperExceptionPage(); 77 | } 78 | 79 | app.UseHttpsRedirection(); 80 | 81 | app.UseRouting(); 82 | 83 | app.UseAuthorization(); 84 | 85 | app.UseEndpoints(endpoints => 86 | { 87 | endpoints.MapControllers(); 88 | }); 89 | 90 | context.Database.EnsureCreated(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "ConnectionStrings": { 10 | "DBConnectionString": "" 11 | }, 12 | "AllowedHosts": "*" 13 | } -------------------------------------------------------------------------------- /src/Contoso.Expenses.Function/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | [b|B]in 3 | [O|o]bj -------------------------------------------------------------------------------- /src/Contoso.Expenses.Function/Contoso.Expenses.Function.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | 089c0e50-bec7-4320-be62-1fbfcc1d6fc7 5 | Linux 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Function/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:7.0 as base 2 | WORKDIR /app 3 | EXPOSE 80 4 | EXPOSE 443 5 | 6 | FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build 7 | WORKDIR /src 8 | COPY ["Contoso.Expenses.Function/Contoso.Expenses.Function.csproj", "Contoso.Expenses.Function/"] 9 | COPY ["Contoso.Common/Contoso.Expenses.Common.csproj", "Contoso.Common/"] 10 | RUN dotnet restore "Contoso.Expenses.Function/Contoso.Expenses.Function.csproj" 11 | COPY . . 12 | WORKDIR "/src/Contoso.Expenses.Function" 13 | RUN dotnet build "Contoso.Expenses.Function.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "Contoso.Expenses.Function.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | 22 | ENTRYPOINT ["dotnet", "Contoso.Expenses.Function.dll"] -------------------------------------------------------------------------------- /src/Contoso.Expenses.Function/EmailHandler.cs: -------------------------------------------------------------------------------- 1 | using Contoso.Expenses.Common.Models; 2 | using Microsoft.AspNetCore.Http; 3 | using SendGrid; 4 | using SendGrid.Helpers.Mail; 5 | using System; 6 | using System.IO; 7 | using System.Text.Json; 8 | using System.Threading.Tasks; 9 | 10 | namespace Contoso.Expenses.Function 11 | { 12 | public class EmailHandler 13 | { 14 | public async Task<(int, string)> Handle(HttpRequest request) 15 | { 16 | var reader = new StreamReader(request.Body); 17 | var input = await reader.ReadToEndAsync(); 18 | input = input.Substring(1, input.Length - 2); 19 | input = input.Replace("\\", String.Empty ); 20 | Console.WriteLine($"Input: {input}"); 21 | Expense expense = JsonSerializer.Deserialize(input); 22 | var apiKey = Environment.GetEnvironmentVariable("SENDGRID_API_KEY"); 23 | var client = new SendGridClient(apiKey); 24 | 25 | string emailFrom = "Expense@ContosoExpenses.com"; 26 | string emailTo = expense.ApproverEmail; 27 | string emailSubject = $"New Expense for the amount of ${expense.Amount} submitted"; 28 | string emailBody = $"Hello {expense.ApproverEmail},
New Expense report submitted for the purpose of: {expense.Purpose}.
Please review as soon as possible.


This is a auto generated email, please do not reply to this email"; 29 | 30 | Console.WriteLine($"Email Subject: {emailSubject}"); 31 | Console.WriteLine($"Email body: {emailBody}"); 32 | 33 | var message = new SendGridMessage(); 34 | message.From = new EmailAddress(emailFrom, "Contoso Expenses"); 35 | message.AddTo(emailTo, null); 36 | message.Subject = emailSubject; 37 | message.AddContent(MimeType.Html, emailBody); 38 | 39 | var response = await client.SendEmailAsync(message); 40 | 41 | Console.WriteLine($"Email sent successfully to: {emailTo}"); 42 | 43 | Console.WriteLine($"Input received is: {input}"); 44 | return (200, $"Input received is: {input}"); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Function/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Contoso.Expenses.Function 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Function/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:53724", 7 | "sslPort": 44397 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Contoso.Expenses.Function": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 25 | }, 26 | "Docker": { 27 | "commandName": "Docker", 28 | "launchBrowser": true, 29 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 30 | "publishAllPorts": true, 31 | "useSSL": true 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Contoso.Expenses.Function/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using CloudNative.CloudEvents.NewtonsoftJson; 7 | using System; 8 | using CloudNative.CloudEvents; 9 | 10 | namespace Contoso.Expenses.Function 11 | { 12 | public class Startup 13 | { 14 | // This method gets called by the runtime. Use this method to add services to the container. 15 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 16 | public void ConfigureServices(IServiceCollection services) 17 | { 18 | } 19 | 20 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 21 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 22 | { 23 | app.Run(async (context) => 24 | { 25 | Console.WriteLine("context.Request.Path: {0}", context.Request.Path); 26 | 27 | if (context.Request.Path != "/") 28 | { 29 | context.Response.StatusCode = 404; 30 | await context.Response.WriteAsync("404 - Not Found"); 31 | return; 32 | } 33 | 34 | Console.WriteLine("context.Request.Method: {0}", context.Request.Method); 35 | if (context.Request.Method != "POST") 36 | { 37 | context.Response.StatusCode = 405; 38 | await context.Response.WriteAsync("405 - Only POST method allowed"); 39 | return; 40 | } 41 | 42 | try 43 | { 44 | var (status, text) = await new EmailHandler().Handle(context.Request); 45 | context.Response.StatusCode = status; 46 | if (!string.IsNullOrEmpty(text)) 47 | await context.Response.WriteAsync(text); 48 | } 49 | catch (NotImplementedException nie) 50 | { 51 | Console.WriteLine(nie.ToString()); 52 | context.Response.StatusCode = 501; 53 | await context.Response.WriteAsync(nie.ToString()); 54 | } 55 | catch (Exception ex) 56 | { 57 | Console.WriteLine(ex.ToString()); 58 | context.Response.StatusCode = 500; 59 | await context.Response.WriteAsync(ex.ToString()); 60 | } 61 | }); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Function/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Function/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | [b|B]in 3 | [O|o]bj -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/AppMetrics/MetricsRegistry.cs: -------------------------------------------------------------------------------- 1 | using App.Metrics; 2 | using App.Metrics.Counter; 3 | 4 | namespace Contoso.Expenses.Web.AppMetrics 5 | { 6 | public class MetricsRegistry 7 | { 8 | public static CounterOptions CreatedExpenseCounter => new CounterOptions 9 | { 10 | // App Metrics counter to track number of expenses submitted 11 | Name = "Number of Expenses Submitted", 12 | Context = "Contoso Expenses Web App", 13 | MeasurementUnit = Unit.Calls 14 | }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Contoso.Expenses.Web.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net7.0 4 | abb56eae-8a6c-4d37-8d63-51ecaa019cba 5 | false 6 | false 7 | false 8 | Linux 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build 2 | WORKDIR /app 3 | 4 | COPY ./Contoso.Common ./Contoso.Common/ 5 | COPY ./Contoso.Expenses.Web ./Contoso.Expenses.Web/ 6 | 7 | WORKDIR /app/Contoso.Expenses.Web 8 | 9 | RUN dotnet publish -c Release -o out 10 | 11 | FROM mcr.microsoft.com/dotnet/aspnet:7.0 12 | WORKDIR /app 13 | COPY --from=build /app/Contoso.Expenses.Web/out . 14 | 15 | ENTRYPOINT ["dotnet", "Contoso.Expenses.Web.dll"] -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Models/ConfigValues.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Contoso.Expenses.Web.Models 7 | { 8 | public class ConfigValues 9 | { 10 | public string CostCenterAPIUrl { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Models/CostCenter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Contoso.Expenses.Web.Models 7 | { 8 | public class CostCenter 9 | { 10 | [System.ComponentModel.DataAnnotations.Key] 11 | public string SubmitterEmail { get; set; } 12 | public string ApproverEmail { get; set; } 13 | public string CostCenterName { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Models/QueueInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Contoso.Expenses.Web.Models 7 | { 8 | public class QueueInfo 9 | { 10 | public string ConnectionString { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/About.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AboutModel 3 | @{ 4 | ViewData["Title"] = "About"; 5 | } 6 |

@ViewData["Title"]

7 |

@Model.Message

8 | 9 |

Use this area to provide additional information.

10 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/About.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | 7 | namespace Contoso.Expenses.Web.Pages 8 | { 9 | public class AboutModel : PageModel 10 | { 11 | public string Message { get; set; } 12 | 13 | public void OnGet() 14 | { 15 | Message = "Your application description page."; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ContactModel 3 | @{ 4 | ViewData["Title"] = "Contact"; 5 | } 6 |

@ViewData["Title"]

7 |

@Model.Message

8 | 9 |
10 | One Microsoft Way
11 | Redmond, WA 98052-6399
12 | P: 13 | 425.555.0100 14 |
15 | 16 |
17 | Support: Support@example.com
18 | Marketing: Marketing@example.com 19 |
20 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Contact.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | 7 | namespace Contoso.Expenses.Web.Pages 8 | { 9 | public class ContactModel : PageModel 10 | { 11 | public string Message { get; set; } 12 | 13 | public void OnGet() 14 | { 15 | Message = "Your contact page."; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

13 | Request ID: @Model.RequestId 14 |

15 | } 16 | 17 |

Development Mode

18 |

19 | Swapping to Development environment will display more detailed information about the error that occurred. 20 |

21 |

22 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 23 |

24 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | 9 | namespace Contoso.Expenses.Web.Pages 10 | { 11 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 12 | public class ErrorModel : PageModel 13 | { 14 | public string RequestId { get; set; } 15 | 16 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 17 | 18 | public void OnGet() 19 | { 20 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Expenses/Create.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Contoso.Expenses.Web.Pages.Expenses.CreateModel 3 | 4 | @{ 5 | ViewData["Title"] = "Create"; 6 | } 7 | 8 |

Create

9 | 10 |

Expense

11 |
12 |
13 |
14 |
15 |
16 |
17 | 18 | 19 | 20 |
21 |
22 | 23 | 24 | 25 |
26 | @*
27 | 28 | 29 | 30 |
*@ 31 |
32 | 33 | 34 | 35 |
36 |
37 | 38 | 39 | 40 |
41 |
42 | 43 | 44 | 45 |
46 |
47 | 48 | 49 | 50 |
51 | @*
52 |
53 | 56 |
57 |
*@ 58 |
59 | 60 |
61 |
62 |
63 |
64 | 65 |
66 | Back to List 67 |
68 | 69 | @section Scripts { 70 | @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} 71 | } 72 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Expenses/Create.cshtml.cs: -------------------------------------------------------------------------------- 1 | using App.Metrics; 2 | using CloudNative.CloudEvents; 3 | using CloudNative.CloudEvents.Http; 4 | using CloudNative.CloudEvents.NewtonsoftJson; 5 | using Contoso.Expenses.Common.Models; 6 | using Contoso.Expenses.Web.AppMetrics; 7 | using Contoso.Expenses.Web.Models; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Microsoft.AspNetCore.Mvc.RazorPages; 11 | using Microsoft.Extensions.Hosting; 12 | using Microsoft.Extensions.Options; 13 | using Newtonsoft.Json; 14 | using System; 15 | using System.Net.Http; 16 | using System.Net.Http.Headers; 17 | using System.Net.Mime; 18 | using System.Threading.Tasks; 19 | 20 | namespace Contoso.Expenses.Web.Pages.Expenses 21 | { 22 | public class CreateModel : PageModel 23 | { 24 | private readonly ContosoExpensesWebContext _context; 25 | private string costCenterAPIUrl; 26 | private readonly QueueInfo _queueInfo; 27 | private readonly IWebHostEnvironment _env; 28 | private readonly IMetrics _metrics; 29 | 30 | private string Source { get; } = "urn:contoso.web"; 31 | private string Type { get; } = "contoso.web.dispatchemail"; 32 | 33 | public CreateModel(ContosoExpensesWebContext context, IOptions config, QueueInfo queueInfo, 34 | IWebHostEnvironment env, IMetrics metrics) 35 | { 36 | _metrics = metrics; 37 | _context = context; 38 | costCenterAPIUrl = config.Value.CostCenterAPIUrl; 39 | _queueInfo = queueInfo; 40 | _env = env; 41 | } 42 | 43 | public IActionResult OnGet() 44 | { 45 | return Page(); 46 | } 47 | 48 | [BindProperty] 49 | public Expense Expense { get; set; } 50 | 51 | public async Task OnPostAsync() 52 | { 53 | if (!ModelState.IsValid) 54 | { 55 | return Page(); 56 | } 57 | 58 | // Look up cost center 59 | CostCenter costCenter = await GetCostCenterAsync(costCenterAPIUrl, Expense.SubmitterEmail); 60 | if (costCenter != null) 61 | { 62 | Expense.CostCenter = costCenter.CostCenterName; 63 | Expense.ApproverEmail = costCenter.ApproverEmail; 64 | } 65 | else 66 | { 67 | Expense.CostCenter = "Unkown"; 68 | Expense.ApproverEmail = "Unknown"; 69 | } 70 | 71 | // Write to DB, but don't wait right now 72 | _context.Expense.Add(Expense); 73 | Task t = _context.SaveChangesAsync(); 74 | 75 | if (!_env.IsEnvironment("Development")) 76 | { 77 | var cloudEvent = new CloudEvent 78 | { 79 | Id = Guid.NewGuid().ToString(), 80 | Type = Type, 81 | Source = new Uri(Source), 82 | DataContentType = MediaTypeNames.Application.Json, 83 | Data = JsonConvert.SerializeObject(Expense) 84 | }; 85 | 86 | var content = cloudEvent.ToHttpContent(ContentMode.Structured, new JsonEventFormatter()); 87 | 88 | var httpClient = new HttpClient(); 89 | var result = await httpClient.PostAsync(_queueInfo.ConnectionString, content); 90 | 91 | } 92 | // Ensure the DB write is complete 93 | t.Wait(); 94 | 95 | // This is to track customer App Metrics counter to track number of expenses submitted 96 | _metrics.Measure.Counter.Increment(MetricsRegistry.CreatedExpenseCounter); 97 | 98 | return RedirectToPage("./Index"); 99 | } 100 | 101 | private static async Task GetCostCenterAsync(string apiBaseURL, string email) 102 | { 103 | string requestUri = "api/costcenter" + "/" + email; 104 | 105 | using (var client = new HttpClient()) 106 | { 107 | client.BaseAddress = new Uri(apiBaseURL); 108 | client.DefaultRequestHeaders.Accept.Clear(); 109 | client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 110 | 111 | HttpResponseMessage httpResponse = await client.GetAsync(requestUri); 112 | 113 | if (httpResponse.IsSuccessStatusCode) 114 | { 115 | //CostCenter costCenter = await httpResponse.Content.ReadAsAsync(); 116 | var response = await httpResponse.Content.ReadAsStringAsync(); 117 | var costCenter = JsonConvert.DeserializeObject(response); 118 | 119 | if (costCenter != null) 120 | Console.WriteLine("SubmitterEmail: {0} \r\n ApproverEmail: {1} \r\n CostCenterName: {2}", 121 | costCenter.SubmitterEmail, costCenter.ApproverEmail, costCenter.CostCenterName); 122 | return costCenter; 123 | } 124 | else 125 | { 126 | Console.WriteLine("Internal server error: " + httpResponse.StatusCode); 127 | return null; 128 | } 129 | } 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Expenses/Delete.cshtml: -------------------------------------------------------------------------------- 1 | @page "{id:int}" 2 | @model Contoso.Expenses.Web.Pages.Expenses.DeleteModel 3 | 4 | @{ 5 | ViewData["Title"] = "Delete"; 6 | } 7 | 8 |

Delete

9 | 10 |

Are you sure you want to delete this?

11 |
12 |

Expense

13 |
14 |
15 |
16 | @Html.DisplayNameFor(model => model.Expense.Purpose) 17 |
18 |
19 | @Html.DisplayFor(model => model.Expense.Purpose) 20 |
21 |
22 | @Html.DisplayNameFor(model => model.Expense.Date) 23 |
24 |
25 | @Html.DisplayFor(model => model.Expense.Date) 26 |
27 |
28 | @Html.DisplayNameFor(model => model.Expense.CostCenter) 29 |
30 |
31 | @Html.DisplayFor(model => model.Expense.CostCenter) 32 |
33 |
34 | @Html.DisplayNameFor(model => model.Expense.Amount) 35 |
36 |
37 | @Html.DisplayFor(model => model.Expense.Amount) 38 |
39 |
40 | @Html.DisplayNameFor(model => model.Expense.ApproverEmail) 41 |
42 |
43 | @Html.DisplayFor(model => model.Expense.ApproverEmail) 44 |
45 |
46 | @Html.DisplayNameFor(model => model.Expense.SubmitterEmail) 47 |
48 |
49 | @Html.DisplayFor(model => model.Expense.SubmitterEmail) 50 |
51 |
52 | @Html.DisplayNameFor(model => model.Expense.Receipt) 53 |
54 |
55 | @Html.DisplayFor(model => model.Expense.Receipt) 56 |
57 |
58 | 59 |
60 | 61 | | 62 | Back to List 63 |
64 |
65 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Expenses/Delete.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | using Microsoft.EntityFrameworkCore; 8 | using Contoso.Expenses.Common.Models; 9 | 10 | namespace Contoso.Expenses.Web.Pages.Expenses 11 | { 12 | public class DeleteModel : PageModel 13 | { 14 | private readonly ContosoExpensesWebContext _context; 15 | 16 | public DeleteModel(ContosoExpensesWebContext context) 17 | { 18 | _context = context; 19 | } 20 | 21 | [BindProperty] 22 | public Expense Expense { get; set; } 23 | 24 | public async Task OnGetAsync(int? id) 25 | { 26 | if (id == null) 27 | { 28 | return NotFound(); 29 | } 30 | 31 | Expense = await _context.Expense.FirstOrDefaultAsync(m => m.ExpenseId == id); 32 | 33 | if (Expense == null) 34 | { 35 | return NotFound(); 36 | } 37 | return Page(); 38 | } 39 | 40 | public async Task OnPostAsync(int? id) 41 | { 42 | if (id == null) 43 | { 44 | return NotFound(); 45 | } 46 | 47 | Expense = await _context.Expense.FindAsync(id); 48 | 49 | if (Expense != null) 50 | { 51 | _context.Expense.Remove(Expense); 52 | await _context.SaveChangesAsync(); 53 | } 54 | 55 | return RedirectToPage("./Index"); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Expenses/Details.cshtml: -------------------------------------------------------------------------------- 1 | @page "{id:int}" 2 | @model Contoso.Expenses.Web.Pages.Expenses.DetailsModel 3 | 4 | @{ 5 | ViewData["Title"] = "Details"; 6 | } 7 | 8 |

Details

9 | 10 |
11 |

Expense

12 |
13 |
14 |
15 | @Html.DisplayNameFor(model => model.Expense.Purpose) 16 |
17 |
18 | @Html.DisplayFor(model => model.Expense.Purpose) 19 |
20 |
21 | @Html.DisplayNameFor(model => model.Expense.Date) 22 |
23 |
24 | @Html.DisplayFor(model => model.Expense.Date) 25 |
26 |
27 | @Html.DisplayNameFor(model => model.Expense.CostCenter) 28 |
29 |
30 | @Html.DisplayFor(model => model.Expense.CostCenter) 31 |
32 |
33 | @Html.DisplayNameFor(model => model.Expense.Amount) 34 |
35 |
36 | @Html.DisplayFor(model => model.Expense.Amount) 37 |
38 |
39 | @Html.DisplayNameFor(model => model.Expense.ApproverEmail) 40 |
41 |
42 | @Html.DisplayFor(model => model.Expense.ApproverEmail) 43 |
44 |
45 | @Html.DisplayNameFor(model => model.Expense.SubmitterEmail) 46 |
47 |
48 | @Html.DisplayFor(model => model.Expense.SubmitterEmail) 49 |
50 |
51 | @Html.DisplayNameFor(model => model.Expense.Receipt) 52 |
53 |
54 | @Html.DisplayFor(model => model.Expense.Receipt) 55 |
56 |
57 |
58 |
59 | Edit | 60 | Back to List 61 |
62 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Expenses/Details.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | using Microsoft.EntityFrameworkCore; 8 | using Contoso.Expenses.Common.Models; 9 | 10 | namespace Contoso.Expenses.Web.Pages.Expenses 11 | { 12 | public class DetailsModel : PageModel 13 | { 14 | private readonly ContosoExpensesWebContext _context; 15 | 16 | public DetailsModel(ContosoExpensesWebContext context) 17 | { 18 | _context = context; 19 | } 20 | 21 | public Expense Expense { get; set; } 22 | 23 | public async Task OnGetAsync(int? id) 24 | { 25 | if (id == null) 26 | { 27 | return NotFound(); 28 | } 29 | 30 | Expense = await _context.Expense.FirstOrDefaultAsync(m => m.ExpenseId == id); 31 | 32 | if (Expense == null) 33 | { 34 | return NotFound(); 35 | } 36 | return Page(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Expenses/Edit.cshtml: -------------------------------------------------------------------------------- 1 | @page "{id:int}" 2 | @model Contoso.Expenses.Web.Pages.Expenses.EditModel 3 | 4 | @{ 5 | ViewData["Title"] = "Edit"; 6 | } 7 | 8 |

Edit

9 | 10 |

Expense

11 |
12 |
13 |
14 |
15 |
16 | 17 |
18 | 19 | 20 | 21 |
22 |
23 | 24 | 25 | 26 |
27 |
28 | 29 | 30 | 31 |
32 |
33 | 34 | 35 | 36 |
37 |
38 | 39 | 40 | 41 |
42 |
43 | 44 | 45 | 46 |
47 |
48 |
49 | 52 |
53 |
54 |
55 | 56 |
57 |
58 |
59 |
60 | 61 |
62 | Back to List 63 |
64 | 65 | @section Scripts { 66 | @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} 67 | } 68 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Expenses/Edit.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | using Microsoft.AspNetCore.Mvc.Rendering; 8 | using Microsoft.EntityFrameworkCore; 9 | using Contoso.Expenses.Common.Models; 10 | 11 | namespace Contoso.Expenses.Web.Pages.Expenses 12 | { 13 | public class EditModel : PageModel 14 | { 15 | private readonly ContosoExpensesWebContext _context; 16 | 17 | public EditModel(ContosoExpensesWebContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | [BindProperty] 23 | public Expense Expense { get; set; } 24 | 25 | public async Task OnGetAsync(int? id) 26 | { 27 | if (id == null) 28 | { 29 | return NotFound(); 30 | } 31 | 32 | Expense = await _context.Expense.FirstOrDefaultAsync(m => m.ExpenseId == id); 33 | 34 | if (Expense == null) 35 | { 36 | return NotFound(); 37 | } 38 | return Page(); 39 | } 40 | 41 | public async Task OnPostAsync() 42 | { 43 | if (!ModelState.IsValid) 44 | { 45 | return Page(); 46 | } 47 | 48 | _context.Attach(Expense).State = EntityState.Modified; 49 | 50 | try 51 | { 52 | await _context.SaveChangesAsync(); 53 | } 54 | catch (DbUpdateConcurrencyException) 55 | { 56 | if (!ExpenseExists(Expense.ExpenseId)) 57 | { 58 | return NotFound(); 59 | } 60 | else 61 | { 62 | throw; 63 | } 64 | } 65 | 66 | return RedirectToPage("./Index"); 67 | } 68 | 69 | private bool ExpenseExists(int id) 70 | { 71 | return _context.Expense.Any(e => e.ExpenseId == id); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Expenses/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Contoso.Expenses.Web.Pages.Expenses.IndexModel 3 | 4 | @{ 5 | ViewData["Title"] = "Index"; 6 | } 7 | 8 |

Index

9 | 10 |

11 | Create New 12 |

13 | 14 | 15 | 16 | 19 | 22 | 25 | 28 | 31 | 34 | 37 | 38 | 39 | 40 | 41 | @foreach (var item in Model.Expense) { 42 | 43 | 46 | 49 | 52 | 55 | 58 | 61 | 64 | 69 | 70 | } 71 | 72 |
17 | @Html.DisplayNameFor(model => model.Expense[0].Purpose) 18 | 20 | @Html.DisplayNameFor(model => model.Expense[0].Date) 21 | 23 | @Html.DisplayNameFor(model => model.Expense[0].CostCenter) 24 | 26 | @Html.DisplayNameFor(model => model.Expense[0].Amount) 27 | 29 | @Html.DisplayNameFor(model => model.Expense[0].ApproverEmail) 30 | 32 | @Html.DisplayNameFor(model => model.Expense[0].SubmitterEmail) 33 | 35 | @Html.DisplayNameFor(model => model.Expense[0].Receipt) 36 |
44 | @Html.DisplayFor(modelItem => item.Purpose) 45 | 47 | @Html.DisplayFor(modelItem => item.Date) 48 | 50 | @Html.DisplayFor(modelItem => item.CostCenter) 51 | 53 | @Html.DisplayFor(modelItem => item.Amount) 54 | 56 | @Html.DisplayFor(modelItem => item.ApproverEmail) 57 | 59 | @Html.DisplayFor(modelItem => item.SubmitterEmail) 60 | 62 | @Html.DisplayFor(modelItem => item.Receipt) 63 | 65 | Edit | 66 | Details | 67 | Delete 68 |
73 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Expenses/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | using Microsoft.EntityFrameworkCore; 8 | using Contoso.Expenses.Common.Models; 9 | 10 | namespace Contoso.Expenses.Web.Pages.Expenses 11 | { 12 | public class IndexModel : PageModel 13 | { 14 | private readonly ContosoExpensesWebContext _context; 15 | 16 | public IndexModel(ContosoExpensesWebContext context) 17 | { 18 | _context = context; 19 | } 20 | 21 | public IList Expense { get;set; } 22 | 23 | public async Task OnGetAsync() 24 | { 25 | Expense = await _context.Expense.ToListAsync(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Contoso.Expenses.Web.Pages.Expenses.IndexModel 3 | 4 | @{ 5 | ViewData["Title"] = "Index"; 6 | } 7 | 8 |

Index

9 | 10 |

11 | Create New 12 |

13 | 14 | 15 | 16 | 19 | 22 | 25 | 28 | 31 | 34 | 37 | 38 | 39 | 40 | 41 | @foreach (var item in Model.Expense) { 42 | 43 | 46 | 49 | 52 | 55 | 58 | 61 | 64 | 69 | 70 | } 71 | 72 |
17 | @Html.DisplayNameFor(model => model.Expense[0].Purpose) 18 | 20 | @Html.DisplayNameFor(model => model.Expense[0].Date) 21 | 23 | @Html.DisplayNameFor(model => model.Expense[0].CostCenter) 24 | 26 | @Html.DisplayNameFor(model => model.Expense[0].Amount) 27 | 29 | @Html.DisplayNameFor(model => model.Expense[0].ApproverEmail) 30 | 32 | @Html.DisplayNameFor(model => model.Expense[0].SubmitterEmail) 33 | 35 | @Html.DisplayNameFor(model => model.Expense[0].Receipt) 36 |
44 | @Html.DisplayFor(modelItem => item.Purpose) 45 | 47 | @Html.DisplayFor(modelItem => item.Date) 48 | 50 | @Html.DisplayFor(modelItem => item.CostCenter) 51 | 53 | @Html.DisplayFor(modelItem => item.Amount) 54 | 56 | @Html.DisplayFor(modelItem => item.ApproverEmail) 57 | 59 | @Html.DisplayFor(modelItem => item.SubmitterEmail) 60 | 62 | @Html.DisplayFor(modelItem => item.Receipt) 63 | 65 | Edit | 66 | Details | 67 | Delete 68 |
73 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace Contoso.Expenses.Web.Pages 9 | { 10 | public class IndexModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model PrivacyModel 3 | @{ 4 | ViewData["Title"] = "Privacy Policy"; 5 | } 6 |

@ViewData["Title"]

7 | 8 |

Use this page to detail your site's privacy policy.

-------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Privacy.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace Contoso.Expenses.Web.Pages 9 | { 10 | public class PrivacyModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Shared/_CookieConsentPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Http.Features 2 | 3 | @{ 4 | var consentFeature = Context.Features.Get(); 5 | var showBanner = !consentFeature?.CanTrack ?? false; 6 | var cookieString = consentFeature?.CreateConsentCookie(); 7 | } 8 | 9 | @if (showBanner) 10 | { 11 | 33 | 41 | } -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - Contoso Expenses 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 41 | 42 | 43 | 44 |
45 | @RenderBody() 46 |
47 |
48 |

© @DateTime.Now.Year - Contoso Expenses-V3

49 |
50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 70 | 71 | 72 | 73 | @RenderSection("Scripts", required: false) 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Contoso.Expenses.Web 2 | @namespace Contoso.Expenses.Web.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Program.cs: -------------------------------------------------------------------------------- 1 | using App.Metrics.AspNetCore; 2 | using App.Metrics.Formatters.Prometheus; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | namespace Contoso.Expenses.Web 7 | { 8 | public class Program 9 | { 10 | public static void Main(string[] args) 11 | { 12 | //CreateWebHostBuilder(args).Build().Run(); 13 | CreateHostBuilder(args).Build().Run(); 14 | } 15 | 16 | //public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 17 | // WebHost.CreateDefaultBuilder(args) 18 | // .UseStartup(); 19 | 20 | public static IHostBuilder CreateHostBuilder(string[] args) => 21 | Host.CreateDefaultBuilder(args) 22 | .UseMetricsWebTracking() 23 | .UseMetrics(options => 24 | { 25 | options.EndpointOptions = endpointsOptions => 26 | { 27 | endpointsOptions.MetricsTextEndpointOutputFormatter = new MetricsPrometheusTextOutputFormatter(); 28 | endpointsOptions.MetricsEndpointOutputFormatter = new MetricsPrometheusProtobufOutputFormatter(); 29 | endpointsOptions.EnvironmentInfoEndpointEnabled = false; 30 | }; 31 | }) 32 | .ConfigureWebHostDefaults(webBuilder => 33 | { 34 | webBuilder.UseStartup(); 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:59124", 7 | "sslPort": 44335 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Contoso.Expenses.Web": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 25 | }, 26 | "Azure Dev Spaces": { 27 | "commandName": "AzureDevSpaces", 28 | "launchBrowser": true, 29 | "resourceGroup": "ft-k8sexam-rg", 30 | "aksName": "fezkubetrnclustermsdn", 31 | "subscriptionId": "7febed4d-a1d9-4386-8390-cd2977d4f9e2" 32 | }, 33 | "Docker": { 34 | "commandName": "Docker", 35 | "launchBrowser": true, 36 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 37 | "publishAllPorts": true, 38 | "useSSL": true 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Startup.cs: -------------------------------------------------------------------------------- 1 | using Contoso.Expenses.Common.Models; 2 | using Contoso.Expenses.Web.Models; 3 | using Contoso.Expenses.Web.Tracing; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.AspNetCore.Server.Kestrel.Core; 9 | using Microsoft.EntityFrameworkCore; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Microsoft.Extensions.Hosting; 13 | using Microsoft.Extensions.Logging; 14 | 15 | namespace Contoso.Expenses.Web 16 | { 17 | public class Startup 18 | { 19 | private readonly IWebHostEnvironment _env; 20 | public IConfiguration Configuration { get; } 21 | 22 | public Startup(IWebHostEnvironment env) 23 | { 24 | var builder = new ConfigurationBuilder() 25 | .SetBasePath(env.ContentRootPath) 26 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 27 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) 28 | .AddEnvironmentVariables(); 29 | 30 | if (env.IsEnvironment("Development")) 31 | { 32 | builder.AddUserSecrets(); 33 | } 34 | 35 | _env = env; 36 | 37 | Configuration = builder.Build(); 38 | //Configuration = configuration; 39 | } 40 | 41 | // This method gets called by the runtime. Use this method to add services to the container. 42 | public void ConfigureServices(IServiceCollection services) 43 | { 44 | services.Configure(options => { options.AllowSynchronousIO = true; }); 45 | services.AddMetrics(); 46 | 47 | services.AddJaegerTracing(options => { 48 | //options.JaegerAgentHost = Configuration["JAEGER_AGENT_HOST"]; 49 | //options.JaegerAgentHost = Configuration["JAEGER_AGENT_HOST"]; 50 | options.JaegerAgentHost = "jaeger-agent.tracing"; 51 | options.ServiceName = "conexp"; 52 | options.LoggerFactory = (ILoggerFactory)new LoggerFactory(); 53 | }); 54 | 55 | services.Configure(options => 56 | { 57 | // This lambda determines whether user consent for non-essential cookies is needed for a given request. 58 | options.CheckConsentNeeded = context => true; 59 | options.MinimumSameSitePolicy = SameSiteMode.None; 60 | }); 61 | 62 | services.AddMvc(); 63 | 64 | // see https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-2.2 65 | string connectionString = Configuration["ConnectionStrings:DBConnectionString"]; 66 | ServerVersion version = ServerVersion.AutoDetect(connectionString); 67 | services.AddDbContext(options => 68 | options.UseMySql(connectionString, version)); 69 | 70 | services.Configure(Configuration.GetSection("ConfigValues")); 71 | 72 | services.AddSingleton(queueInfo => 73 | { 74 | return new QueueInfo() 75 | { 76 | ConnectionString = Configuration["ConnectionStrings:BrokerConnectionString"] 77 | }; 78 | }); 79 | 80 | services.AddSingleton(_env); 81 | } 82 | 83 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 84 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ContosoExpensesWebContext context, ILoggerFactory loggerFactory) 85 | { 86 | if (env.IsEnvironment("Development")) 87 | { 88 | app.UseDeveloperExceptionPage(); 89 | } 90 | else 91 | { 92 | app.UseExceptionHandler("/Error"); 93 | } 94 | 95 | context.Database.EnsureCreated(); 96 | 97 | //app.UseDefaultFiles(); 98 | //app.UseStaticFiles(); 99 | //app.UseCookiePolicy(); 100 | //app.UseMvc(); 101 | 102 | app.UseHttpsRedirection(); 103 | app.UseStaticFiles(); 104 | 105 | app.UseRouting(); 106 | 107 | app.UseAuthorization(); 108 | 109 | app.UseEndpoints(endpoints => 110 | { 111 | endpoints.MapRazorPages(); 112 | }); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Tracing/JaegerTracingOptions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Contoso.Expenses.Web.Tracing 8 | { 9 | public class JaegerTracingOptions 10 | { 11 | public double SamplingRate { get; set; } 12 | public double LowerBound { get; set; } 13 | public ILoggerFactory LoggerFactory { get; set; } 14 | public string JaegerAgentHost { get; set; } 15 | public int JaegerAgentPort { get; set; } 16 | public string ServiceName { get; set; } 17 | 18 | public JaegerTracingOptions() 19 | { 20 | SamplingRate = 0.1d; 21 | LowerBound = 1d; 22 | LoggerFactory = new LoggerFactory(); 23 | JaegerAgentHost = "localhost"; 24 | JaegerAgentPort = 6831; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/Tracing/JaegerTracingServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Jaeger; 2 | using Jaeger.Reporters; 3 | using Jaeger.Samplers; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Options; 6 | using OpenTracing; 7 | using OpenTracing.Util; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Threading.Tasks; 12 | 13 | namespace Contoso.Expenses.Web.Tracing 14 | { 15 | public static class JaegerTracingServiceCollectionExtensions 16 | { 17 | public static IServiceCollection AddJaegerTracing( 18 | this IServiceCollection services, 19 | Action setupAction = null) 20 | { 21 | if (setupAction != null) services.ConfigureJaegerTracing(setupAction); 22 | 23 | services.AddSingleton(cli => 24 | { 25 | var options = cli.GetService>().Value; 26 | 27 | var senderConfig = new Jaeger.Configuration.SenderConfiguration(options.LoggerFactory) 28 | .WithAgentHost(options.JaegerAgentHost) 29 | .WithAgentPort(options.JaegerAgentPort); 30 | 31 | var reporter = new RemoteReporter.Builder() 32 | .WithLoggerFactory(options.LoggerFactory) 33 | .WithSender(senderConfig.GetSender()) 34 | .Build(); 35 | 36 | var sampler = new GuaranteedThroughputSampler(options.SamplingRate, options.LowerBound); 37 | 38 | var tracer = new Tracer.Builder(options.ServiceName) 39 | .WithLoggerFactory(options.LoggerFactory) 40 | .WithReporter(reporter) 41 | .WithSampler(sampler) 42 | .Build(); 43 | 44 | // Allows code that can't use dependency injection to have access to the tracer. 45 | if (!GlobalTracer.IsRegistered()) 46 | GlobalTracer.Register(tracer); 47 | 48 | return tracer; 49 | }); 50 | 51 | services.AddOpenTracing(builder => { 52 | builder.ConfigureAspNetCore(options => { 53 | options.Hosting.IgnorePatterns.Add(x => { 54 | return x.Request.Path == "/health"; 55 | }); 56 | options.Hosting.IgnorePatterns.Add(x => { 57 | return x.Request.Path == "/metrics"; 58 | }); 59 | }); 60 | }); 61 | 62 | return services; 63 | } 64 | 65 | public static void ConfigureJaegerTracing( 66 | this IServiceCollection services, 67 | Action setupAction) 68 | { 69 | services.Configure(setupAction); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "KeyVaultName": "contosoexpvault", 8 | "ConfigValues": { 9 | "CostCenterAPIUrl": "" 10 | }, 11 | "AllowedHosts": "*", 12 | "ConnectionStrings": { 13 | "DBConnectionString": "", 14 | "BrokerConnectionString": "http://broker-ingress.knative-eventing.svc.cluster.local/conexp-mvp-fn/default" 15 | } 16 | } -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | body { 4 | padding-top: 50px; 5 | padding-bottom: 20px; 6 | } 7 | 8 | /* Wrapping element */ 9 | /* Set some basic padding to keep content from hitting the edges */ 10 | .body-content { 11 | padding-left: 15px; 12 | padding-right: 15px; 13 | } 14 | 15 | /* Carousel */ 16 | .carousel-caption p { 17 | font-size: 20px; 18 | line-height: 1.4; 19 | } 20 | 21 | /* Make .svg files in the carousel display properly in older browsers */ 22 | .carousel-inner .item img[src$=".svg"] { 23 | width: 100%; 24 | } 25 | 26 | /* QR code generator */ 27 | #qrCode { 28 | margin: 15px; 29 | } 30 | 31 | /* Hide/rearrange for smaller screens */ 32 | @media screen and (max-width: 767px) { 33 | /* Hide captions */ 34 | .carousel-caption { 35 | display: none; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/cloud-native-app/d2f68dec4290621e1f526153cb8eb0fbc34fd71c/src/Contoso.Expenses.Web/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/images/Expenses.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/cloud-native-app/d2f68dec4290621e1f526153cb8eb0fbc34fd71c/src/Contoso.Expenses.Web/wwwroot/images/Expenses.jpg -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your Javascript code. 5 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/cloud-native-app/d2f68dec4290621e1f526153cb8eb0fbc34fd71c/src/Contoso.Expenses.Web/wwwroot/js/site.min.js -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap", 3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", 4 | "keywords": [ 5 | "css", 6 | "js", 7 | "less", 8 | "mobile-first", 9 | "responsive", 10 | "front-end", 11 | "framework", 12 | "web" 13 | ], 14 | "homepage": "http://getbootstrap.com", 15 | "license": "MIT", 16 | "moduleType": "globals", 17 | "main": [ 18 | "less/bootstrap.less", 19 | "dist/js/bootstrap.js" 20 | ], 21 | "ignore": [ 22 | "/.*", 23 | "_config.yml", 24 | "CNAME", 25 | "composer.json", 26 | "CONTRIBUTING.md", 27 | "docs", 28 | "js/tests", 29 | "test-infra" 30 | ], 31 | "dependencies": { 32 | "jquery": "1.9.1 - 3" 33 | }, 34 | "version": "3.3.7", 35 | "_release": "3.3.7", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.3.7", 39 | "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" 40 | }, 41 | "_source": "https://github.com/twbs/bootstrap.git", 42 | "_target": "v3.3.7", 43 | "_originalSource": "bootstrap", 44 | "_direct": true 45 | } -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/cloud-native-app/d2f68dec4290621e1f526153cb8eb0fbc34fd71c/src/Contoso.Expenses.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/cloud-native-app/d2f68dec4290621e1f526153cb8eb0fbc34fd71c/src/Contoso.Expenses.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/cloud-native-app/d2f68dec4290621e1f526153cb8eb0fbc34fd71c/src/Contoso.Expenses.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/cloud-native-app/d2f68dec4290621e1f526153cb8eb0fbc34fd71c/src/Contoso.Expenses.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/jquery-validation-unobtrusive/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation-unobtrusive", 3 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", 4 | "version": "3.2.9", 5 | "_release": "3.2.9", 6 | "_resolution": { 7 | "type": "version", 8 | "tag": "v3.2.9", 9 | "commit": "a91f5401898e125f10771c5f5f0909d8c4c82396" 10 | }, 11 | "_source": "https://github.com/aspnet/jquery-validation-unobtrusive.git", 12 | "_target": "^3.2.9", 13 | "_originalSource": "jquery-validation-unobtrusive", 14 | "_direct": true 15 | } -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | // Unobtrusive validation support library for jQuery and jQuery Validate 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // @version v3.2.9 4 | !function(a){"function"==typeof define&&define.amd?define("jquery.validate.unobtrusive",["jquery.validation"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery-validation")):jQuery.validator.unobtrusive=a(jQuery)}(function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function u(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=f.unobtrusive.options||{},u=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),u("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),u("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),u("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var m,f=a.validator,v="unobtrusiveValidation";return f.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=u(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){f.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=u(this);a&&a.attachValidation()})}},m=f.unobtrusive.adapters,m.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},m.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},m.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},m.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},f.addMethod("__dummy__",function(a,e,n){return!0}),f.addMethod("regex",function(a,e,n){var t;return!!this.optional(e)||(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),f.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),f.methods.extension?(m.addSingleVal("accept","mimtype"),m.addSingleVal("extension","extension")):m.addSingleVal("extension","extension","accept"),m.addSingleVal("regex","pattern"),m.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),m.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),m.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),m.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),m.add("required",function(a){"INPUT"===a.element.tagName.toUpperCase()&&"CHECKBOX"===a.element.type.toUpperCase()||e(a,"required",!0)}),m.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),m.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),m.add("fileextensions",["extensions"],function(a){e(a,"extension",a.params.extensions)}),a(function(){f.unobtrusive.parse(document)}),f.unobtrusive}); -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/jquery-validation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation", 3 | "homepage": "https://jqueryvalidation.org/", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/jquery-validation/jquery-validation.git" 7 | }, 8 | "authors": [ 9 | "Jörn Zaefferer " 10 | ], 11 | "description": "Form validation made easy", 12 | "main": "dist/jquery.validate.js", 13 | "keywords": [ 14 | "forms", 15 | "validation", 16 | "validate" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "demo", 25 | "lib" 26 | ], 27 | "dependencies": { 28 | "jquery": ">= 1.7.2" 29 | }, 30 | "version": "1.17.0", 31 | "_release": "1.17.0", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "1.17.0", 35 | "commit": "fc9b12d3bfaa2d0c04605855b896edb2934c0772" 36 | }, 37 | "_source": "https://github.com/jzaefferer/jquery-validation.git", 38 | "_target": "^1.17.0", 39 | "_originalSource": "jquery-validation", 40 | "_direct": true 41 | } -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "3.3.1", 16 | "_release": "3.3.1", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "3.3.1", 20 | "commit": "9e8ec3d10fad04748176144f108d7355662ae75e" 21 | }, 22 | "_source": "https://github.com/jquery/jquery-dist.git", 23 | "_target": "^3.3.1", 24 | "_originalSource": "jquery", 25 | "_direct": true 26 | } -------------------------------------------------------------------------------- /src/Contoso.Expenses.Web/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /src/ContosoCore.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.6.33801.468 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Contoso.Expenses.API", "Contoso.Expenses.API\Contoso.Expenses.API.csproj", "{80727FE6-EBF0-4825-9DAA-6C2449AD4732}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Contoso.Expenses.Common", "Contoso.Common\Contoso.Expenses.Common.csproj", "{C1292895-9F2F-4D64-81A6-C5D2DA09E027}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1CF87116-5DEE-4C00-8687-E4813F8E4668}" 11 | ProjectSection(SolutionItems) = preProject 12 | backend.yaml = backend.yaml 13 | frontend.yaml = frontend.yaml 14 | README.md = README.md 15 | function.yaml = function.yaml 16 | EndProjectSection 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Contoso.Expenses.Function", "Contoso.Expenses.Function\Contoso.Expenses.Function.csproj", "{D970D874-3CC3-4FF5-9279-AD8A96A97D64}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Contoso.Expenses.Web", "Contoso.Expenses.Web\Contoso.Expenses.Web.csproj", "{2376D120-36B6-4CC7-8D27-9F10057D4B32}" 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Release|Any CPU = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {80727FE6-EBF0-4825-9DAA-6C2449AD4732}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {80727FE6-EBF0-4825-9DAA-6C2449AD4732}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {80727FE6-EBF0-4825-9DAA-6C2449AD4732}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {80727FE6-EBF0-4825-9DAA-6C2449AD4732}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {C1292895-9F2F-4D64-81A6-C5D2DA09E027}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {C1292895-9F2F-4D64-81A6-C5D2DA09E027}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {C1292895-9F2F-4D64-81A6-C5D2DA09E027}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {C1292895-9F2F-4D64-81A6-C5D2DA09E027}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {D970D874-3CC3-4FF5-9279-AD8A96A97D64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {D970D874-3CC3-4FF5-9279-AD8A96A97D64}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {D970D874-3CC3-4FF5-9279-AD8A96A97D64}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {D970D874-3CC3-4FF5-9279-AD8A96A97D64}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {2376D120-36B6-4CC7-8D27-9F10057D4B32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {2376D120-36B6-4CC7-8D27-9F10057D4B32}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {2376D120-36B6-4CC7-8D27-9F10057D4B32}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {2376D120-36B6-4CC7-8D27-9F10057D4B32}.Release|Any CPU.Build.0 = Release|Any CPU 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {C3333FA8-27B3-469C-B633-14D31980AA92} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project. 3 | 4 | # Getting Started 5 | TODO: Guide users through getting your code up and running on their own system. In this section you can talk about: 6 | 1. Installation process 7 | 2. Software dependencies 8 | 3. Latest releases 9 | 4. API references 10 | 11 | # Build and Test 12 | TODO: Describe and show how to build your code and run the tests. 13 | 14 | # Contribute 15 | TODO: Explain how other users and developers can contribute to make your code better. 16 | 17 | If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files: 18 | - [ASP.NET Core](https://github.com/aspnet/Home) 19 | - [Visual Studio Code](https://github.com/Microsoft/vscode) 20 | - [Chakra Core](https://github.com/Microsoft/ChakraCore) 21 | 22 | # Testing Update 1 23 | -------------------------------------------------------------------------------- /src/backend.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: backend-deployment 5 | labels: 6 | app: backend 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: backend 12 | template: 13 | metadata: 14 | labels: 15 | app: backend 16 | spec: 17 | containers: 18 | - name: backend 19 | image: __IMAGE__ 20 | ports: 21 | - containerPort: 80 22 | imagePullPolicy: Always 23 | env: 24 | - name: ConnectionStrings__DBConnectionString 25 | value: "__APIDBCONSTR__" 26 | imagePullSecrets: 27 | - name: regcred 28 | nodeSelector: 29 | "kubernetes.io/os": linux 30 | --- 31 | apiVersion: v1 32 | kind: Service 33 | metadata: 34 | name: backend-svc 35 | spec: 36 | selector: 37 | app: backend 38 | ports: 39 | - name: http 40 | protocol: TCP 41 | port: 80 42 | targetPort: 80 43 | -------------------------------------------------------------------------------- /src/frontend.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: frontend-deployment 5 | labels: 6 | app: conexp-frontend 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: conexp-frontend 12 | template: 13 | metadata: 14 | annotations: 15 | prometheus.io/scrape: "true" 16 | prometheus.io/path: "/metrics-text" 17 | prometheus.io/port: "80" 18 | prometheus.io/scheme: "http" 19 | labels: 20 | app: conexp-frontend 21 | spec: 22 | containers: 23 | - name: frontend 24 | image: __IMAGE__ 25 | ports: 26 | - containerPort: 80 27 | imagePullPolicy: Always 28 | env: 29 | - name: ConnectionStrings__DBConnectionString 30 | value: "__WEBDBCONSTR__" 31 | - name: ConfigValues__CostCenterAPIUrl 32 | value: "__APIURL__" 33 | imagePullSecrets: 34 | - name: regcred 35 | nodeSelector: 36 | "kubernetes.io/os": linux 37 | --- 38 | apiVersion: v1 39 | kind: Service 40 | metadata: 41 | name: conexp-frontend-svc 42 | labels: 43 | app: conexp-frontend 44 | spec: 45 | selector: 46 | app: conexp-frontend 47 | ports: 48 | - name: http 49 | protocol: TCP 50 | port: 80 51 | targetPort: 80 52 | --- 53 | apiVersion: networking.k8s.io/v1 54 | kind: Ingress 55 | metadata: 56 | name: frontend-ingress-rules 57 | annotations: 58 | nginx.ingress.kubernetes.io/rewrite-target: /$1 59 | nginx.ingress.kubernetes.io/configuration-snippet: | 60 | proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local; 61 | proxy_hide_header l5d-remote-ip; 62 | proxy_hide_header l5d-server-id; 63 | spec: 64 | ingressClassName: nginx 65 | rules: 66 | - host: __APPHOSTNAME__ 67 | http: 68 | paths: 69 | - backend: 70 | service: 71 | name: conexp-frontend-svc 72 | port: 73 | number: 80 74 | path: /(.*) 75 | pathType: Prefix 76 | -------------------------------------------------------------------------------- /src/function.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: serving.knative.dev/v1 2 | kind: Service 3 | metadata: 4 | name: expenseemailsender 5 | namespace: conexp-mvp-fn 6 | labels: 7 | networking.knative.dev/visibility: cluster-local 8 | spec: 9 | template: 10 | spec: 11 | imagePullSecrets: 12 | - name: regcred 13 | containers: 14 | - image: __IMAGE__ 15 | ports: 16 | - containerPort: 80 17 | env: 18 | - name: SENDGRID_API_KEY 19 | value: "__SENDGRIDAPIKEY__" 20 | --- 21 | apiVersion: eventing.knative.dev/v1 22 | kind: Trigger 23 | metadata: 24 | name: expenseemailsender 25 | namespace: conexp-mvp-fn 26 | spec: 27 | broker: default 28 | filter: 29 | attributes: 30 | type: contoso.web.dispatchemail 31 | subscriber: 32 | ref: 33 | apiVersion: serving.knative.dev/v1 34 | kind: Service 35 | name: expenseemailsender -------------------------------------------------------------------------------- /yml/README.md: -------------------------------------------------------------------------------- 1 | ## All the required yaml files referenced by the steps 2 | -------------------------------------------------------------------------------- /yml/app-admin-role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Role 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | metadata: 5 | name: tekton-triggers-admin 6 | rules: 7 | # EventListeners need to be able to fetch all namespaced resources 8 | - apiGroups: ["triggers.tekton.dev"] 9 | resources: ["eventlisteners", "triggerbindings", "triggertemplates", "triggers", "interceptors"] 10 | verbs: ["get", "list", "watch"] 11 | - apiGroups: [""] 12 | # configmaps is needed for updating logging config 13 | resources: ["configmaps"] 14 | verbs: ["get", "list", "watch"] 15 | # Permissions to create resources in associated TriggerTemplates 16 | - apiGroups: ["tekton.dev"] 17 | resources: ["pipelineruns", "pipelineresources", "taskruns"] 18 | verbs: ["create"] 19 | - apiGroups: [""] 20 | resources: ["serviceaccounts"] 21 | verbs: ["impersonate"] 22 | - apiGroups: ["policy"] 23 | resources: ["podsecuritypolicies"] 24 | resourceNames: ["tekton-triggers"] 25 | verbs: ["use"] 26 | --- 27 | apiVersion: v1 28 | kind: ServiceAccount 29 | metadata: 30 | name: tekton-triggers-admin 31 | --- 32 | apiVersion: rbac.authorization.k8s.io/v1 33 | kind: RoleBinding 34 | metadata: 35 | name: tekton-triggers-admin-binding 36 | subjects: 37 | - kind: ServiceAccount 38 | name: tekton-triggers-admin 39 | roleRef: 40 | apiGroup: rbac.authorization.k8s.io 41 | kind: Role 42 | name: tekton-triggers-admin 43 | --- 44 | kind: ClusterRole 45 | apiVersion: rbac.authorization.k8s.io/v1 46 | metadata: 47 | name: tekton-triggers-admin-clusterrole 48 | rules: 49 | # EventListeners need to be able to fetch any clustertriggerbindings 50 | - apiGroups: ["triggers.tekton.dev"] 51 | resources: ["clustertriggerbindings", "clusterinterceptors"] 52 | verbs: ["get", "list", "watch"] 53 | --- 54 | apiVersion: rbac.authorization.k8s.io/v1 55 | kind: ClusterRoleBinding 56 | metadata: 57 | name: tekton-triggers-admin-clusterbinding 58 | subjects: 59 | - kind: ServiceAccount 60 | name: tekton-triggers-admin 61 | namespace: conexp-mvp-devops 62 | roleRef: 63 | apiGroup: rbac.authorization.k8s.io 64 | kind: ClusterRole 65 | name: tekton-triggers-admin-clusterrole 66 | -------------------------------------------------------------------------------- /yml/app-deploy-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Role 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | metadata: 5 | name: pipeline-role 6 | rules: 7 | - apiGroups: ["", "extensions", "apps", "networking.k8s.io", "serving.knative.dev", "eventing.knative.dev"] 8 | resources: ["*"] 9 | verbs: ["*"] 10 | --- 11 | apiVersion: rbac.authorization.k8s.io/v1 12 | kind: RoleBinding 13 | metadata: 14 | name: pipeline-role-binding 15 | roleRef: 16 | apiGroup: rbac.authorization.k8s.io 17 | kind: Role 18 | name: pipeline-role 19 | subjects: 20 | - kind: ServiceAccount 21 | name: deployment-user 22 | namespace: conexp-mvp-devops 23 | --- -------------------------------------------------------------------------------- /yml/app-pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Pipeline 3 | metadata: 4 | name: conexp-pipeline 5 | spec: 6 | params: 7 | - name: deployment-namespace 8 | type: string 9 | description: namespace to deploy the yaml 10 | default: conexp-mvp 11 | - name: fn-deployment-namespace 12 | type: string 13 | description: namespace to deploy the function yaml 14 | default: conexp-mvp-fn 15 | - name: git-revision 16 | type: string 17 | - name: git-url 18 | type: string 19 | - name: web-image-source 20 | type: string 21 | - name: api-image-source 22 | type: string 23 | - name: fn-image-source 24 | type: string 25 | workspaces: 26 | - name: shared-data 27 | - name: docker-credentials 28 | tasks: 29 | - name: fetch-source 30 | taskRef: 31 | name: git-clone 32 | workspaces: 33 | - name: output 34 | workspace: shared-data 35 | params: 36 | - name: url 37 | value: $(params.git-url) 38 | - name: revision 39 | value: $(params.git-revision) 40 | - name: build-docker-image-web 41 | runAfter: ["fetch-source", "build-docker-image-fn"] 42 | taskRef: 43 | name: kaniko 44 | workspaces: 45 | - name: source 46 | workspace: shared-data 47 | - name: dockerconfig 48 | workspace: docker-credentials 49 | params: 50 | - name: DOCKERFILE 51 | value: Contoso.Expenses.Web/Dockerfile 52 | - name: CONTEXT 53 | value: /src 54 | - name: IMAGE 55 | value: $(params.web-image-source) 56 | - name: build-docker-image-api 57 | runAfter: ["fetch-source", "build-docker-image-web"] 58 | taskRef: 59 | name: kaniko 60 | workspaces: 61 | - name: source 62 | workspace: shared-data 63 | - name: dockerconfig 64 | workspace: docker-credentials 65 | params: 66 | - name: DOCKERFILE 67 | value: Dockerfile 68 | - name: CONTEXT 69 | value: /src/Contoso.Expenses.API 70 | - name: IMAGE 71 | value: $(params.api-image-source) 72 | - name: build-docker-image-fn 73 | runAfter: ["fetch-source"] 74 | taskRef: 75 | name: kaniko 76 | workspaces: 77 | - name: source 78 | workspace: shared-data 79 | - name: dockerconfig 80 | workspace: docker-credentials 81 | params: 82 | - name: DOCKERFILE 83 | value: Contoso.Expenses.Function/Dockerfile 84 | - name: CONTEXT 85 | value: /src 86 | - name: IMAGE 87 | value: $(params.fn-image-source) 88 | - name: deploy-using-kubectl-web 89 | runAfter: ["build-docker-image-web"] 90 | taskRef: 91 | name: deploy-using-kubectl 92 | workspaces: 93 | - name: source 94 | workspace: shared-data 95 | params: 96 | - name: pathToYamlFile 97 | value: frontend.yaml 98 | - name: deployment-namespace 99 | value: $(params.deployment-namespace) 100 | - name: image-source 101 | value: $(params.web-image-source) 102 | - name: deploy-using-kubectl-api 103 | runAfter: ["build-docker-image-api"] 104 | taskRef: 105 | name: deploy-using-kubectl 106 | workspaces: 107 | - name: source 108 | workspace: shared-data 109 | params: 110 | - name: pathToYamlFile 111 | value: backend.yaml 112 | - name: deployment-namespace 113 | value: $(params.deployment-namespace) 114 | - name: image-source 115 | value: $(params.api-image-source) 116 | - name: deploy-using-kubectl-fn 117 | runAfter: ["build-docker-image-fn"] 118 | taskRef: 119 | name: deploy-using-kubectl 120 | workspaces: 121 | - name: source 122 | workspace: shared-data 123 | params: 124 | - name: pathToYamlFile 125 | value: function.yaml 126 | - name: deployment-namespace 127 | value: $(params.fn-deployment-namespace) 128 | - name: image-source 129 | value: $(params.fn-image-source) 130 | --- 131 | apiVersion: tekton.dev/v1beta1 132 | kind: Task 133 | metadata: 134 | name: deploy-using-kubectl 135 | spec: 136 | params: 137 | - name: pathToYamlFile 138 | description: The path to the yaml file to deploy within the git source 139 | - name: deployment-namespace 140 | description: The namespace to deploy to 141 | - name: image-source 142 | description: The path of the image 143 | workspaces: 144 | - name: source 145 | steps: 146 | - name: update-yaml 147 | image: alpine 148 | command: ["sed"] 149 | args: 150 | - "-i" 151 | - "-e" 152 | - "s|__IMAGE__|$(params.image-source)|g;s|__SENDGRIDAPIKEY__|{SENDGRIDAPIKEYRELACE}|g;s|__APPHOSTNAME__|{APPHOSTNAMEREPLACE}|g;s|__APIDBCONSTR__|server=mysql.mysql.svc.cluster.local;Port=3306;database=conexpapi;user=ftacncf;password=FTA@CNCF0n@zure3;|g;s|__WEBDBCONSTR__|server=mysql.mysql.svc.cluster.local;Port=3306;database=conexpweb;user=ftacncf;password=FTA@CNCF0n@zure3;|g;s|__APIURL__|http://backend-svc.conexp-mvp.svc.cluster.local:80|g;" 153 | - "$(workspaces.source.path)/src/$(params.pathToYamlFile)" 154 | - name: print-yaml 155 | image: alpine 156 | command: ["cat"] 157 | args: 158 | - "$(workspaces.source.path)/src/$(params.pathToYamlFile)" 159 | - name: run-kubectl 160 | image: lachlanevenson/k8s-kubectl 161 | command: ["kubectl"] 162 | args: 163 | - "apply" 164 | - "-f" 165 | - "$(workspaces.source.path)/src/$(params.pathToYamlFile)" 166 | - "-n" 167 | - "$(params.deployment-namespace)" 168 | -------------------------------------------------------------------------------- /yml/app-triggers.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: triggers.tekton.dev/v1alpha1 2 | kind: TriggerTemplate 3 | metadata: 4 | name: conexp-triggertemplate 5 | spec: 6 | params: 7 | - name: gitrevision 8 | description: The git revision 9 | default: master 10 | - name: gitrepositoryurl 11 | description: The git repository url 12 | - name: web-imageUrl 13 | description: Url of image repository 14 | - name: api-imageUrl 15 | description: Url of image repository 16 | - name: fn-imageUrl 17 | description: Url of image repository 18 | - name: namespace 19 | description: The namespace to create the resources 20 | - name: deployment-namespace 21 | description: The namespace to deploy the yaml 22 | - name: fn-deployment-namespace 23 | description: The namespace to deploy the yaml 24 | resourcetemplates: 25 | - apiVersion: tekton.dev/v1beta1 26 | kind: PipelineRun 27 | metadata: 28 | name: conexp-pipeline-run-$(uid) 29 | namespace: $(tt.params.namespace) 30 | spec: 31 | serviceAccountName: deployment-user 32 | pipelineRef: 33 | name: conexp-pipeline 34 | podTemplate: 35 | nodeSelector: 36 | kubernetes.io/os: linux 37 | securityContext: 38 | fsGroup: 65532 39 | params: 40 | - name: deployment-namespace 41 | value: $(tt.params.deployment-namespace) 42 | - name: fn-deployment-namespace 43 | value: $(tt.params.fn-deployment-namespace) 44 | - name: git-revision 45 | value: $(tt.params.gitrevision) 46 | - name: git-url 47 | value: $(tt.params.gitrepositoryurl) 48 | - name: web-image-source 49 | value: $(tt.params.web-imageUrl) 50 | - name: api-image-source 51 | value: $(tt.params.api-imageUrl) 52 | - name: fn-image-source 53 | value: $(tt.params.fn-imageUrl) 54 | workspaces: 55 | - name: shared-data 56 | volumeClaimTemplate: 57 | spec: 58 | storageClassName: rook-ceph-block 59 | accessModes: 60 | - ReadWriteOnce 61 | resources: 62 | requests: 63 | storage: 1Gi 64 | - name: docker-credentials 65 | secret: 66 | secretName: regcred 67 | --- 68 | apiVersion: triggers.tekton.dev/v1alpha1 69 | kind: TriggerBinding 70 | metadata: 71 | name: conexp-pipelinebinding 72 | spec: 73 | params: 74 | - name: gitrevision 75 | value: $(body.head_commit.id) 76 | - name: namespace 77 | value: conexp-mvp-devops 78 | - name: deployment-namespace 79 | value: conexp-mvp 80 | - name: fn-deployment-namespace 81 | value: conexp-mvp-fn 82 | - name: gitrepositoryurl 83 | value: "https://github.com/$(body.repository.full_name)" 84 | - name: web-imageUrl 85 | value: "{registryHost}/conexp/web:$(body.head_commit.id)" 86 | - name: api-imageUrl 87 | value: "{registryHost}/conexp/api:$(body.head_commit.id)" 88 | - name: fn-imageUrl 89 | value: "{registryHost}/conexp/emaildispatcher:$(body.head_commit.id)" 90 | --- 91 | apiVersion: triggers.tekton.dev/v1alpha1 92 | kind: EventListener 93 | metadata: 94 | name: conexp-listener 95 | spec: 96 | serviceAccountName: tekton-triggers-admin 97 | triggers: 98 | - name: github-listener 99 | bindings: 100 | - ref: conexp-pipelinebinding 101 | template: 102 | ref: conexp-triggertemplate 103 | --- 104 | apiVersion: v1 105 | kind: ServiceAccount 106 | metadata: 107 | name: deployment-user 108 | secrets: 109 | - name: regcred 110 | --- 111 | kind: Role 112 | apiVersion: rbac.authorization.k8s.io/v1 113 | metadata: 114 | name: pipeline-role 115 | rules: 116 | - apiGroups: ["", "extensions", "apps", "serving.knative.dev", "eventing.knative.dev"] 117 | resources: ["*"] 118 | verbs: ["*"] 119 | --- 120 | kind: ClusterRole 121 | apiVersion: rbac.authorization.k8s.io/v1 122 | metadata: 123 | name: pipeline-cluster-role 124 | rules: 125 | - apiGroups: ["apiextensions.k8s.io"] 126 | resources: ["customresourcedefinitions"] 127 | verbs: ["list"] 128 | --- 129 | apiVersion: rbac.authorization.k8s.io/v1 130 | kind: ClusterRoleBinding 131 | metadata: 132 | name: pipeline-cluster-role-binding 133 | roleRef: 134 | apiGroup: rbac.authorization.k8s.io 135 | kind: ClusterRole 136 | name: pipeline-cluster-role 137 | subjects: 138 | - kind: ServiceAccount 139 | name: deployment-user 140 | namespace: conexp-mvp-devops 141 | --- 142 | apiVersion: rbac.authorization.k8s.io/v1 143 | kind: RoleBinding 144 | metadata: 145 | name: pipeline-role-binding 146 | roleRef: 147 | apiGroup: rbac.authorization.k8s.io 148 | kind: Role 149 | name: pipeline-role 150 | subjects: 151 | - kind: ServiceAccount 152 | name: deployment-user 153 | namespace: conexp-mvp-devops 154 | --- 155 | -------------------------------------------------------------------------------- /yml/knative-broker.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: eventing.knative.dev/v1 2 | kind: Broker 3 | metadata: 4 | name: default 5 | namespace: conexp-mvp-fn -------------------------------------------------------------------------------- /yml/linkerd-ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | type: Opaque 4 | metadata: 5 | name: web-ingress-auth 6 | namespace: linkerd 7 | data: 8 | auth: YWRtaW46JGFwcjEkbjdDdTZnSGwkRTQ3b2dmN0NPOE5SWWpFakJPa1dNLgoK 9 | --- 10 | apiVersion: networking.k8s.io/v1beta1 11 | kind: Ingress 12 | metadata: 13 | annotations: 14 | kubernetes.io/ingress.class: 'nginx' 15 | nginx.ingress.kubernetes.io/upstream-vhost: $service_name.$namespace.svc.cluster.local:8084 16 | nginx.ingress.kubernetes.io/configuration-snippet: | 17 | proxy_set_header Origin ""; 18 | proxy_hide_header l5d-remote-ip; 19 | proxy_hide_header l5d-server-id; 20 | nginx.ingress.kubernetes.io/auth-type: basic 21 | nginx.ingress.kubernetes.io/auth-secret: web-ingress-auth 22 | nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required' 23 | name: linkerd-dashboard 24 | namespace: linkerd 25 | spec: 26 | rules: 27 | - host: {linkerdDashboard} 28 | http: 29 | paths: 30 | - backend: 31 | serviceName: linkerd-web 32 | servicePort: 8084 33 | path: / -------------------------------------------------------------------------------- /yml/linkerd-opencesus-collector.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: tracing 5 | --- 6 | apiVersion: v1 7 | kind: ConfigMap 8 | metadata: 9 | name: oc-collector-conf 10 | namespace: tracing 11 | labels: 12 | app: opencensus 13 | component: oc-collector-conf 14 | data: 15 | oc-collector-config: | 16 | receivers: 17 | opencensus: 18 | port: 55678 19 | zipkin: 20 | port: 9411 21 | queued-exporters: 22 | jaeger-collector: 23 | num-workers: 4 24 | queue-size: 100 25 | retry-on-failure: true 26 | sender-type: jaeger-thrift-http 27 | jaeger-thrift-http: 28 | collector-endpoint: http://jaeger-collector:14268/api/traces 29 | timeout: 5s 30 | --- 31 | apiVersion: v1 32 | kind: Service 33 | metadata: 34 | name: oc-collector 35 | namespace: tracing 36 | labels: 37 | app: opencesus 38 | component: oc-collector 39 | spec: 40 | ports: 41 | - name: opencensus 42 | port: 55678 43 | protocol: TCP 44 | targetPort: 55678 45 | - name: zipkin 46 | port: 9411 47 | protocol: TCP 48 | targetPort: 9411 49 | selector: 50 | component: oc-collector 51 | --- 52 | apiVersion: apps/v1 53 | kind: Deployment 54 | metadata: 55 | name: oc-collector 56 | namespace: tracing 57 | labels: 58 | app: opencensus 59 | component: oc-collector 60 | spec: 61 | selector: 62 | matchLabels: 63 | app: opencensus 64 | component: oc-collector 65 | replicas: 1 66 | template: 67 | metadata: 68 | annotations: 69 | linkerd.io/inject: enabled 70 | config.linkerd.io/skip-outbound-ports: "14268" 71 | labels: 72 | app: opencensus 73 | component: oc-collector 74 | spec: 75 | containers: 76 | - command: 77 | - "/occollector_linux" 78 | - "--config=/conf/oc-collector-config.yaml" 79 | env: 80 | - name: GOGC 81 | value: "80" 82 | image: omnition/opencensus-collector:0.1.11 83 | name: oc-collector 84 | ports: 85 | - containerPort: 55678 86 | - containerPort: 9411 87 | volumeMounts: 88 | - name: oc-collector-config-vol 89 | mountPath: /conf 90 | livenessProbe: 91 | httpGet: 92 | path: / 93 | port: 13133 94 | readinessProbe: 95 | httpGet: 96 | path: / 97 | port: 13133 98 | volumes: 99 | - configMap: 100 | name: oc-collector-conf 101 | items: 102 | - key: oc-collector-config 103 | path: oc-collector-config.yaml 104 | name: oc-collector-config-vol 105 | -------------------------------------------------------------------------------- /yml/linkerd-prometheus-additional.yaml: -------------------------------------------------------------------------------- 1 | - job_name: kubernetes-pods 2 | kubernetes_sd_configs: 3 | - role: pod 4 | relabel_configs: 5 | - action: keep 6 | regex: true 7 | source_labels: 8 | - __meta_kubernetes_pod_annotation_prometheus_io_scrape 9 | - action: replace 10 | regex: (.+) 11 | source_labels: 12 | - __meta_kubernetes_pod_annotation_prometheus_io_path 13 | target_label: __metrics_path__ 14 | - action: replace 15 | regex: ([^:]+)(?::\d+)?;(\d+) 16 | replacement: $1:$2 17 | source_labels: 18 | - __address__ 19 | - __meta_kubernetes_pod_annotation_prometheus_io_port 20 | target_label: __address__ 21 | - action: labelmap 22 | regex: __meta_kubernetes_pod_label_(.+) 23 | - action: replace 24 | source_labels: 25 | - __meta_kubernetes_namespace 26 | target_label: kubernetes_namespace 27 | - action: replace 28 | source_labels: 29 | - __meta_kubernetes_pod_name 30 | target_label: kubernetes_pod_name 31 | 32 | - job_name: 'linkerd-controller' 33 | kubernetes_sd_configs: 34 | - role: pod 35 | namespaces: 36 | names: 37 | - 'linkerd' 38 | relabel_configs: 39 | - source_labels: 40 | - __meta_kubernetes_pod_container_port_name 41 | action: keep 42 | regex: admin-http 43 | - source_labels: [__meta_kubernetes_pod_container_name] 44 | action: replace 45 | target_label: component 46 | 47 | - job_name: 'linkerd-service-mirror' 48 | kubernetes_sd_configs: 49 | - role: pod 50 | relabel_configs: 51 | - source_labels: 52 | - __meta_kubernetes_pod_label_linkerd_io_control_plane_component 53 | - __meta_kubernetes_pod_container_port_name 54 | action: keep 55 | regex: linkerd-service-mirror;admin-http$ 56 | - source_labels: [__meta_kubernetes_pod_container_name] 57 | action: replace 58 | target_label: component 59 | 60 | - job_name: 'linkerd-proxy' 61 | kubernetes_sd_configs: 62 | - role: pod 63 | relabel_configs: 64 | - source_labels: 65 | - __meta_kubernetes_pod_container_name 66 | - __meta_kubernetes_pod_container_port_name 67 | - __meta_kubernetes_pod_label_linkerd_io_control_plane_ns 68 | action: keep 69 | regex: ^{{default .Values.proxyContainerName "linkerd-proxy" .Values.proxyContainerName}};linkerd-admin;{{.Values.linkerdNamespace}}$ 70 | - source_labels: [__meta_kubernetes_namespace] 71 | action: replace 72 | target_label: namespace 73 | - source_labels: [__meta_kubernetes_pod_name] 74 | action: replace 75 | target_label: pod 76 | # special case k8s' "job" label, to not interfere with prometheus' "job" 77 | # label 78 | # __meta_kubernetes_pod_label_linkerd_io_proxy_job=foo => 79 | # k8s_job=foo 80 | - source_labels: [__meta_kubernetes_pod_label_linkerd_io_proxy_job] 81 | action: replace 82 | target_label: k8s_job 83 | # drop __meta_kubernetes_pod_label_linkerd_io_proxy_job 84 | - action: labeldrop 85 | regex: __meta_kubernetes_pod_label_linkerd_io_proxy_job 86 | # __meta_kubernetes_pod_label_linkerd_io_proxy_deployment=foo => 87 | # deployment=foo 88 | - action: labelmap 89 | regex: __meta_kubernetes_pod_label_linkerd_io_proxy_(.+) 90 | # drop all labels that we just made copies of in the previous labelmap 91 | - action: labeldrop 92 | regex: __meta_kubernetes_pod_label_linkerd_io_proxy_(.+) 93 | # __meta_kubernetes_pod_label_linkerd_io_foo=bar => 94 | # foo=bar 95 | - action: labelmap 96 | regex: __meta_kubernetes_pod_label_linkerd_io_(.+) 97 | # Copy all pod labels to tmp labels 98 | - action: labelmap 99 | regex: __meta_kubernetes_pod_label_(.+) 100 | replacement: __tmp_pod_label_$1 101 | # Take `linkerd_io_` prefixed labels and copy them without the prefix 102 | - action: labelmap 103 | regex: __tmp_pod_label_linkerd_io_(.+) 104 | replacement: __tmp_pod_label_$1 105 | # Drop the `linkerd_io_` originals 106 | - action: labeldrop 107 | regex: __tmp_pod_label_linkerd_io_(.+) 108 | # Copy tmp labels into real labels 109 | - action: labelmap 110 | regex: __tmp_pod_label_(.+) 111 | -------------------------------------------------------------------------------- /yml/prometheus-values.yaml: -------------------------------------------------------------------------------- 1 | alertmanagerSpec: 2 | storage: 3 | volumeClaimTemplate: 4 | spec: 5 | storageClassName: rook-ceph-block 6 | accessModes: ["ReadWriteOnce"] 7 | resources: 8 | requests: 9 | storage: 10Gi 10 | 11 | 12 | ## Using default values from https://github.com/helm/charts/blob/master/stable/grafana/values.yaml 13 | ## 14 | grafana: 15 | enabled: true 16 | 17 | ## Deploy default dashboards. 18 | ## 19 | defaultDashboardsEnabled: true 20 | 21 | 22 | ## Manages Prometheus and Alertmanager components 23 | ## 24 | prometheusOperator: 25 | enabled: true 26 | ## Deploy a Prometheus instance 27 | ## 28 | prometheus: 29 | 30 | enabled: true 31 | 32 | prometheusSpec: 33 | storageSpec: 34 | volumeClaimTemplate: 35 | spec: 36 | storageClassName: rook-ceph-block 37 | accessModes: ["ReadWriteOnce"] 38 | resources: 39 | requests: 40 | storage: 1Gi 41 | 42 | additionalScrapeConfigs: 43 | - job_name: 'kubernetes-pods' 44 | kubernetes_sd_configs: 45 | - role: pod 46 | relabel_configs: 47 | - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] 48 | action: keep 49 | regex: true 50 | - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] 51 | action: replace 52 | target_label: __metrics_path__ 53 | regex: (.+) 54 | - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] 55 | action: replace 56 | regex: ([^:]+)(?::\d+)?;(\d+) 57 | replacement: $1:$2 58 | target_label: __address__ 59 | - action: labelmap 60 | regex: __meta_kubernetes_pod_label_(.+) 61 | - source_labels: [__meta_kubernetes_namespace] 62 | action: replace 63 | target_label: kubernetes_namespace 64 | - source_labels: [__meta_kubernetes_pod_name] 65 | action: replace 66 | target_label: kubernetes_pod_name 67 | -------------------------------------------------------------------------------- /yml/rook-storageclass.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ceph.rook.io/v1 2 | kind: CephBlockPool 3 | metadata: 4 | name: replicapool 5 | namespace: rook-ceph 6 | spec: 7 | failureDomain: host 8 | replicated: 9 | size: 3 10 | --- 11 | apiVersion: storage.k8s.io/v1 12 | kind: StorageClass 13 | metadata: 14 | name: rook-ceph-block 15 | provisioner: rook-ceph.rbd.csi.ceph.com 16 | parameters: 17 | clusterID: rook-ceph 18 | pool: replicapool 19 | imageFormat: "2" 20 | imageFeatures: layering 21 | csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner 22 | csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph 23 | csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node 24 | csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph 25 | csi.storage.k8s.io/fstype: xfs 26 | reclaimPolicy: Delete -------------------------------------------------------------------------------- /yml/tekton-default-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: config-defaults 5 | namespace: tekton-pipelines 6 | data: 7 | default-service-account: "tekton" 8 | default-timeout-minutes: "20" 9 | default-pod-template: | 10 | nodeSelector: 11 | kops.k8s.io/instancegroup: build-instance-group 12 | default-managed-by-label-value: "my-tekton-installation" -------------------------------------------------------------------------------- /yml/tekton-el-ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: ingress-resource 5 | annotations: 6 | cert-manager.io/cluster-issuer: letsencrypt 7 | acme.cert-manager.io/http01-ingress-class: nginx 8 | nginx.ingress.kubernetes.io/ssl-redirect: "true" 9 | nginx.ingress.kubernetes.io/rewrite-target: /cd/$2 10 | spec: 11 | ingressClassName: nginx 12 | tls: 13 | - hosts: 14 | - {cicdWebhook} 15 | secretName: webhook-tls 16 | rules: 17 | - host: {cicdWebhook} 18 | http: 19 | paths: 20 | - path: /cd(/|$)(.*) 21 | pathType: Prefix 22 | backend: 23 | service: 24 | name: el-conexp-listener 25 | port: 26 | number: 8080 27 | -------------------------------------------------------------------------------- /yml/tekton-feature-flags-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: feature-flags 5 | namespace: tekton-pipelines 6 | data: 7 | disable-working-directory-overwrite: "true" # Tekton will not overwrite the working directory in Steps. 8 | enable-api-fields: alpha # Tekton will enable the API fields in Tasks and Pipelines. -------------------------------------------------------------------------------- /yml/tekton-limit-range.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: LimitRange 3 | metadata: 4 | name: set-limit-range 5 | spec: 6 | limits: 7 | - default: 8 | memory: 1Gi 9 | defaultRequest: 10 | memory: 256Mi 11 | type: Container 12 | --------------------------------------------------------------------------------